diff options
1845 files changed, 59799 insertions, 27848 deletions
diff --git a/.gitattributes b/.gitattributes index 03c6f96f80..47ba2b45bb 100644 --- a/.gitattributes +++ b/.gitattributes @@ -9,3 +9,4 @@ drivers/* linguist-vendored *.py eol=lf *.hpp eol=lf *.xml eol=lf +*.natvis eol=lf diff --git a/.gitignore b/.gitignore index 6db75f2324..fe504482b3 100644 --- a/.gitignore +++ b/.gitignore @@ -91,6 +91,9 @@ bld/ *.debug *.dSYM +# Visual Studio cache/options directory +.vs/ + # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* @@ -98,9 +101,6 @@ bld/ # Hints for improving IntelliSense, created together with VS project cpp.hint -# Visualizers for the VS debugger -*.natvis - #NUNIT *.VisualState.xml TestResult.xml @@ -238,6 +238,7 @@ ClientBin/ *.pfx *.publishsettings node_modules/ +__pycache__/ # KDE .directory @@ -310,5 +311,8 @@ platform/windows/godot_res.res /.vs /.vscode +# Visual Studio Code workspace file +*.code-workspace + # Scons progress indicator .scons_node_count diff --git a/.travis.yml b/.travis.yml index 404bdc9d90..a8bc8289e4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ env: - SCONS_CACHE=$HOME/.scons_cache - SCONS_CACHE_LIMIT=1024 - OPTIONS="debug_symbols=no verbose=yes progress=no gdnative_wrapper=yes" - - secure: "QLFRizqry/Y5pnEZvDlQz5S3YydQ+600u4rHEzFgUTd0heYeQaETXAQeMzp0ymuG1BkdRAl5YJoLVJgAzjwI9hrvugvoUlh2//SfpqZCHN/Q1fYbtGgNTn01R3VFEpcfYQL93I2EjrxVm0WTM4PwCvMO+hU0aWTRDvCt1Lty0kMR+RMDQOO/woqunoXh5wvFNxTJJkAmuLe0v962DJYOIwJAnqMLR0aFYjmeQJ20bc/2X5oLt+WuJDuf/lGj6WSlD6z/o/kL3YxHoUyw4A/HAZ2IX0IfNHKuay60ESWzl/NlobnePiPwHAE2pdDVu//q16fanb9VeYnBYRFse49TpFRb86Lo+Qz8nKDJqpQEIY0YKNCFqekrubqTM++Lj6QvGpykQZNxUhybmELcEsRG4PS0UMvCpebdnJD46nNB+DtO2Lgb4xXDLQwpq19z1wizq/XDQ5hz61TIIx8+i8TsgdSQKCTeWovd4HcD4CVjAD5XTLGgyRmI/zC2d+lTnKo6W9diLq/bX/Goq2QPeaTPABqv817IaJka2JyugQ7Qal/+gNTjYRRsimRCL9B2tVh+Uh8rWhTFhQL4QbP5P65HF+p8qojUzqtAhPMbZ8mxUtNukUI3liVgPgiMss96sG0nTVglFgkkAkEjIMFnqMSKnTfG812K4jIhp2jCO2Q3NeI=" + - secure: "uch9QszCgsl1qVbuzY41P7S2hWL2IiNFV4SbAYRCdi0oJ9MIu+pVyrQdpf3+jG4rH6j4Rffl+sN17Zz4dIDDioFL1JwqyCqyCyswR8uACC0Rr8gr4Mi3+HIRbv+2s2P4cIQq41JM8FJe84k9jLEMGCGh69w+ibCWoWs74CokYVA=" cache: directories: @@ -29,16 +29,7 @@ matrix: - clang-format-6.0 - libstdc++6 # >= 4.9 needed for clang-format-6.0 - coverity_scan: - project: - name: "godotengine/godot" - description: "Godot Engine Coverity scans" - notification_email: coverity@godotengine.org - build_command_prepend: "" - build_command: "scons p=x11 -j2 $OPTIONS" - branch_pattern: coverity_scan - - - env: GODOT_TARGET=x11 TOOLS=yes CACHE_NAME=${GODOT_TARGET}-tools-mono-gcc EXTRA_ARGS="module_mono_enabled=yes mono_glue=no" + - env: PLATFORM=x11 TOOLS=yes TARGET=debug CACHE_NAME=${PLATFORM}-tools-mono-gcc EXTRA_ARGS="module_mono_enabled=yes mono_glue=no werror=yes" os: linux compiler: gcc addons: @@ -47,9 +38,18 @@ matrix: - mono packages: - &linux_deps [libasound2-dev, libfreetype6-dev, libgl1-mesa-dev, libglu1-mesa-dev, libx11-dev, libxcursor-dev, libxi-dev, libxinerama-dev, libxrandr-dev] - - &linux_mono_deps [mono-devel, msbuild] + - &linux_mono_deps [mono-devel, msbuild, nuget] + + coverity_scan: + project: + name: "godotengine/godot" + description: "Godot Engine Coverity scans" + notification_email: coverity@godotengine.org + build_command_prepend: "" + build_command: "scons p=x11 -j2 $OPTIONS" + branch_pattern: coverity_scan - - env: GODOT_TARGET=x11 TOOLS=no CACHE_NAME=${GODOT_TARGET}-clang + - env: PLATFORM=x11 TOOLS=no TARGET=release CACHE_NAME=${PLATFORM}-clang EXTRA_ARGS="werror=yes" os: linux compiler: clang addons: @@ -57,19 +57,19 @@ matrix: packages: - *linux_deps - - env: GODOT_TARGET=android TOOLS=no CACHE_NAME=${GODOT_TARGET}-clang + - env: PLATFORM=android TOOLS=no TARGET=release_debug CACHE_NAME=${PLATFORM}-clang EXTRA_ARGS="werror=yes" os: linux compiler: clang - - env: GODOT_TARGET=osx TOOLS=yes CACHE_NAME=${GODOT_TARGET}-tools-clang + - env: PLATFORM=osx TOOLS=yes TARGET=debug CACHE_NAME=${PLATFORM}-tools-clang os: osx compiler: clang - - env: GODOT_TARGET=iphone TOOLS=no CACHE_NAME=${GODOT_TARGET}-clang + - env: PLATFORM=iphone TOOLS=no TARGET=debug CACHE_NAME=${PLATFORM}-clang os: osx compiler: clang - - env: GODOT_TARGET=server TOOLS=yes CACHE_NAME=${GODOT_TARGET}-tools-gcc + - env: PLATFORM=server TOOLS=yes TARGET=release_debug CACHE_NAME=${PLATFORM}-tools-gcc os: linux compiler: gcc addons: @@ -83,18 +83,18 @@ before_install: fi install: - - if [ "$TRAVIS_OS_NAME" = "linux" ] && [ "$GODOT_TARGET" = "android" ]; then + - if [ "$TRAVIS_OS_NAME" = "linux" ] && [ "$PLATFORM" = "android" ]; then misc/travis/android-tools-linux.sh; fi - if [ "$TRAVIS_OS_NAME" = "osx" ]; then misc/travis/scons-local-osx.sh; fi - - if [ "$TRAVIS_OS_NAME" = "osx" ] && [ "$GODOT_TARGET" = "android" ]; then + - if [ "$TRAVIS_OS_NAME" = "osx" ] && [ "$PLATFORM" = "android" ]; then misc/travis/android-tools-osx.sh; fi before_script: - - if [ "$GODOT_TARGET" = "android" ]; then + - if [ "$PLATFORM" = "android" ]; then export ANDROID_HOME=$TRAVIS_BUILD_DIR/godot-dev/build-tools/android-sdk; export ANDROID_NDK_ROOT=$TRAVIS_BUILD_DIR/godot-dev/build-tools/android-ndk; fi @@ -103,5 +103,5 @@ script: - if [ "$STATIC_CHECKS" = "yes" ]; then sh ./misc/travis/clang-format.sh; else - scons -j2 CC=$CC CXX=$CXX platform=$GODOT_TARGET TOOLS=$TOOLS $EXTRA_ARGS $OPTIONS; + scons -j2 CC=$CC CXX=$CXX platform=$PLATFORM tools=$TOOLS target=$TARGET $EXTRA_ARGS $OPTIONS; fi diff --git a/AUTHORS.md b/AUTHORS.md index e1171392d5..2f109aff6e 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -27,6 +27,7 @@ name is available. (in alphabetical order, with 10 commits or more excluding merges) + Aaron Franke (aaronfranke) Alexander Holland (AlexHolly) Alexey Velikiy (jonyrock) Alket Rexhepi (alketii) @@ -44,10 +45,13 @@ name is available. Bojidar Marinov (bojidar-bg) bruvzg Carl Olsson (not-surt) + Chris Bradfield (cbscribe) Dana Olson (adolson) Daniel J. Ramirez (djrm) Dmitry Koteroff (Krakean) + DualMatrix Emmanuel Leblond (touilleMan) + Eric Lasota (elasota) est31 Fabio Alessandrelli (Faless) Ferenc Arn (tagcup) @@ -69,7 +73,7 @@ name is available. J08nY Jakub Grzesik (kubecz3k) Jérôme GULLY (Nutriz) - JFonS + Joan Fons Sanchez (JFonS) Johan Manuel (29jm) Joshua Grams (JoshuaGrams) Juan Linietsky (reduz) @@ -85,6 +89,7 @@ name is available. Marcin Zawiejski (dragmz) Mariano Javier Suligoy (MarianoGnu) Mario Schlack (hurikhan) + Martin Capitanio (capnm) Martin Sjursen (binbitten) marynate Masoud BH (masoudbh3) @@ -108,9 +113,9 @@ name is available. Ramesh Ravone (RameshRavone) Ray Koopa (RayKoopa) Rémi Verschelde (akien-mga) + Rhody Lugo (rraallvv) Roberto F. Arroyo (robfram) romulox-x - rraallvv Ruslan Mustakov (endragor) Saniko (sanikoyes) SaracenOne diff --git a/CHANGELOG.md b/CHANGELOG.md index 533968886e..6a0310f904 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,7 +47,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Setters and getters for node properties. - Underscores in number literals are now allowed for improved readability (for example,`1_000_000`). - Improved performance (+20% to +40%, based on various benchmarks). -- [Feature tags](http://docs.godotengine.org/en/latest/learning/workflow/export/feature_tags.html) in the Project Settings, for custom per-platform settings. +- [Feature tags](http://docs.godotengine.org/en/latest/getting_started/workflow/export/feature_tags.html) in the Project Settings, for custom per-platform settings. - Full support for the [glTF 2.0](https://www.khronos.org/gltf/) 3D interchange format. - Freelook and fly navigation to the 3D editor. - Built-in editor logging (logging standard output to a file), disabled by default. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 68ec20a525..a8e4650710 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -70,11 +70,15 @@ Similar rules can be applied when contributing bug fixes - it's always best to discuss the implementation in the bug report first if you are not 100% about what would be the best fix. +In addition to the following tips, also take a look at the +[Engine development guide](https://docs.godotengine.org/en/latest/development/cpp/) +for an introduction to developing on Godot. + #### Be nice to the git history -Try to make simple PRs with that handle one specific topic. Just like for -reporting issues, it's better to open 3 different PRs that each address a -different issue than one big PR with three commits. +Try to make simple PRs that handle one specific topic. Just like for reporting +issues, it's better to open 3 different PRs that each address a different issue +than one big PR with three commits. When updating your fork with upstream changes, please use ``git pull --rebase`` to avoid creating "merge commits". Those commits unnecessarily pollute the git diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index 4ba80941e0..894cd1a53a 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -10,8 +10,9 @@ # all corresponding files (also recursively in subfolders), apart from those # with a more explicit copyright statement. # -# Licenses are given with their SPDX identifier, and are all included in -# plain text at the end of this file (in alphabetical order). +# Licenses are given with their debian/copyright short name (or SPDX identifier +# if no standard short name exists) and are all included in plain text at the +# end of this file (in alphabetical order). # # Disclaimer for thirdparty libraries: # ------------------------------------ @@ -95,6 +96,11 @@ Copyright: 1997-2017, Sam Lantinga 2014-2018, Godot Engine contributors. License: Expat and Zlib +Files: ./scene/animation/tween_interpolaters.cpp +Comment: Penner Easing +Copyright: 2001, Robert Penner +License: BSD-3-clause + Files: ./servers/physics/gjk_epa.cpp ./servers/physics/joints/generic_6dof_joint_sw.cpp ./servers/physics/joints/generic_6dof_joint_sw.h @@ -139,7 +145,7 @@ Files: ./thirdparty/cvtt/ Comment: Convection Texture Tools Stand-Alone Kernels Copyright: 2018, Eric Lasota 2018, Microsoft Corp. -License: MIT +License: Expat Files: ./thirdparty/enet/ Comment: ENet @@ -272,6 +278,11 @@ Comment: curl Copyright: 1998-2012, Daniel Stenberg et al. License: curl +Files: ./thirdparty/misc/easing_equations.cpp +Comment: Robert Penner's Easing Functions +Copyright: 2001, Robert Penner +License: BSD-3-clause + Files: ./thirdparty/misc/fastlz.c ./thirdparty/misc/fastlz.h Comment: FastLZ @@ -373,7 +384,7 @@ Files: ./thirdparty/tinyexr/ Comment: TinyEXR Copyright: 2014-2017, Syoyo Fujita 2002, Industrial Light & Magic, a division of Lucas Digital Ltd. LLC -License: BSD-3-Clause +License: BSD-3-clause Files: ./thirdparty/zlib/ Comment: zlib @@ -383,7 +394,7 @@ License: Zlib Files: ./thirdparty/zstd/ Comment: Zstandard Copyright: 2016-2018, Facebook, Inc. -License: BSD-3-Clause +License: BSD-3-clause @@ -17,67 +17,65 @@ generous deed immortalized in the next stable release of Godot Engine. ## Gold sponsors Gamblify <https://www.gamblify.com> - GameDev.TV <https://www.gamedev.tv> + GameDev.TV <https://gdev.tv/godot> + Image Campus <https://www.imagecampus.edu.ar> ## Mini sponsors + Andrew Dunai Brandon Lamb Christian Uldall Pedersen - Christopher Igoe Christoph Woinke - E Hewert + David Graham GameDev.net Hein-Pieter van Braam + iDev.Network Studios Jamal Alyafei - Jay Sistar - Loreshaper Games + Leona Eden Matthieu Huvé Mike King Nathan Warden Neal Gompa (Conan Kudo) - Pascal Julien Patrick Aarstad - rottis Ruslan Mustakov - Sébastien Manin Slobodan Milnovic Stephan Lanfermann Stoney Meyerhoeffer + thechris Thomas Mathews VilliHaukka ## Gold donors - 3Dexplorer - Alexander Otto Asdf cheese65536 K9Kraken - Kris Michael Manuele Finocchiaro Nathanael Beisiegel Officine Pixel S.n.c. + Retro Village Zaven Muradyan + 13MHz Allen Schade Andreas Schüle Austen McRae + Daniel Lynn David Gehrig - DeepSquid - Fidget Sinner Florian Breisch Gary Oberbrunner + Jay Horton Johannes Wuensch - Josep G. Camarasa Joshua Lesperance + Krzysztof Dluzniewski Kyle Szklenski - Libre-Dépanne - Matthew Bennett + Mohammad Taleb + Moonwards Paul LaMotte Ranoller Sergey Svenne Krap - Timothy Hagberg + Xananax BanjoNode2D Beliar @@ -92,11 +90,11 @@ generous deed immortalized in the next stable release of Godot Engine. Justo Delgado Baudà KTL Laurence Bannister + Markus Wiesner paul gruenbacher Rami Robert Willes Robin Arys - Rodrigo Loli Ronnie Ashlock ScottMakesGames Thomas Bjarnelöf @@ -107,50 +105,43 @@ generous deed immortalized in the next stable release of Godot Engine. Alessandra Pereyra Alexey Dyadchenko Amanda Haldy - Anthony Ryan + Chau Siu Hung Chris Brown Chris Petrich - Chris Wilson Cody Parker - Corey Auger D Deadly Lampshade E.G. Eric Eric Monson + Ethan Bennis + Fidget Sinner flesk - floopf G Barnes + Gero GGGames.org Giovanni Solimeno Guilherme Felipe de C. G. da Silva Hasen Judy Heath Hayes - Jan Čejka - Jay Horton Jeppe Zapp joe513 Juraj Móza Justin Arnold - Lars Wuethrich Leandro Voltolino - Lisandro Lorea - Markus Wiesner + Marius Kamm Marvin - Mohammad Taleb - Neal Barry + Nahuel Sacchetti Nick Nikitin Pablo Cholaky - Patrick Schnorbus Pete Goodwin + ray-tracer Ruben Soares Luis - Rufus Xavier Sarsaparilla - Sindre Sømme Sofox Stoned Xander - Tim Dalporto Trent McPheron - Wyatt Walker + WytRabbit + Zachariah Gibbons ## Silver donors @@ -168,9 +159,9 @@ generous deed immortalized in the next stable release of Godot Engine. Artur Barichello Aubrey Falconer Avencherus + Bailey Bastian Böhm Benedikt - Benjamin Beshara Ben Vercammen Blair Allen Brandon @@ -181,12 +172,10 @@ generous deed immortalized in the next stable release of Godot Engine. Chris Chapin Christian Baune Christian Winter - Christopher Schmitt Collin Shooltz Daniel Johnson Daniel Kaplan DanielMaximiano - Daniel Mircea David David Cravens David May @@ -196,25 +185,23 @@ generous deed immortalized in the next stable release of Godot Engine. Edward Herbert Elias Nykrem Eric Martini + Eric Williams + Eugenio Hugo Salgüero Jáñez Fabian Becker fengjiongmax - Francesco Lisi + Foomf G3Dev sà rl + George Lesica Gerrit Großkopf - Gerrit Procee Gilberto K. Otubo Greg Olson - Guillaume Laforte Guldoman - Gumichan01 Heribert Hirth Hunter Jones ialex32x - Ivan Vodopiviz Jahn Johansen Jaime Ruiz-Borau Vizárraga - Jake Huxell - Jed + Jako Danar Jeff Hungerford Joel Fivat Johan Lindberg @@ -222,8 +209,9 @@ generous deed immortalized in the next stable release of Godot Engine. Jonas Yamazaki Jonathan Martin Jonathan Nieto - Jonathon + Jon Bonazza Jon Sully + Jose Malheiro Josh 'Cheeseness' Bush Juanfran Juan Negrier @@ -235,14 +223,16 @@ generous deed immortalized in the next stable release of Godot Engine. Kevin Kamper Meejach Petersen Klavdij Voncina Krzysztof Jankowski + Krzysztof Sierszecki + KsyTek Games Linus Lind Lundgren - Luc Magitem + Loreshaper Games Luis Moraes Macil magodev - Manolis Makris + Malcolm Martin Eigel - Martins Odabi + Matt Welke Max R.R. Collada Maxwell Mertcan Mermerkaya @@ -253,25 +243,28 @@ generous deed immortalized in the next stable release of Godot Engine. Mikael Olsson MoM Moritz Laass + Moritz Weissenberger Natrim nee Neil Blakey-Milner Nick Pavlica Niclas Eriksen + Nicolás Montaña Nicolas SAN AGUSTIN - Niko Leopold + Nik Noi Sek - Pablo Seibelt Pan Ip Pascal Grüter - Pat LaBine Patrick Nafarrete Paul E Hansen + Paul Gieske Paul Mason Paweł Kowal + Phillip Ryals Pierre-Igor Berthet Pietro Vertechi Piotr Kaczmarski + Prokhorenko Leonid Rémi Verschelde Richman Stewart Roger Burgess @@ -279,26 +272,23 @@ generous deed immortalized in the next stable release of Godot Engine. Roman Tinkov Sasori Olkof Sootstone - Stefan Butucea - The K-B Theo Cranmore - Thibaud Galloy Thibault Barbaroux Thomas Bell Thomas Holmes Thomas Kurz tiansheng li + Tim Tom Larrow - Tristan Crawford Trym Nilsen + Tyggve Sollid Tyler Stafos UltyX + Vaiktorg Victor Viktor Ferenczi waka nya - werner mendizabal Wout Standaert - Yu He ## Bronze donors @@ -37,10 +37,10 @@ Official binaries for the Godot editor and the export templates can be found #### Compiling from source -[See the official docs](http://docs.godotengine.org/en/latest/development/compiling/) +[See the official docs](https://docs.godotengine.org/en/latest/development/compiling/) for compilation instructions for every supported platform. -### Community +### Community and contributing Godot is not only an engine but an ever-growing community of users and engine developers. The main community channels are listed [on the homepage](https://godotengine.org/community). @@ -49,12 +49,14 @@ To get in touch with the developers, the best way is to join the [#godotengine IRC channel](https://webchat.freenode.net/?channels=godotengine) on Freenode. +To get started contributing to the project, see the [contributing guide](CONTRIBUTING.md). + ### Documentation and demos -The official documentation is hosted on [ReadTheDocs](http://docs.godotengine.org). +The official documentation is hosted on [ReadTheDocs](https://docs.godotengine.org). It is maintained by the Godot community in its own [GitHub repository](https://github.com/godotengine/godot-docs). -The [class reference](http://docs.godotengine.org/en/latest/classes/) +The [class reference](https://docs.godotengine.org/en/latest/classes/) is also accessible from within the engine. The official demos are maintained in their own [GitHub repository](https://github.com/godotengine/godot-demo-projects) diff --git a/SConstruct b/SConstruct index 2cc486fd1b..628aac42af 100644 --- a/SConstruct +++ b/SConstruct @@ -23,7 +23,7 @@ platform_exporters = [] platform_apis = [] global_defaults = [] -for x in glob.glob("platform/*"): +for x in sorted(glob.glob("platform/*")): if (not os.path.isdir(x) or not os.path.exists(x + "/detect.py")): continue tmppath = "./" + x @@ -123,6 +123,7 @@ env_base.__class__.add_shared_library = methods.add_shared_library env_base.__class__.add_library = methods.add_library env_base.__class__.add_program = methods.add_program env_base.__class__.CommandNoCache = methods.CommandNoCache +env_base.__class__.disable_warnings = methods.disable_warnings env_base["x86_libtheora_opt_gcc"] = False env_base["x86_libtheora_opt_vc"] = False @@ -156,22 +157,24 @@ opts.Add(BoolVariable('deprecated', "Enable deprecated features", True)) opts.Add(BoolVariable('gdscript', "Enable GDScript support", True)) opts.Add(BoolVariable('minizip', "Enable ZIP archive support using minizip", True)) opts.Add(BoolVariable('xaudio2', "Enable the XAudio2 audio driver", False)) -opts.Add(BoolVariable('xml', "Enable XML format support for resources", True)) # Advanced options -opts.Add(BoolVariable('disable_3d', "Disable 3D nodes for a smaller executable", False)) -opts.Add(BoolVariable('disable_advanced_gui', "Disable advanced 3D GUI nodes and behaviors", False)) -opts.Add('extra_suffix', "Custom extra suffix added to the base filename of all generated binary files", '') opts.Add(BoolVariable('verbose', "Enable verbose output for the compilation", False)) -opts.Add(BoolVariable('vsproj', "Generate a Visual Studio solution", False)) -opts.Add(EnumVariable('warnings', "Set the level of warnings emitted during compilation", 'no', ('extra', 'all', 'moderate', 'no'))) opts.Add(BoolVariable('progress', "Show a progress indicator during compilation", True)) +opts.Add(EnumVariable('warnings', "Set the level of warnings emitted during compilation", 'all', ('extra', 'all', 'moderate', 'no'))) +opts.Add(BoolVariable('werror', "Treat compiler warnings as errors. Depends on the level of warnings set with 'warnings'", False)) opts.Add(BoolVariable('dev', "If yes, alias for verbose=yes warnings=all", False)) +opts.Add('extra_suffix', "Custom extra suffix added to the base filename of all generated binary files", '') +opts.Add(BoolVariable('vsproj', "Generate a Visual Studio solution", False)) opts.Add(EnumVariable('macports_clang', "Build using Clang from MacPorts", 'no', ('no', '5.0', 'devel'))) +opts.Add(BoolVariable('disable_3d', "Disable 3D nodes for a smaller executable", False)) +opts.Add(BoolVariable('disable_advanced_gui', "Disable advanced 3D GUI nodes and behaviors", False)) opts.Add(BoolVariable('no_editor_splash', "Don't use the custom splash screen for the editor", False)) +opts.Add('system_certs_path', "Use this path as SSL certificates default for editor (for package maintainers)", '') # Thirdparty libraries opts.Add(BoolVariable('builtin_bullet', "Use the built-in Bullet library", True)) +opts.Add(BoolVariable('builtin_certs', "Bundle default SSL certificates to be used if you don't specify an override in the project settings", True)) opts.Add(BoolVariable('builtin_enet', "Use the built-in ENet library", True)) opts.Add(BoolVariable('builtin_freetype', "Use the built-in FreeType library", True)) opts.Add(BoolVariable('builtin_libogg', "Use the built-in libogg library", True)) @@ -188,6 +191,7 @@ opts.Add(BoolVariable('builtin_pcre2', "Use the built-in PCRE2 library)", True)) opts.Add(BoolVariable('builtin_recast', "Use the built-in Recast library", True)) opts.Add(BoolVariable('builtin_squish', "Use the built-in squish library", True)) opts.Add(BoolVariable('builtin_thekla_atlas', "Use the built-in thekla_altas library", True)) +opts.Add(BoolVariable('builtin_xatlas', "Use the built-in xatlas library", True)) opts.Add(BoolVariable('builtin_zlib', "Use the built-in zlib library", True)) opts.Add(BoolVariable('builtin_zstd', "Use the built-in Zstd library", True)) @@ -224,14 +228,14 @@ Help(opts.GenerateHelpText(env_base)) # generate help # add default include paths -env_base.Append(CPPPATH=['#core', '#core/math', '#editor', '#drivers', '#']) +env_base.Append(CPPPATH=['#editor', '#']) # configure ENV for platform env_base.platform_exporters = platform_exporters env_base.platform_apis = platform_apis if (env_base['target'] == 'debug'): - env_base.Append(CPPDEFINES=['DEBUG_MEMORY_ALLOC', 'SCI_NAMESPACE']) + env_base.Append(CPPDEFINES=['DEBUG_MEMORY_ALLOC','DISABLE_FORCED_INLINE']) if (env_base['no_editor_splash']): env_base.Append(CPPDEFINES=['NO_EDITOR_SPLASH']) @@ -314,34 +318,41 @@ if selected_platform in platform_list: # must happen after the flags, so when flags are used by configure, stuff happens (ie, ssl on x11) detect.configure(env) - if (env["warnings"] == 'yes'): - print("WARNING: warnings=yes is deprecated; assuming warnings=all") - + # Configure compiler warnings if env.msvc: - disable_nonessential_warnings = ['/wd4267', '/wd4244', '/wd4305', '/wd4800'] # Truncations, narrowing conversions... + # Truncations, narrowing conversions, signed/unsigned comparisons... + disable_nonessential_warnings = ['/wd4267', '/wd4244', '/wd4305', '/wd4018', '/wd4800'] if (env["warnings"] == 'extra'): env.Append(CCFLAGS=['/Wall']) # Implies /W4 - elif (env["warnings"] == 'all' or env["warnings"] == 'yes'): + elif (env["warnings"] == 'all'): env.Append(CCFLAGS=['/W3'] + disable_nonessential_warnings) elif (env["warnings"] == 'moderate'): - # C4244 shouldn't be needed here being a level-3 warning, but it is env.Append(CCFLAGS=['/W2'] + disable_nonessential_warnings) else: # 'no' env.Append(CCFLAGS=['/w']) # Set exception handling model to avoid warnings caused by Windows system headers. env.Append(CCFLAGS=['/EHsc']) + if (env["werror"]): + env.Append(CCFLAGS=['/WX']) else: # Rest of the world + disable_nonessential_warnings = ['-Wno-sign-compare'] if (env["warnings"] == 'extra'): env.Append(CCFLAGS=['-Wall', '-Wextra']) - elif (env["warnings"] == 'all' or env["warnings"] == 'yes'): - env.Append(CCFLAGS=['-Wall']) + elif (env["warnings"] == 'all'): + env.Append(CCFLAGS=['-Wall'] + disable_nonessential_warnings) elif (env["warnings"] == 'moderate'): - env.Append(CCFLAGS=['-Wall', '-Wno-unused']) + env.Append(CCFLAGS=['-Wall', '-Wno-unused'] + disable_nonessential_warnings) else: # 'no' env.Append(CCFLAGS=['-w']) - env.Append(CCFLAGS=['-Werror=return-type']) + if (env["werror"]): + env.Append(CCFLAGS=['-Werror']) + else: # always enable those errors + env.Append(CCFLAGS=['-Werror=return-type']) - suffix = "." + selected_platform + if (hasattr(detect, 'get_program_suffix')): + suffix = "." + detect.get_program_suffix() + else: + suffix = "." + selected_platform if (env["target"] == "release"): if env["tools"]: @@ -439,8 +450,6 @@ if selected_platform in platform_list: env.Append(CPPDEFINES=['ADVANCED_GUI_DISABLED']) if env['minizip']: env.Append(CPPDEFINES=['MINIZIP_ENABLED']) - if env['xml']: - env.Append(CPPDEFINES=['XML_ENABLED']) if not env['verbose']: methods.no_verbose(sys, env) @@ -538,7 +547,7 @@ if 'env' in locals(): [os.remove(f) for f in files] def file_list(self): - if self.path == None: + if self.path is None: # Nothing to do return [] # Gather a list of (filename, (size, atime)) within the @@ -563,7 +572,7 @@ if 'env' in locals(): if sum > self.limit: mark = i break - if mark == None: + if mark is None: return [] else: return [x[0] for x in file_stat[mark:]] diff --git a/core/SCsub b/core/SCsub index c8e2e10b9f..c6d0b7e5b1 100644 --- a/core/SCsub +++ b/core/SCsub @@ -8,7 +8,6 @@ from platform_methods import run_in_subprocess env.core_sources = [] - # Generate global defaults gd_call = "" gd_inc = "" @@ -18,7 +17,7 @@ for x in env.global_defaults: gd_inc += '#include "platform/' + x + '/globals/global_defaults.h"\n' gd_call += "\tregister_" + x + "_global_defaults();\n" -gd_cpp = '#include "project_settings.h"\n' +gd_cpp = '#include "core/project_settings.h"\n' gd_cpp += gd_inc gd_cpp += "void ProjectSettings::register_global_defaults() {\n" + gd_call + "\n}\n" @@ -52,13 +51,17 @@ if ("SCRIPT_AES256_ENCRYPTION_KEY" in os.environ): # 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 \"core/project_settings.h\"\nuint8_t script_encryption_key[32]={" + txt + "};\n") + +# Add required thirdparty code. +env_thirdparty = env.Clone() +env_thirdparty.disable_warnings() -# Add required thirdparty code. Header paths are hardcoded, we don't need to append +# Misc 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 = [ +thirdparty_misc_dir = "#thirdparty/misc/" +thirdparty_misc_sources = [ # C sources "base64.c", "fastlz.c", @@ -72,10 +75,34 @@ thirdparty_sources = [ "pcg.cpp", "triangulator.cpp", ] -thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env.add_source_files(env.core_sources, thirdparty_sources) - -# Minizip library, can be unbundled in theory +thirdparty_misc_sources = [thirdparty_misc_dir + file for file in thirdparty_misc_sources] +env_thirdparty.add_source_files(env.core_sources, thirdparty_misc_sources) + +# Zlib library, can be unbundled +if env['builtin_zlib']: + thirdparty_zlib_dir = "#thirdparty/zlib/" + thirdparty_zlib_sources = [ + "adler32.c", + "compress.c", + "crc32.c", + "deflate.c", + "infback.c", + "inffast.c", + "inflate.c", + "inftrees.c", + "trees.c", + "uncompr.c", + "zutil.c", + ] + thirdparty_zlib_sources = [thirdparty_zlib_dir + file for file in thirdparty_zlib_sources] + + env_thirdparty.Append(CPPPATH=[thirdparty_zlib_dir]) + # Needs to be available in main env too + env.Append(CPPPATH=[thirdparty_zlib_dir]) + + env_thirdparty.add_source_files(env.core_sources, thirdparty_zlib_sources) + +# Minizip library, could be unbundled in theory # However, our version has some custom modifications, so it won't compile with the system one thirdparty_minizip_dir = "#thirdparty/minizip/" thirdparty_minizip_sources = [ @@ -84,15 +111,50 @@ thirdparty_minizip_sources = [ "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) - -if 'builtin_zstd' in env and env['builtin_zstd']: - SConscript("#thirdparty/zstd/SCsub") +env_thirdparty.add_source_files(env.core_sources, thirdparty_minizip_sources) + +# Zstd library, can be unbundled in theory +# though we currently use some private symbols +# https://github.com/godotengine/godot/issues/17374 +if env['builtin_zstd']: + thirdparty_zstd_dir = "#thirdparty/zstd/" + thirdparty_zstd_sources = [ + "common/entropy_common.c", + "common/error_private.c", + "common/fse_decompress.c", + "common/pool.c", + "common/threading.c", + "common/xxhash.c", + "common/zstd_common.c", + "compress/fse_compress.c", + "compress/huf_compress.c", + "compress/zstd_compress.c", + "compress/zstd_double_fast.c", + "compress/zstd_fast.c", + "compress/zstd_lazy.c", + "compress/zstd_ldm.c", + "compress/zstdmt_compress.c", + "compress/zstd_opt.c", + "decompress/huf_decompress.c", + "decompress/zstd_decompress.c", + ] + thirdparty_zstd_sources = [thirdparty_zstd_dir + file for file in thirdparty_zstd_sources] + + env_thirdparty.Append(CPPPATH=[thirdparty_zstd_dir, thirdparty_zstd_dir + "common"]) + env_thirdparty.Append(CCFLAGS="-DZSTD_STATIC_LINKING_ONLY") + env.Append(CPPPATH=thirdparty_zstd_dir) + # Also needed in main env includes will trigger warnings + env.Append(CCFLAGS="-DZSTD_STATIC_LINKING_ONLY") + + env_thirdparty.add_source_files(env.core_sources, thirdparty_zstd_sources) # Godot's own sources env.add_source_files(env.core_sources, "*.cpp") +# Certificates +env.Depends("#core/io/certs_compressed.gen.h", ["#thirdparty/certs/ca-certificates.crt", env.Value(env['builtin_certs']), env.Value(env['system_certs_path'])]) +env.CommandNoCache("#core/io/certs_compressed.gen.h", "#thirdparty/certs/ca-certificates.crt", run_in_subprocess(core_builders.make_certs_header)) # Make binders env.CommandNoCache(['method_bind.gen.inc', 'method_bind_ext.gen.inc'], 'make_binders.py', run_in_subprocess(make_binders.run)) @@ -114,10 +176,8 @@ SConscript('os/SCsub') SConscript('math/SCsub') SConscript('io/SCsub') SConscript('bind/SCsub') -SConscript('helper/SCsub') # Build it all as a library lib = env.add_library("core", env.core_sources) env.Prepend(LIBS=[lib]) -Export('env') diff --git a/core/allocators.h b/core/allocators.h index e17ab298d6..840b117958 100644 --- a/core/allocators.h +++ b/core/allocators.h @@ -31,7 +31,8 @@ #ifndef ALLOCATORS_H #define ALLOCATORS_H -#include "os/memory.h" +#include "core/os/memory.h" + template <int PREALLOC_COUNT = 64, int MAX_HANDS = 8> class BalloonAllocator { diff --git a/core/array.cpp b/core/array.cpp index ebad0df126..9708452850 100644 --- a/core/array.cpp +++ b/core/array.cpp @@ -30,10 +30,10 @@ #include "array.h" -#include "hashfuncs.h" -#include "object.h" -#include "variant.h" -#include "vector.h" +#include "core/hashfuncs.h" +#include "core/object.h" +#include "core/variant.h" +#include "core/vector.h" class ArrayPrivate { public: diff --git a/core/array.h b/core/array.h index c824c9b4f7..fa5113a376 100644 --- a/core/array.h +++ b/core/array.h @@ -31,7 +31,8 @@ #ifndef ARRAY_H #define ARRAY_H -#include "typedefs.h" +#include "core/typedefs.h" + class Variant; class ArrayPrivate; class Object; diff --git a/core/bind/SCsub b/core/bind/SCsub index 4efc902717..1c5f954470 100644 --- a/core/bind/SCsub +++ b/core/bind/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.core_sources, "*.cpp") - -Export('env') diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index 2bd271205a..bebb4bd83d 100644 --- a/core/bind/core_bind.cpp +++ b/core/bind/core_bind.cpp @@ -30,14 +30,14 @@ #include "core_bind.h" +#include "core/io/file_access_compressed.h" +#include "core/io/file_access_encrypted.h" +#include "core/io/json.h" +#include "core/io/marshalls.h" +#include "core/math/geometry.h" +#include "core/os/keyboard.h" +#include "core/os/os.h" #include "core/project_settings.h" -#include "geometry.h" -#include "io/file_access_compressed.h" -#include "io/file_access_encrypted.h" -#include "io/json.h" -#include "io/marshalls.h" -#include "os/keyboard.h" -#include "os/os.h" #include "thirdparty/misc/base64.h" @@ -180,6 +180,7 @@ void _ResourceSaver::_bind_methods() { BIND_ENUM_CONSTANT(FLAG_OMIT_EDITOR_PROPERTIES); BIND_ENUM_CONSTANT(FLAG_SAVE_BIG_ENDIAN); BIND_ENUM_CONSTANT(FLAG_COMPRESS); + BIND_ENUM_CONSTANT(FLAG_REPLACE_SUBRESOURCE_PATHS); } _ResourceSaver::_ResourceSaver() { @@ -1019,6 +1020,11 @@ void _OS::center_window() { OS::get_singleton()->center_window(); } +void _OS::move_window_to_foreground() { + + OS::get_singleton()->move_window_to_foreground(); +} + bool _OS::is_debug_build() const { #ifdef DEBUG_ENABLED @@ -1120,6 +1126,7 @@ void _OS::_bind_methods() { ClassDB::bind_method(D_METHOD("request_attention"), &_OS::request_attention); ClassDB::bind_method(D_METHOD("get_real_window_size"), &_OS::get_real_window_size); ClassDB::bind_method(D_METHOD("center_window"), &_OS::center_window); + ClassDB::bind_method(D_METHOD("move_window_to_foreground"), &_OS::move_window_to_foreground); ClassDB::bind_method(D_METHOD("set_borderless_window", "borderless"), &_OS::set_borderless_window); ClassDB::bind_method(D_METHOD("get_borderless_window"), &_OS::get_borderless_window); @@ -2487,7 +2494,7 @@ _Thread::~_Thread() { if (active) { ERR_EXPLAIN("Reference to a Thread object object was lost while the thread is still running..."); } - ERR_FAIL_COND(active == true); + ERR_FAIL_COND(active); } ///////////////////////////////////// diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h index 21aea12b23..437d7515c6 100644 --- a/core/bind/core_bind.h +++ b/core/bind/core_bind.h @@ -31,15 +31,15 @@ #ifndef CORE_BIND_H #define CORE_BIND_H -#include "image.h" -#include "io/compression.h" -#include "io/resource_loader.h" -#include "io/resource_saver.h" -#include "os/dir_access.h" -#include "os/file_access.h" -#include "os/os.h" -#include "os/semaphore.h" -#include "os/thread.h" +#include "core/image.h" +#include "core/io/compression.h" +#include "core/io/resource_loader.h" +#include "core/io/resource_saver.h" +#include "core/os/dir_access.h" +#include "core/os/file_access.h" +#include "core/os/os.h" +#include "core/os/semaphore.h" +#include "core/os/thread.h" class _ResourceLoader : public Object { GDCLASS(_ResourceLoader, Object); @@ -80,6 +80,7 @@ public: FLAG_OMIT_EDITOR_PROPERTIES = 8, FLAG_SAVE_BIG_ENDIAN = 16, FLAG_COMPRESS = 32, + FLAG_REPLACE_SUBRESOURCE_PATHS = 64, }; static _ResourceSaver *get_singleton() { return singleton; } @@ -184,6 +185,7 @@ public: virtual bool is_window_always_on_top() const; virtual void request_attention(); virtual void center_window(); + virtual void move_window_to_foreground(); virtual void set_borderless_window(bool p_borderless); virtual bool get_borderless_window() const; diff --git a/core/class_db.cpp b/core/class_db.cpp index 03b214aa41..71809d5454 100644 --- a/core/class_db.cpp +++ b/core/class_db.cpp @@ -30,8 +30,9 @@ #include "class_db.h" -#include "os/mutex.h" -#include "version.h" +#include "core/engine.h" +#include "core/os/mutex.h" +#include "core/version.h" #define OBJTYPE_RLOCK RWLockRead _rw_lockr_(lock); #define OBJTYPE_WLOCK RWLockWrite _rw_lockw_(lock); @@ -512,7 +513,12 @@ Object *ClassDB::instance(const StringName &p_class) { ERR_FAIL_COND_V(ti->disabled, NULL); ERR_FAIL_COND_V(!ti->creation_func, NULL); } - +#ifdef TOOLS_ENABLED + if (ti->api == API_EDITOR && !Engine::get_singleton()->is_editor_hint()) { + ERR_PRINTS("Class '" + String(p_class) + "' can only be instantiated by editor."); + return NULL; + } +#endif return ti->creation_func(); } bool ClassDB::can_instance(const StringName &p_class) { @@ -803,10 +809,10 @@ void ClassDB::add_signal(StringName p_class, const MethodInfo &p_signal) { ClassInfo *type = classes.getptr(p_class); ERR_FAIL_COND(!type); - ClassInfo *check = type; StringName sname = p_signal.name; -#ifdef DEBUG_METHODS_ENABLED +#ifdef DEBUG_METHODS_ENABLED + ClassInfo *check = type; while (check) { if (check->signal_map.has(sname)) { ERR_EXPLAIN("Type " + String(p_class) + " already has signal: " + String(sname)); diff --git a/core/class_db.h b/core/class_db.h index 66a67f6c9f..11cc3033cf 100644 --- a/core/class_db.h +++ b/core/class_db.h @@ -31,16 +31,16 @@ #ifndef CLASS_DB_H #define CLASS_DB_H -#include "method_bind.h" -#include "object.h" -#include "print_string.h" +#include "core/method_bind.h" +#include "core/object.h" +#include "core/print_string.h" /** @author Juan Linietsky <reduzio@gmail.com> */ /** To bind more then 6 parameters include this: - * #include "method_bind_ext.gen.inc" + * #include "core/method_bind_ext.gen.inc" */ #define DEFVAL(m_defval) (m_defval) diff --git a/core/color.cpp b/core/color.cpp index 17c9e2daeb..ac314417ec 100644 --- a/core/color.cpp +++ b/core/color.cpp @@ -30,10 +30,10 @@ #include "color.h" -#include "color_names.inc" -#include "map.h" -#include "math_funcs.h" -#include "print_string.h" +#include "core/color_names.inc" +#include "core/map.h" +#include "core/math/math_funcs.h" +#include "core/print_string.h" uint32_t Color::to_argb32() const { @@ -468,7 +468,7 @@ String Color::to_html(bool p_alpha) const { return txt; } -Color Color::from_hsv(float p_h, float p_s, float p_v, float p_a) { +Color Color::from_hsv(float p_h, float p_s, float p_v, float p_a) const { p_h = Math::fmod(p_h * 360.0f, 360.0f); if (p_h < 0.0) diff --git a/core/color.h b/core/color.h index 00f4c9e9e8..d6ad5f91c5 100644 --- a/core/color.h +++ b/core/color.h @@ -31,8 +31,8 @@ #ifndef COLOR_H #define COLOR_H -#include "math_funcs.h" -#include "ustring.h" +#include "core/math/math_funcs.h" +#include "core/ustring.h" /** @author Juan Linietsky <reduzio@gmail.com> */ @@ -194,7 +194,7 @@ struct Color { static bool html_is_valid(const String &p_color); static Color named(const String &p_name); String to_html(bool p_alpha = true) const; - Color from_hsv(float p_h, float p_s, float p_v, float p_a); + Color from_hsv(float p_h, float p_s, float p_v, float p_a) const; static Color from_rgbe9995(uint32_t p_color); _FORCE_INLINE_ bool operator<(const Color &p_color) const; //used in set keys diff --git a/core/color_names.inc b/core/color_names.inc index 3ae42648d0..e126bfe0f8 100644 --- a/core/color_names.inc +++ b/core/color_names.inc @@ -1,5 +1,5 @@ // Names from https://en.wikipedia.org/wiki/List_of_colors (through https://raw.githubusercontent.com/SuperUserNameMan/color_to_name/616a7cddafefda91478b7bc26167de97fb5badb1/godot_version.gd), slightly edited and normalized -#include "map.h" +#include "core/map.h" static Map<String, Color> _named_colors; static void _populate_named_colors() { diff --git a/core/command_queue_mt.cpp b/core/command_queue_mt.cpp index a39c920dfa..1b3a74bd0d 100644 --- a/core/command_queue_mt.cpp +++ b/core/command_queue_mt.cpp @@ -30,7 +30,7 @@ #include "command_queue_mt.h" -#include "os/os.h" +#include "core/os/os.h" void CommandQueueMT::lock() { diff --git a/core/command_queue_mt.h b/core/command_queue_mt.h index 7978eaa7bf..5911cf5006 100644 --- a/core/command_queue_mt.h +++ b/core/command_queue_mt.h @@ -31,11 +31,11 @@ #ifndef COMMAND_QUEUE_MT_H #define COMMAND_QUEUE_MT_H -#include "os/memory.h" -#include "os/mutex.h" -#include "os/semaphore.h" -#include "simple_type.h" -#include "typedefs.h" +#include "core/os/memory.h" +#include "core/os/mutex.h" +#include "core/os/semaphore.h" +#include "core/simple_type.h" +#include "core/typedefs.h" /** @author Juan Linietsky <reduzio@gmail.com> */ diff --git a/core/compressed_translation.cpp b/core/compressed_translation.cpp index 46df63066b..7dd5308fab 100644 --- a/core/compressed_translation.cpp +++ b/core/compressed_translation.cpp @@ -30,7 +30,7 @@ #include "compressed_translation.h" -#include "pair.h" +#include "core/pair.h" extern "C" { #include "thirdparty/misc/smaz.h" diff --git a/core/compressed_translation.h b/core/compressed_translation.h index ccc47d0bf6..b3d193b478 100644 --- a/core/compressed_translation.h +++ b/core/compressed_translation.h @@ -31,7 +31,7 @@ #ifndef COMPRESSED_TRANSLATION_H #define COMPRESSED_TRANSLATION_H -#include "translation.h" +#include "core/translation.h" class PHashTranslation : public Translation { diff --git a/core/core_builders.py b/core/core_builders.py index 90e505aab9..f3a9e3b221 100644 --- a/core/core_builders.py +++ b/core/core_builders.py @@ -4,7 +4,40 @@ All such functions are invoked in a subprocess on Windows to prevent build flaki """ from platform_methods import subprocess_main -from compat import iteritems, itervalues, open_utf8, escape_string +from compat import iteritems, itervalues, open_utf8, escape_string, byte_to_str + + +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") + + # System certs path. Editor will use them if defined. (for package maintainers) + path = env['system_certs_path'] + g.write("#define _SYSTEM_CERTS_PATH \"%s\"\n" % str(path)) + if env['builtin_certs']: + # Defined here and not in env so changing it does not trigger a full rebuild. + g.write("#define BUILTIN_CERTS_ENABLED\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_authors_header(target, source, env): diff --git a/core/core_string_names.h b/core/core_string_names.h index dcbce14aac..3fdb240408 100644 --- a/core/core_string_names.h +++ b/core/core_string_names.h @@ -31,7 +31,7 @@ #ifndef CORE_STRING_NAMES_H #define CORE_STRING_NAMES_H -#include "string_db.h" +#include "core/string_db.h" class CoreStringNames { diff --git a/core/cowdata.h b/core/cowdata.h index 6a8f644d53..54ece4c856 100644 --- a/core/cowdata.h +++ b/core/cowdata.h @@ -31,8 +31,8 @@ #ifndef COWDATA_H_ #define COWDATA_H_ -#include "os/memory.h" -#include "safe_refcount.h" +#include "core/os/memory.h" +#include "core/safe_refcount.h" template <class T> class Vector; @@ -87,7 +87,10 @@ private: #if defined(_add_overflow) && defined(_mul_overflow) size_t o; size_t p; - if (_mul_overflow(p_elements, sizeof(T), &o)) return false; + if (_mul_overflow(p_elements, sizeof(T), &o)) { + *out = 0; + 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; diff --git a/core/dictionary.cpp b/core/dictionary.cpp index 9cc913fa0d..ccbdff3816 100644 --- a/core/dictionary.cpp +++ b/core/dictionary.cpp @@ -30,9 +30,9 @@ #include "dictionary.h" -#include "ordered_hash_map.h" -#include "safe_refcount.h" -#include "variant.h" +#include "core/ordered_hash_map.h" +#include "core/safe_refcount.h" +#include "core/variant.h" struct DictionaryPrivate { @@ -145,6 +145,11 @@ bool Dictionary::operator==(const Dictionary &p_dictionary) const { return _p == p_dictionary._p; } +bool Dictionary::operator!=(const Dictionary &p_dictionary) const { + + return _p != p_dictionary._p; +} + void Dictionary::_ref(const Dictionary &p_from) const { //make a copy first (thread safe) diff --git a/core/dictionary.h b/core/dictionary.h index dbf2233819..d3b98c2f63 100644 --- a/core/dictionary.h +++ b/core/dictionary.h @@ -31,9 +31,10 @@ #ifndef DICTIONARY_H #define DICTIONARY_H -#include "array.h" -#include "list.h" -#include "ustring.h" +#include "core/array.h" +#include "core/list.h" +#include "core/ustring.h" + class Variant; struct DictionaryPrivate; @@ -68,6 +69,7 @@ public: bool erase(const Variant &p_key); bool operator==(const Dictionary &p_dictionary) const; + bool operator!=(const Dictionary &p_dictionary) const; uint32_t hash() const; void operator=(const Dictionary &p_dictionary); diff --git a/core/dvector.h b/core/dvector.h index e03a755e6c..2830c57ec0 100644 --- a/core/dvector.h +++ b/core/dvector.h @@ -31,12 +31,12 @@ #ifndef DVECTOR_H #define DVECTOR_H -#include "os/copymem.h" -#include "os/memory.h" -#include "os/rw_lock.h" -#include "pool_allocator.h" -#include "safe_refcount.h" -#include "ustring.h" +#include "core/os/copymem.h" +#include "core/os/memory.h" +#include "core/os/rw_lock.h" +#include "core/pool_allocator.h" +#include "core/safe_refcount.h" +#include "core/ustring.h" struct MemoryPool { @@ -149,7 +149,7 @@ class PoolVector { } } - if (old_alloc->refcount.unref() == true) { + if (old_alloc->refcount.unref()) { //this should never happen but.. #ifdef DEBUG_ENABLED @@ -209,7 +209,7 @@ class PoolVector { if (!alloc) return; - if (alloc->refcount.unref() == false) { + if (!alloc->refcount.unref()) { alloc = NULL; return; } diff --git a/core/engine.cpp b/core/engine.cpp index 7c8024b946..fceb602dcf 100644 --- a/core/engine.cpp +++ b/core/engine.cpp @@ -30,11 +30,11 @@ #include "engine.h" -#include "authors.gen.h" -#include "donors.gen.h" -#include "license.gen.h" -#include "version.h" -#include "version_hash.gen.h" +#include "core/authors.gen.h" +#include "core/donors.gen.h" +#include "core/license.gen.h" +#include "core/version.h" +#include "core/version_hash.gen.h" void Engine::set_iterations_per_second(int p_ips) { diff --git a/core/engine.h b/core/engine.h index 031ba29cd6..82b1e5d681 100644 --- a/core/engine.h +++ b/core/engine.h @@ -31,10 +31,10 @@ #ifndef ENGINE_H #define ENGINE_H -#include "list.h" -#include "os/main_loop.h" -#include "ustring.h" -#include "vector.h" +#include "core/list.h" +#include "core/os/main_loop.h" +#include "core/ustring.h" +#include "core/vector.h" class Engine { @@ -125,6 +125,7 @@ public: String get_license_text() const; Engine(); + virtual ~Engine() {} }; #endif // ENGINE_H diff --git a/core/error_macros.cpp b/core/error_macros.cpp index 5786802930..c1a0dd0dfc 100644 --- a/core/error_macros.cpp +++ b/core/error_macros.cpp @@ -30,7 +30,7 @@ #include "error_macros.h" -#include "io/logger.h" +#include "core/io/logger.h" #include "os/os.h" bool _err_error_exists = false; diff --git a/core/error_macros.h b/core/error_macros.h index bee738ceea..8cec18d24f 100644 --- a/core/error_macros.h +++ b/core/error_macros.h @@ -31,7 +31,7 @@ #ifndef ERROR_MACROS_H #define ERROR_MACROS_H -#include "typedefs.h" +#include "core/typedefs.h" /** * Error macros. Unlike exceptions and asserts, these macros try to maintain consistency and stability * inside the code. It is recommended to always return processable data, so in case of an error, the diff --git a/core/func_ref.h b/core/func_ref.h index 681fe747d6..930c376abf 100644 --- a/core/func_ref.h +++ b/core/func_ref.h @@ -31,7 +31,7 @@ #ifndef FUNC_REF_H #define FUNC_REF_H -#include "reference.h" +#include "core/reference.h" class FuncRef : public Reference { diff --git a/core/global_constants.cpp b/core/global_constants.cpp index 962881e720..c70f960a66 100644 --- a/core/global_constants.cpp +++ b/core/global_constants.cpp @@ -30,10 +30,10 @@ #include "global_constants.h" -#include "object.h" -#include "os/input_event.h" -#include "os/keyboard.h" -#include "variant.h" +#include "core/object.h" +#include "core/os/input_event.h" +#include "core/os/keyboard.h" +#include "core/variant.h" struct _GlobalConstant { diff --git a/core/global_constants.h b/core/global_constants.h index 76f618989c..48f16934a3 100644 --- a/core/global_constants.h +++ b/core/global_constants.h @@ -31,7 +31,7 @@ #ifndef GLOBAL_CONSTANTS_H #define GLOBAL_CONSTANTS_H -#include "string_db.h" +#include "core/string_db.h" class GlobalConstants { public: diff --git a/core/hash_map.h b/core/hash_map.h index 2df743ba7d..3869cd3c36 100644 --- a/core/hash_map.h +++ b/core/hash_map.h @@ -31,12 +31,12 @@ #ifndef HASH_MAP_H #define HASH_MAP_H -#include "error_macros.h" -#include "hashfuncs.h" -#include "list.h" -#include "math_funcs.h" -#include "os/memory.h" -#include "ustring.h" +#include "core/error_macros.h" +#include "core/hashfuncs.h" +#include "core/list.h" +#include "core/math/math_funcs.h" +#include "core/os/memory.h" +#include "core/ustring.h" /** * @class HashMap @@ -150,7 +150,7 @@ private: if (new_hash_table_power == -1) return; - Element **new_hash_table = memnew_arr(Element *, (1 << new_hash_table_power)); + Element **new_hash_table = memnew_arr(Element *, ((uint64_t)1 << new_hash_table_power)); if (!new_hash_table) { ERR_PRINT("Out of Memory"); @@ -230,7 +230,7 @@ private: if (!p_t.hash_table || p_t.hash_table_power == 0) return; /* not copying from empty table */ - hash_table = memnew_arr(Element *, 1 << p_t.hash_table_power); + hash_table = memnew_arr(Element *, (uint64_t)1 << p_t.hash_table_power); hash_table_power = p_t.hash_table_power; elements = p_t.elements; diff --git a/core/hashfuncs.h b/core/hashfuncs.h index 735e679d1e..457cfadc9c 100644 --- a/core/hashfuncs.h +++ b/core/hashfuncs.h @@ -31,12 +31,12 @@ #ifndef HASHFUNCS_H #define HASHFUNCS_H -#include "math_defs.h" -#include "math_funcs.h" -#include "node_path.h" -#include "string_db.h" -#include "typedefs.h" -#include "ustring.h" +#include "core/math/math_defs.h" +#include "core/math/math_funcs.h" +#include "core/node_path.h" +#include "core/string_db.h" +#include "core/typedefs.h" +#include "core/ustring.h" /** * Hashing functions diff --git a/core/helper/SCsub b/core/helper/SCsub deleted file mode 100644 index 4efc902717..0000000000 --- a/core/helper/SCsub +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env python - -Import('env') - -env.add_source_files(env.core_sources, "*.cpp") - -Export('env') diff --git a/core/image.cpp b/core/image.cpp index 7778169995..172f5e517a 100644 --- a/core/image.cpp +++ b/core/image.cpp @@ -30,15 +30,15 @@ #include "image.h" +#include "core/hash_map.h" #include "core/io/image_loader.h" +#include "core/io/resource_loader.h" +#include "core/math/math_funcs.h" #include "core/os/copymem.h" -#include "hash_map.h" -#include "math_funcs.h" -#include "print_string.h" +#include "core/print_string.h" -#include "io/resource_loader.h" -#include "math_funcs.h" #include "thirdparty/misc/hq2x.h" + #include <stdio.h> const char *Image::format_names[Image::FORMAT_MAX] = { @@ -307,6 +307,7 @@ void Image::_get_mipmap_offset_and_size(int p_mipmap, int &r_offset, int &r_widt r_width = w; r_height = h; } + int Image::get_mipmap_offset(int p_mipmap) const { ERR_FAIL_INDEX_V(p_mipmap, get_mipmap_count() + 1, -1); @@ -499,8 +500,6 @@ void Image::convert(Format p_new_format) { bool gen_mipmaps = mipmaps; - //mipmaps=false; - _copy_internals_from(new_img); if (gen_mipmaps) @@ -781,9 +780,9 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { // Setup mipmap-aware scaling Image dst2; - int mip1; - int mip2; - float mip1_weight; + int mip1 = 0; + int mip2 = 0; + float mip1_weight = 0; if (mipmap_aware) { float avg_scale = ((float)p_width / width + (float)p_height / height) * 0.5f; if (avg_scale >= 1.0f) { @@ -799,6 +798,7 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { if (interpolate_mipmaps) { dst2.create(p_width, p_height, 0, format); } + bool had_mipmaps = mipmaps; if (interpolate_mipmaps && !had_mipmaps) { generate_mipmaps(); @@ -951,6 +951,7 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { } void Image::crop_from_point(int p_x, int p_y, int p_width, int p_height) { + if (!_can_modify(format)) { ERR_EXPLAIN("Cannot crop in indexed, compressed or custom image formats."); ERR_FAIL(); @@ -996,7 +997,7 @@ void Image::crop_from_point(int p_x, int p_y, int p_width, int p_height) { } } - if (mipmaps > 0) + if (has_mipmaps()) dst.generate_mipmaps(); _copy_internals_from(dst); } @@ -1013,10 +1014,10 @@ void Image::flip_y() { ERR_FAIL(); } - bool gm = mipmaps; - - if (gm) + bool used_mipmaps = has_mipmaps(); + if (used_mipmaps) { clear_mipmaps(); + } { PoolVector<uint8_t>::Write w = data.write(); @@ -1037,8 +1038,9 @@ void Image::flip_y() { } } - if (gm) + if (used_mipmaps) { generate_mipmaps(); + } } void Image::flip_x() { @@ -1048,9 +1050,10 @@ void Image::flip_x() { ERR_FAIL(); } - bool gm = mipmaps; - if (gm) + bool used_mipmaps = has_mipmaps(); + if (used_mipmaps) { clear_mipmaps(); + } { PoolVector<uint8_t>::Write w = data.write(); @@ -1071,8 +1074,9 @@ void Image::flip_x() { } } - if (gm) + if (used_mipmaps) { generate_mipmaps(); + } } int Image::_get_dst_image_size(int p_width, int p_height, Format p_format, int &r_mipmaps, int p_mipmaps) { @@ -1133,20 +1137,23 @@ template <class Component, int CC, bool renormalize, static void _generate_po2_mipmap(const Component *p_src, Component *p_dst, uint32_t p_width, uint32_t p_height) { //fast power of 2 mipmap generation - uint32_t dst_w = p_width >> 1; - uint32_t dst_h = p_height >> 1; + uint32_t dst_w = MAX(p_width >> 1, 1); + uint32_t dst_h = MAX(p_height >> 1, 1); + + int right_step = (p_width == 1) ? 0 : CC; + int down_step = (p_height == 1) ? 0 : (p_width * CC); for (uint32_t i = 0; i < dst_h; i++) { - const Component *rup_ptr = &p_src[i * 2 * p_width * CC]; - const Component *rdown_ptr = rup_ptr + p_width * CC; + const Component *rup_ptr = &p_src[i * 2 * down_step]; + const Component *rdown_ptr = rup_ptr + down_step; Component *dst_ptr = &p_dst[i * dst_w * CC]; uint32_t count = dst_w; while (count--) { for (int j = 0; j < CC; j++) { - average_func(dst_ptr[j], rup_ptr[j], rup_ptr[j + CC], rdown_ptr[j], rdown_ptr[j + CC]); + average_func(dst_ptr[j], rup_ptr[j], rup_ptr[j + right_step], rdown_ptr[j], rdown_ptr[j + right_step]); } if (renormalize) { @@ -1154,8 +1161,8 @@ static void _generate_po2_mipmap(const Component *p_src, Component *p_dst, uint3 } dst_ptr += CC; - rup_ptr += CC * 2; - rdown_ptr += CC * 2; + rup_ptr += right_step * 2; + rdown_ptr += right_step * 2; } } } @@ -1164,12 +1171,13 @@ void Image::expand_x2_hq2x() { ERR_FAIL_COND(!_can_modify(format)); - Format current = format; - bool mm = has_mipmaps(); - if (mm) { + bool used_mipmaps = has_mipmaps(); + if (used_mipmaps) { clear_mipmaps(); } + Format current = format; + if (current != FORMAT_RGBA8) convert(FORMAT_RGBA8); @@ -1190,6 +1198,8 @@ void Image::expand_x2_hq2x() { if (current != FORMAT_RGBA8) convert(current); + // FIXME: This is likely meant to use "used_mipmaps" as defined above, but if we do, + // we end up with a regression: GH-22747 if (mipmaps) { generate_mipmaps(); } @@ -1313,7 +1323,7 @@ Error Image::generate_mipmaps(bool p_renormalize) { int prev_h = height; int prev_w = width; - for (int i = 1; i < mmcount; i++) { + for (int i = 1; i <= mmcount; i++) { int ofs, w, h; _get_mipmap_offset_and_size(i, ofs, w, h); @@ -1463,7 +1473,8 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma void Image::create(const char **p_xpm) { - int size_width, size_height; + int size_width = 0; + int size_height = 0; int pixelchars = 0; mipmaps = false; bool has_alpha = false; @@ -1479,8 +1490,8 @@ void Image::create(const char **p_xpm) { int line = 0; HashMap<String, Color> colormap; - int colormap_size; - uint32_t pixel_size; + int colormap_size = 0; + uint32_t pixel_size = 0; PoolVector<uint8_t>::Write w; while (status != DONE) { diff --git a/core/image.h b/core/image.h index 6af55ca8d9..11f9380c3c 100644 --- a/core/image.h +++ b/core/image.h @@ -31,10 +31,10 @@ #ifndef IMAGE_H #define IMAGE_H -#include "color.h" -#include "dvector.h" -#include "rect2.h" -#include "resource.h" +#include "core/color.h" +#include "core/dvector.h" +#include "core/math/rect2.h" +#include "core/resource.h" /** * @author Juan Linietsky <reduzio@gmail.com> diff --git a/core/input_map.cpp b/core/input_map.cpp index ffc8a39da5..b88d99470a 100644 --- a/core/input_map.cpp +++ b/core/input_map.cpp @@ -30,8 +30,8 @@ #include "input_map.h" -#include "os/keyboard.h" -#include "project_settings.h" +#include "core/os/keyboard.h" +#include "core/project_settings.h" InputMap *InputMap::singleton = NULL; @@ -199,6 +199,10 @@ bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const Str Ref<InputEventAction> input_event_action = p_event; if (input_event_action.is_valid()) { + if (p_pressed != NULL) + *p_pressed = input_event_action->is_pressed(); + if (p_strength != NULL) + *p_strength = (*p_pressed) ? 1.0f : 0.0f; return input_event_action->get_action() == p_action; } diff --git a/core/input_map.h b/core/input_map.h index bdec75c65b..8f286277d1 100644 --- a/core/input_map.h +++ b/core/input_map.h @@ -31,8 +31,8 @@ #ifndef INPUT_MAP_H #define INPUT_MAP_H -#include "object.h" -#include "os/input_event.h" +#include "core/object.h" +#include "core/os/input_event.h" class InputMap : public Object { diff --git a/core/io/SCsub b/core/io/SCsub index 79b56cb716..1c5f954470 100644 --- a/core/io/SCsub +++ b/core/io/SCsub @@ -3,6 +3,3 @@ Import('env') env.add_source_files(env.core_sources, "*.cpp") - -Export('env') - diff --git a/core/io/compression.cpp b/core/io/compression.cpp index bc3bfcf356..3c0b6541bd 100644 --- a/core/io/compression.cpp +++ b/core/io/compression.cpp @@ -29,9 +29,10 @@ /*************************************************************************/ #include "compression.h" -#include "os/copymem.h" -#include "project_settings.h" -#include "zip_io.h" + +#include "core/io/zip_io.h" +#include "core/os/copymem.h" +#include "core/project_settings.h" #include "thirdparty/misc/fastlz.h" @@ -174,7 +175,7 @@ int Compression::decompress(uint8_t *p_dst, int p_dst_max_size, const uint8_t *p } break; case MODE_ZSTD: { ZSTD_DCtx *dctx = ZSTD_createDCtx(); - if (zstd_long_distance_matching) ZSTD_DCtx_setMaxWindowSize(dctx, 1 << zstd_window_log_size); + if (zstd_long_distance_matching) ZSTD_DCtx_setMaxWindowSize(dctx, (size_t)1 << zstd_window_log_size); int ret = ZSTD_decompressDCtx(dctx, p_dst, p_dst_max_size, p_src, p_src_size); ZSTD_freeDCtx(dctx); return ret; diff --git a/core/io/compression.h b/core/io/compression.h index a0ccd539cb..2f770e6aee 100644 --- a/core/io/compression.h +++ b/core/io/compression.h @@ -31,7 +31,7 @@ #ifndef COMPRESSION_H #define COMPRESSION_H -#include "typedefs.h" +#include "core/typedefs.h" class Compression { diff --git a/core/io/config_file.cpp b/core/io/config_file.cpp index aa06ae5cc0..e2bc0f5d8f 100644 --- a/core/io/config_file.cpp +++ b/core/io/config_file.cpp @@ -29,9 +29,10 @@ /*************************************************************************/ #include "config_file.h" -#include "os/file_access.h" -#include "os/keyboard.h" -#include "variant_parser.h" + +#include "core/os/file_access.h" +#include "core/os/keyboard.h" +#include "core/variant_parser.h" PoolStringArray ConfigFile::_get_sections() const { diff --git a/core/io/config_file.h b/core/io/config_file.h index ac749bed76..2bc439d413 100644 --- a/core/io/config_file.h +++ b/core/io/config_file.h @@ -32,7 +32,7 @@ #define CONFIG_FILE_H #include "core/ordered_hash_map.h" -#include "reference.h" +#include "core/reference.h" class ConfigFile : public Reference { diff --git a/core/io/file_access_buffered.cpp b/core/io/file_access_buffered.cpp index dcaf99e24a..d44c8a9585 100644 --- a/core/io/file_access_buffered.cpp +++ b/core/io/file_access_buffered.cpp @@ -30,9 +30,7 @@ #include "file_access_buffered.h" -#include <string.h> - -#include "error_macros.h" +#include "core/error_macros.h" Error FileAccessBuffered::set_error(Error p_error) const { diff --git a/core/io/file_access_buffered.h b/core/io/file_access_buffered.h index f4ed47d6bc..a36a9a4603 100644 --- a/core/io/file_access_buffered.h +++ b/core/io/file_access_buffered.h @@ -31,10 +31,9 @@ #ifndef FILE_ACCESS_BUFFERED_H #define FILE_ACCESS_BUFFERED_H -#include "os/file_access.h" - -#include "dvector.h" -#include "ustring.h" +#include "core/dvector.h" +#include "core/os/file_access.h" +#include "core/ustring.h" class FileAccessBuffered : public FileAccess { diff --git a/core/io/file_access_compressed.cpp b/core/io/file_access_compressed.cpp index d6547ba19f..645d97ae7e 100644 --- a/core/io/file_access_compressed.cpp +++ b/core/io/file_access_compressed.cpp @@ -29,7 +29,9 @@ /*************************************************************************/ #include "file_access_compressed.h" -#include "print_string.h" + +#include "core/print_string.h" + void FileAccessCompressed::configure(const String &p_magic, Compression::Mode p_mode, int p_block_size) { magic = p_magic.ascii().get_data(); @@ -291,7 +293,6 @@ uint8_t FileAccessCompressed::get_8() const { } else { read_block--; at_end = true; - ret = 0; } } diff --git a/core/io/file_access_compressed.h b/core/io/file_access_compressed.h index 587f58a7c6..5be42a6d32 100644 --- a/core/io/file_access_compressed.h +++ b/core/io/file_access_compressed.h @@ -31,8 +31,8 @@ #ifndef FILE_ACCESS_COMPRESSED_H #define FILE_ACCESS_COMPRESSED_H -#include "io/compression.h" -#include "os/file_access.h" +#include "core/io/compression.h" +#include "core/os/file_access.h" class FileAccessCompressed : public FileAccess { diff --git a/core/io/file_access_encrypted.cpp b/core/io/file_access_encrypted.cpp index 812e881114..28bf55b962 100644 --- a/core/io/file_access_encrypted.cpp +++ b/core/io/file_access_encrypted.cpp @@ -30,9 +30,9 @@ #include "file_access_encrypted.h" +#include "core/os/copymem.h" +#include "core/print_string.h" #include "core/variant.h" -#include "os/copymem.h" -#include "print_string.h" #include "thirdparty/misc/aes256.h" #include "thirdparty/misc/md5.h" diff --git a/core/io/file_access_encrypted.h b/core/io/file_access_encrypted.h index b9365a9fd0..873dbfa7e8 100644 --- a/core/io/file_access_encrypted.h +++ b/core/io/file_access_encrypted.h @@ -31,7 +31,7 @@ #ifndef FILE_ACCESS_ENCRYPTED_H #define FILE_ACCESS_ENCRYPTED_H -#include "os/file_access.h" +#include "core/os/file_access.h" class FileAccessEncrypted : public FileAccess { public: diff --git a/core/io/file_access_memory.cpp b/core/io/file_access_memory.cpp index c4eb2848b1..4c2aa4294d 100644 --- a/core/io/file_access_memory.cpp +++ b/core/io/file_access_memory.cpp @@ -30,10 +30,10 @@ #include "file_access_memory.h" -#include "map.h" -#include "os/copymem.h" -#include "os/dir_access.h" -#include "project_settings.h" +#include "core/map.h" +#include "core/os/copymem.h" +#include "core/os/dir_access.h" +#include "core/project_settings.h" static Map<String, Vector<uint8_t> > *files = NULL; diff --git a/core/io/file_access_memory.h b/core/io/file_access_memory.h index 2136f4cc0c..e474fb0d05 100644 --- a/core/io/file_access_memory.h +++ b/core/io/file_access_memory.h @@ -31,7 +31,7 @@ #ifndef FILE_ACCESS_MEMORY_H #define FILE_ACCESS_MEMORY_H -#include "os/file_access.h" +#include "core/os/file_access.h" class FileAccessMemory : public FileAccess { diff --git a/core/io/file_access_network.cpp b/core/io/file_access_network.cpp index d72d3ca9f1..b9544ac166 100644 --- a/core/io/file_access_network.cpp +++ b/core/io/file_access_network.cpp @@ -29,10 +29,11 @@ /*************************************************************************/ #include "file_access_network.h" -#include "io/ip.h" -#include "marshalls.h" -#include "os/os.h" -#include "project_settings.h" + +#include "core/io/ip.h" +#include "core/io/marshalls.h" +#include "core/os/os.h" +#include "core/project_settings.h" //#define DEBUG_PRINT(m_p) print_line(m_p) //#define DEBUG_TIME(m_what) printf("MS: %s - %lli\n",m_what,OS::get_singleton()->get_ticks_usec()); @@ -226,7 +227,7 @@ FileAccessNetworkClient::FileAccessNetworkClient() { quit = false; singleton = this; last_id = 0; - client = Ref<StreamPeerTCP>(StreamPeerTCP::create_ref()); + client.instance(); sem = Semaphore::create(); lockcount = 0; } @@ -499,8 +500,9 @@ uint64_t FileAccessNetwork::_get_modified_time(const String &p_file) { void FileAccessNetwork::configure() { GLOBAL_DEF("network/remote_fs/page_size", 65536); + ProjectSettings::get_singleton()->set_custom_property_info("network/remote_fs/page_size", PropertyInfo(Variant::INT, "network/remote_fs/page_size", PROPERTY_HINT_RANGE, "1,65536,1,or_greater")); //is used as denominator and can't be zero GLOBAL_DEF("network/remote_fs/page_read_ahead", 4); - GLOBAL_DEF("network/remote_fs/max_pages", 20); + ProjectSettings::get_singleton()->set_custom_property_info("network/remote_fs/page_read_ahead", PropertyInfo(Variant::INT, "network/remote_fs/page_read_ahead", PROPERTY_HINT_RANGE, "0,8,1,or_greater")); } FileAccessNetwork::FileAccessNetwork() { @@ -518,7 +520,6 @@ FileAccessNetwork::FileAccessNetwork() { nc->unlock_mutex(); page_size = GLOBAL_GET("network/remote_fs/page_size"); read_ahead = GLOBAL_GET("network/remote_fs/page_read_ahead"); - max_pages = GLOBAL_GET("network/remote_fs/max_pages"); last_activity_val = 0; waiting_on_page = -1; last_page = -1; diff --git a/core/io/file_access_network.h b/core/io/file_access_network.h index be9bdb1af6..c929e8446d 100644 --- a/core/io/file_access_network.h +++ b/core/io/file_access_network.h @@ -31,10 +31,10 @@ #ifndef FILE_ACCESS_NETWORK_H #define FILE_ACCESS_NETWORK_H -#include "io/stream_peer_tcp.h" -#include "os/file_access.h" -#include "os/semaphore.h" -#include "os/thread.h" +#include "core/io/stream_peer_tcp.h" +#include "core/os/file_access.h" +#include "core/os/semaphore.h" +#include "core/os/thread.h" class FileAccessNetwork; @@ -47,8 +47,6 @@ class FileAccessNetworkClient { int size; }; - int ml; - List<BlockRequest> block_requests; Semaphore *sem; @@ -100,7 +98,6 @@ class FileAccessNetwork : public FileAccess { int page_size; int read_ahead; - int max_pages; mutable int waiting_on_page; mutable int last_activity_val; diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp index efb4c7a073..40f756ba9a 100644 --- a/core/io/file_access_pack.cpp +++ b/core/io/file_access_pack.cpp @@ -29,7 +29,8 @@ /*************************************************************************/ #include "file_access_pack.h" -#include "version.h" + +#include "core/version.h" #include <stdio.h> @@ -168,11 +169,11 @@ bool PackedSourcePCK::try_open_pack(const String &p_path) { uint32_t version = f->get_32(); uint32_t ver_major = f->get_32(); uint32_t ver_minor = f->get_32(); - uint32_t ver_rev = f->get_32(); + f->get_32(); // ver_rev ERR_EXPLAIN("Pack version unsupported: " + itos(version)); ERR_FAIL_COND_V(version != PACK_VERSION, false); - ERR_EXPLAIN("Pack created with a newer version of the engine: " + itos(ver_major) + "." + itos(ver_minor) + "." + itos(ver_rev)); + ERR_EXPLAIN("Pack created with a newer version of the engine: " + itos(ver_major) + "." + itos(ver_minor)); ERR_FAIL_COND_V(ver_major > VERSION_MAJOR || (ver_major == VERSION_MAJOR && ver_minor > VERSION_MINOR), false); for (int i = 0; i < 16; i++) { diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h index f29e431d9a..9e31bcf88a 100644 --- a/core/io/file_access_pack.h +++ b/core/io/file_access_pack.h @@ -31,11 +31,11 @@ #ifndef FILE_ACCESS_PACK_H #define FILE_ACCESS_PACK_H -#include "list.h" -#include "map.h" -#include "os/dir_access.h" -#include "os/file_access.h" -#include "print_string.h" +#include "core/list.h" +#include "core/map.h" +#include "core/os/dir_access.h" +#include "core/os/file_access.h" +#include "core/print_string.h" class PackSource; diff --git a/core/io/file_access_zip.h b/core/io/file_access_zip.h index df83575f6a..9bb1ad221a 100644 --- a/core/io/file_access_zip.h +++ b/core/io/file_access_zip.h @@ -34,7 +34,7 @@ #define FILE_ACCESS_ZIP_H #include "core/io/file_access_pack.h" -#include "map.h" +#include "core/map.h" #include "thirdparty/minizip/unzip.h" @@ -90,8 +90,6 @@ class FileAccessZip : public FileAccess { mutable bool at_eof; - ZipArchive *archive; - public: virtual Error _open(const String &p_path, int p_mode_flags); ///< open a file virtual void close(); ///< close a file diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp index 2425bb6d69..36dd688e77 100644 --- a/core/io/http_client.cpp +++ b/core/io/http_client.cpp @@ -29,8 +29,9 @@ /*************************************************************************/ #include "http_client.h" -#include "io/stream_peer_ssl.h" -#include "version.h" + +#include "core/io/stream_peer_ssl.h" +#include "core/version.h" const char *HTTPClient::_methods[METHOD_MAX] = { "GET", @@ -274,7 +275,7 @@ void HTTPClient::close() { response_headers.clear(); response_str.clear(); - body_size = 0; + body_size = -1; body_left = 0; chunk_left = 0; read_until_eof = false; @@ -348,7 +349,7 @@ Error HTTPClient::poll() { } if (ssl->get_status() == StreamPeerSSL::STATUS_CONNECTED) { - // Handshake has been successfull + // Handshake has been successful handshaking = false; status = STATUS_CONNECTED; return OK; @@ -373,7 +374,20 @@ Error HTTPClient::poll() { } break; } } break; + case STATUS_BODY: case STATUS_CONNECTED: { + // Check if we are still connected + if (ssl) { + Ref<StreamPeerSSL> tmp = connection; + tmp->poll(); + if (tmp->get_status() != StreamPeerSSL::STATUS_CONNECTED) { + status = STATUS_CONNECTION_ERROR; + return ERR_CONNECTION_ERROR; + } + } else if (tcp_connection->get_status() != StreamPeerTCP::STATUS_CONNECTED) { + status = STATUS_CONNECTION_ERROR; + return ERR_CONNECTION_ERROR; + } // Connection established, requests can now be made return OK; } break; @@ -403,7 +417,7 @@ Error HTTPClient::poll() { String response; response.parse_utf8((const char *)response_str.ptr()); Vector<String> responses = response.split("\n"); - body_size = 0; + body_size = -1; chunked = false; body_left = 0; chunk_left = 0; @@ -447,7 +461,7 @@ Error HTTPClient::poll() { } } - if (body_size || chunked) { + if (body_size != -1 || chunked) { status = STATUS_BODY; } else if (!keep_alive) { @@ -467,7 +481,8 @@ Error HTTPClient::poll() { case STATUS_DISCONNECTED: { return ERR_UNCONFIGURED; } break; - case STATUS_CONNECTION_ERROR: { + case STATUS_CONNECTION_ERROR: + case STATUS_SSL_HANDSHAKE_ERROR: { return ERR_CONNECTION_ERROR; } break; case STATUS_CANT_CONNECT: { @@ -664,11 +679,24 @@ Error HTTPClient::_get_http_data(uint8_t *p_buffer, int p_bytes, int &r_received if (blocking) { - Error err = connection->get_data(p_buffer, p_bytes); - if (err == OK) - r_received = p_bytes; - else - r_received = 0; + // We can't use StreamPeer.get_data, since when reaching EOF we will get an + // error without knowing how many bytes we received. + Error err = ERR_FILE_EOF; + int read = 0; + int left = p_bytes; + r_received = 0; + while (left > 0) { + err = connection->get_partial_data(p_buffer + r_received, left, read); + if (err == OK) { + r_received += read; + } else if (err == ERR_FILE_EOF) { + r_received += read; + return err; + } else { + return err; + } + left -= read; + } return err; } else { return connection->get_partial_data(p_buffer, p_bytes, r_received); @@ -682,11 +710,11 @@ void HTTPClient::set_read_chunk_size(int p_size) { HTTPClient::HTTPClient() { - tcp_connection = StreamPeerTCP::create_ref(); + tcp_connection.instance(); resolving = IP::RESOLVER_INVALID_ID; status = STATUS_DISCONNECTED; conn_port = -1; - body_size = 0; + body_size = -1; chunked = false; body_left = 0; read_until_eof = false; diff --git a/core/io/http_client.h b/core/io/http_client.h index 82b56b01db..9e674e4c1d 100644 --- a/core/io/http_client.h +++ b/core/io/http_client.h @@ -31,10 +31,10 @@ #ifndef HTTP_CLIENT_H #define HTTP_CLIENT_H -#include "io/ip.h" -#include "io/stream_peer.h" -#include "io/stream_peer_tcp.h" -#include "reference.h" +#include "core/io/ip.h" +#include "core/io/stream_peer.h" +#include "core/io/stream_peer_tcp.h" +#include "core/reference.h" class HTTPClient : public Reference { diff --git a/core/io/image_loader.cpp b/core/io/image_loader.cpp index b8fd13d67c..3ae9ff676c 100644 --- a/core/io/image_loader.cpp +++ b/core/io/image_loader.cpp @@ -30,7 +30,8 @@ #include "image_loader.h" -#include "print_string.h" +#include "core/print_string.h" + bool ImageFormatLoader::recognize(const String &p_extension) const { List<String> extensions; @@ -117,7 +118,6 @@ RES ResourceFormatLoaderImage::load(const String &p_path, const String &p_origin if (r_error) { *r_error = ERR_CANT_OPEN; } - memdelete(f); return RES(); } diff --git a/core/io/image_loader.h b/core/io/image_loader.h index fbb654c326..561f275e0c 100644 --- a/core/io/image_loader.h +++ b/core/io/image_loader.h @@ -31,11 +31,11 @@ #ifndef IMAGE_LOADER_H #define IMAGE_LOADER_H -#include "image.h" -#include "io/resource_loader.h" -#include "list.h" -#include "os/file_access.h" -#include "ustring.h" +#include "core/image.h" +#include "core/io/resource_loader.h" +#include "core/list.h" +#include "core/os/file_access.h" +#include "core/ustring.h" /** @author Juan Linietsky <reduzio@gmail.com> diff --git a/core/io/ip.cpp b/core/io/ip.cpp index 66bd96df4f..f56e850796 100644 --- a/core/io/ip.cpp +++ b/core/io/ip.cpp @@ -29,9 +29,10 @@ /*************************************************************************/ #include "ip.h" -#include "hash_map.h" -#include "os/semaphore.h" -#include "os/thread.h" + +#include "core/hash_map.h" +#include "core/os/semaphore.h" +#include "core/os/thread.h" VARIANT_ENUM_CAST(IP::ResolverStatus); @@ -117,7 +118,7 @@ IP_Address IP::resolve_hostname(const String &p_hostname, IP::Type p_type) { resolver->mutex->lock(); String key = _IP_ResolverPrivate::get_cache_key(p_hostname, p_type); - if (resolver->cache.has(key)) { + if (resolver->cache.has(key) && resolver->cache[key].is_valid()) { IP_Address res = resolver->cache[key]; resolver->mutex->unlock(); return res; @@ -144,7 +145,7 @@ IP::ResolverID IP::resolve_hostname_queue_item(const String &p_hostname, IP::Typ String key = _IP_ResolverPrivate::get_cache_key(p_hostname, p_type); resolver->queue[id].hostname = p_hostname; resolver->queue[id].type = p_type; - if (resolver->cache.has(key)) { + if (resolver->cache.has(key) && resolver->cache[key].is_valid()) { resolver->queue[id].response = resolver->cache[key]; resolver->queue[id].status = IP::RESOLVER_STATUS_DONE; } else { diff --git a/core/io/ip.h b/core/io/ip.h index d55b05b6fe..967f04a4bd 100644 --- a/core/io/ip.h +++ b/core/io/ip.h @@ -31,8 +31,8 @@ #ifndef IP_H #define IP_H -#include "io/ip_address.h" -#include "os/os.h" +#include "core/io/ip_address.h" +#include "core/os/os.h" struct _IP_ResolverPrivate; diff --git a/core/io/ip_address.h b/core/io/ip_address.h index d7b031b960..607ce81ef4 100644 --- a/core/io/ip_address.h +++ b/core/io/ip_address.h @@ -31,7 +31,7 @@ #ifndef IP_ADDRESS_H #define IP_ADDRESS_H -#include "ustring.h" +#include "core/ustring.h" struct IP_Address { diff --git a/core/io/json.cpp b/core/io/json.cpp index 7b2c5a62df..26e18828e5 100644 --- a/core/io/json.cpp +++ b/core/io/json.cpp @@ -29,7 +29,8 @@ /*************************************************************************/ #include "json.h" -#include "print_string.h" + +#include "core/print_string.h" const char *JSON::tk_name[TK_MAX] = { "'{'", diff --git a/core/io/json.h b/core/io/json.h index 9c12423798..af6c463331 100644 --- a/core/io/json.h +++ b/core/io/json.h @@ -31,7 +31,7 @@ #ifndef JSON_H #define JSON_H -#include "variant.h" +#include "core/variant.h" class JSON { diff --git a/core/io/logger.cpp b/core/io/logger.cpp index 786bec461b..01755c8ee9 100644 --- a/core/io/logger.cpp +++ b/core/io/logger.cpp @@ -30,9 +30,9 @@ #include "logger.h" -#include "os/dir_access.h" -#include "os/os.h" -#include "print_string.h" +#include "core/os/dir_access.h" +#include "core/os/os.h" +#include "core/print_string.h" // va_copy was defined in the C99, but not in C++ standards before C++11. // When you compile C++ without --std=c++<XX> option, compilers still define @@ -45,6 +45,10 @@ #endif #endif +#if defined(MINGW_ENABLED) || defined(_MSC_VER) +#define sprintf sprintf_s +#endif + bool Logger::should_log(bool p_err) { return (!p_err || _print_error_enabled) && (p_err || _print_line_enabled); } diff --git a/core/io/logger.h b/core/io/logger.h index 631e7a0589..d32b43b030 100644 --- a/core/io/logger.h +++ b/core/io/logger.h @@ -31,9 +31,10 @@ #ifndef LOGGER_H #define LOGGER_H -#include "os/file_access.h" -#include "ustring.h" -#include "vector.h" +#include "core/os/file_access.h" +#include "core/ustring.h" +#include "core/vector.h" + #include <stdarg.h> class Logger { @@ -106,4 +107,4 @@ public: virtual ~CompositeLogger(); }; -#endif
\ No newline at end of file +#endif diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index e97df0c261..6338cee39d 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -29,9 +29,11 @@ /*************************************************************************/ #include "marshalls.h" -#include "os/keyboard.h" -#include "print_string.h" -#include "reference.h" + +#include "core/os/keyboard.h" +#include "core/print_string.h" +#include "core/reference.h" + #include <limits.h> #include <stdio.h> @@ -805,7 +807,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo case Variant::INT: { int64_t val = p_variant; - if (val > 0x7FFFFFFF || val < -0x80000000) { + if (val > (int64_t)INT_MAX || val < (int64_t)INT_MIN) { flags |= ENCODE_FLAG_64; } } break; @@ -822,6 +824,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo flags |= ENCODE_FLAG_OBJECT_AS_ID; } } break; + default: {} // nothing to do at this stage } if (buf) { @@ -847,17 +850,16 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } break; case Variant::INT: { - int64_t val = p_variant; - if (val > 0x7FFFFFFF || val < -0x80000000) { + if (flags & ENCODE_FLAG_64) { //64 bits if (buf) { - encode_uint64(val, buf); + encode_uint64(p_variant.operator int64_t(), buf); } r_len += 8; } else { if (buf) { - encode_uint32(int32_t(val), buf); + encode_uint32(p_variant.operator int32_t(), buf); } r_len += 4; @@ -865,9 +867,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } break; case Variant::REAL: { - double d = p_variant; - float f = d; - if (double(f) != d) { + if (flags & ENCODE_FLAG_64) { if (buf) { encode_double(p_variant.operator double(), buf); } diff --git a/core/io/marshalls.h b/core/io/marshalls.h index 381e4e3d20..1284520945 100644 --- a/core/io/marshalls.h +++ b/core/io/marshalls.h @@ -31,10 +31,10 @@ #ifndef MARSHALLS_H #define MARSHALLS_H -#include "typedefs.h" +#include "core/reference.h" +#include "core/typedefs.h" +#include "core/variant.h" -#include "reference.h" -#include "variant.h" /** * Miscellaneous helpers for marshalling data types, and encoding * in an endian independent way diff --git a/core/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp index 8e67f1c97a..b3f0a76a80 100644 --- a/core/io/multiplayer_api.cpp +++ b/core/io/multiplayer_api.cpp @@ -28,7 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "core/io/multiplayer_api.h" +#include "multiplayer_api.h" + #include "core/io/marshalls.h" #include "scene/main/node.h" @@ -37,24 +38,26 @@ _FORCE_INLINE_ bool _should_call_local(MultiplayerAPI::RPCMode mode, bool is_mas switch (mode) { case MultiplayerAPI::RPC_MODE_DISABLED: { - //do nothing + // Do nothing. } break; case MultiplayerAPI::RPC_MODE_REMOTE: { - //do nothing also, no need to call local + // Do nothing also. Remote cannot produce a local call. } break; + case MultiplayerAPI::RPC_MODE_MASTERSYNC: { + if (is_master) + r_skip_rpc = true; // I am the master, so skip remote call. + } // Do not break, fall over to other sync. case MultiplayerAPI::RPC_MODE_REMOTESYNC: - case MultiplayerAPI::RPC_MODE_MASTERSYNC: - case MultiplayerAPI::RPC_MODE_SLAVESYNC: - case MultiplayerAPI::RPC_MODE_SYNC: { - //call it, sync always results in call + case MultiplayerAPI::RPC_MODE_PUPPETSYNC: { + // Call it, sync always results in a local call. return true; } break; case MultiplayerAPI::RPC_MODE_MASTER: { if (is_master) - r_skip_rpc = true; //no other master so.. + r_skip_rpc = true; // I am the master, so skip remote call. return is_master; } break; - case MultiplayerAPI::RPC_MODE_SLAVE: { + case MultiplayerAPI::RPC_MODE_PUPPET: { return !is_master; } break; } @@ -67,19 +70,16 @@ _FORCE_INLINE_ bool _can_call_mode(Node *p_node, MultiplayerAPI::RPCMode mode, i case MultiplayerAPI::RPC_MODE_DISABLED: { return false; } break; - case MultiplayerAPI::RPC_MODE_REMOTE: { - return true; - } break; - case MultiplayerAPI::RPC_MODE_REMOTESYNC: - case MultiplayerAPI::RPC_MODE_SYNC: { + case MultiplayerAPI::RPC_MODE_REMOTE: + case MultiplayerAPI::RPC_MODE_REMOTESYNC: { return true; } break; case MultiplayerAPI::RPC_MODE_MASTERSYNC: case MultiplayerAPI::RPC_MODE_MASTER: { return p_node->is_network_master(); } break; - case MultiplayerAPI::RPC_MODE_SLAVESYNC: - case MultiplayerAPI::RPC_MODE_SLAVE: { + case MultiplayerAPI::RPC_MODE_PUPPETSYNC: + case MultiplayerAPI::RPC_MODE_PUPPET: { return !p_node->is_network_master() && p_remote_id == p_node->get_network_master(); } break; } @@ -94,7 +94,7 @@ void MultiplayerAPI::poll() { network_peer->poll(); - if (!network_peer.is_valid()) //it's possible that polling might have resulted in a disconnection, so check here + if (!network_peer.is_valid()) // It's possible that polling might have resulted in a disconnection, so check here. return; while (network_peer->get_available_packet_count()) { @@ -113,7 +113,7 @@ void MultiplayerAPI::poll() { rpc_sender_id = 0; if (!network_peer.is_valid()) { - break; //it's also possible that a packet or RPC caused a disconnection, so also check here + break; // It's also possible that a packet or RPC caused a disconnection, so also check here. } } } @@ -160,7 +160,9 @@ Ref<NetworkedMultiplayerPeer> MultiplayerAPI::get_network_peer() const { void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len) { + ERR_EXPLAIN("Multiplayer root node was not initialized. If you are using custom multiplayer, remember to set the root node via MultiplayerAPI.set_root_node before using it"); ERR_FAIL_COND(root_node == NULL); + ERR_EXPLAIN("Invalid packet received. Size too small."); ERR_FAIL_COND(p_packet_len < 1); uint8_t packet_type = p_packet[0]; @@ -180,13 +182,15 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_ case NETWORK_COMMAND_REMOTE_CALL: case NETWORK_COMMAND_REMOTE_SET: { + ERR_EXPLAIN("Invalid packet received. Size too small."); ERR_FAIL_COND(p_packet_len < 6); Node *node = _process_get_node(p_from, p_packet, p_packet_len); + ERR_EXPLAIN("Invalid packet received. Requested node was not found."); ERR_FAIL_COND(node == NULL); - //detect cstring end + // Detect cstring end. int len_end = 5; for (; len_end < p_packet_len; len_end++) { if (p_packet[len_end] == 0) { @@ -194,6 +198,7 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_ } } + ERR_EXPLAIN("Invalid packet received. Size too small."); ERR_FAIL_COND(len_end >= p_packet_len); StringName name = String::utf8((const char *)&p_packet[5]); @@ -222,9 +227,11 @@ Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, int Node *node = NULL; if (target & 0x80000000) { - //use full path (not cached yet) + // Use full path (not cached yet). int ofs = target & 0x7FFFFFFF; + + ERR_EXPLAIN("Invalid packet received. Size smaller than declared."); ERR_FAIL_COND_V(ofs >= p_packet_len, NULL); String paths; @@ -237,17 +244,19 @@ Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, int if (!node) ERR_PRINTS("Failed to get path from RPC: " + String(np)); } else { - //use cached path + // Use cached path. int id = target; Map<int, PathGetCache>::Element *E = path_get_cache.find(p_from); + ERR_EXPLAIN("Invalid packet received. Requests invalid peer cache."); ERR_FAIL_COND_V(!E, NULL); Map<int, PathGetCache::NodeInfo>::Element *F = E->get().nodes.find(id); + ERR_EXPLAIN("Invalid packet received. Unabled to find requested cached node."); ERR_FAIL_COND_V(!F, NULL); PathGetCache::NodeInfo *ni = &F->get(); - //do proper caching later + // Do proper caching later. node = root_node->get_node(ni->path); if (!node) @@ -258,9 +267,10 @@ Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, int void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) { + ERR_EXPLAIN("Invalid packet received. Size too small."); ERR_FAIL_COND(p_offset >= p_packet_len); - // Check that remote can call the RPC on this node + // Check that remote can call the RPC on this node. RPCMode rpc_mode = RPC_MODE_DISABLED; const Map<StringName, RPCMode>::Element *E = p_node->get_node_rpc_mode(p_name); if (E) { @@ -268,6 +278,8 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_ } else if (p_node->get_script_instance()) { rpc_mode = p_node->get_script_instance()->get_rpc_mode(p_name); } + + ERR_EXPLAIN("RPC '" + String(p_name) + "' is not allowed from: " + itos(p_from) + ". Mode is " + itos((int)rpc_mode) + ", master is " + itos(p_node->get_network_master()) + "."); ERR_FAIL_COND(!_can_call_mode(p_node, rpc_mode, p_from)); int argc = p_packet[p_offset]; @@ -280,11 +292,14 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_ for (int i = 0; i < argc; i++) { + ERR_EXPLAIN("Invalid packet received. Size too small."); ERR_FAIL_COND(p_offset >= p_packet_len); + int vlen; Error err = decode_variant(args.write[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen); + ERR_EXPLAIN("Invalid packet received. Unable to decode RPC argument."); ERR_FAIL_COND(err != OK); - //args[i]=p_packet[3+i]; + argp.write[i] = &args[i]; p_offset += vlen; } @@ -301,9 +316,10 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_ void MultiplayerAPI::_process_rset(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) { + ERR_EXPLAIN("Invalid packet received. Size too small."); ERR_FAIL_COND(p_offset >= p_packet_len); - // Check that remote can call the RSET on this node + // Check that remote can call the RSET on this node. RPCMode rset_mode = RPC_MODE_DISABLED; const Map<StringName, RPCMode>::Element *E = p_node->get_node_rset_mode(p_name); if (E) { @@ -311,10 +327,15 @@ void MultiplayerAPI::_process_rset(Node *p_node, const StringName &p_name, int p } else if (p_node->get_script_instance()) { rset_mode = p_node->get_script_instance()->get_rset_mode(p_name); } + + ERR_EXPLAIN("RSET '" + String(p_name) + "' is not allowed from: " + itos(p_from) + ". Mode is " + itos((int)rset_mode) + ", master is " + itos(p_node->get_network_master()) + "."); ERR_FAIL_COND(!_can_call_mode(p_node, rset_mode, p_from)); Variant value; - decode_variant(value, &p_packet[p_offset], p_packet_len - p_offset); + Error err = decode_variant(value, &p_packet[p_offset], p_packet_len - p_offset); + + ERR_EXPLAIN("Invalid packet received. Unable to decode RSET value."); + ERR_FAIL_COND(err != OK); bool valid; @@ -327,6 +348,7 @@ void MultiplayerAPI::_process_rset(Node *p_node, const StringName &p_name, int p void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) { + ERR_EXPLAIN("Invalid packet received. Size too small."); ERR_FAIL_COND(p_packet_len < 5); int id = decode_uint32(&p_packet[1]); @@ -345,9 +367,7 @@ void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, path_get_cache[p_from].nodes[id] = ni; - //send ack - - //encode path + // Encode path to send ack. CharString pname = String(path).utf8(); int len = encode_cstring(pname.get_data(), NULL); @@ -364,6 +384,7 @@ void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) { + ERR_EXPLAIN("Invalid packet received. Size too small."); ERR_FAIL_COND(p_packet_len < 2); String paths; @@ -372,31 +393,33 @@ void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet, NodePath path = paths; PathSentCache *psc = path_send_cache.getptr(path); + ERR_EXPLAIN("Invalid packet received. Tries to confirm a path which was not found in cache."); ERR_FAIL_COND(!psc); Map<int, bool>::Element *E = psc->confirmed_peers.find(p_from); + ERR_EXPLAIN("Invalid packet received. Source peer was not found in cache for the given path."); ERR_FAIL_COND(!E); E->get() = true; } bool MultiplayerAPI::_send_confirm_path(NodePath p_path, PathSentCache *psc, int p_target) { bool has_all_peers = true; - List<int> peers_to_add; //if one is missing, take note to add it + List<int> peers_to_add; // If one is missing, take note to add it. for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) { if (p_target < 0 && E->get() == -p_target) - continue; //continue, excluded + continue; // Continue, excluded. if (p_target > 0 && E->get() != p_target) - continue; //continue, not for this peer + continue; // Continue, not for this peer. Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get()); - if (!F || F->get() == false) { - //path was not cached, or was cached but is unconfirmed + if (!F || !F->get()) { + // Path was not cached, or was cached but is unconfirmed. if (!F) { - //not cached at all, take note + // Not cached at all, take note. peers_to_add.push_back(E->get()); } @@ -404,11 +427,11 @@ bool MultiplayerAPI::_send_confirm_path(NodePath p_path, PathSentCache *psc, int } } - //those that need to be added, send a message for this + // Those that need to be added, send a message for this. for (List<int>::Element *E = peers_to_add.front(); E; E = E->next()) { - //encode function name + // Encode function name. CharString pname = String(p_path).utf8(); int len = encode_cstring(pname.get_data(), NULL); @@ -419,11 +442,11 @@ bool MultiplayerAPI::_send_confirm_path(NodePath p_path, PathSentCache *psc, int 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_target_peer(E->get()); // To all of you. network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); network_peer->put_packet(packet.ptr(), packet.size()); - psc->confirmed_peers.insert(E->get(), false); //insert into confirmed, but as false since it was not confirmed + psc->confirmed_peers.insert(E->get(), false); // Insert into confirmed, but as false since it was not confirmed. } return has_all_peers; @@ -462,35 +485,36 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p } NodePath from_path = (root_node->get_path()).rel_path_to(p_from->get_path()); + ERR_EXPLAIN("Unable to send RPC. Relative path is empty. THIS IS LIKELY A BUG IN THE ENGINE!"); ERR_FAIL_COND(from_path.is_empty()); - //see if the path is cached + // See if the path is cached. PathSentCache *psc = path_send_cache.getptr(from_path); if (!psc) { - //path is not cached, create + // Path is not cached, create. path_send_cache[from_path] = PathSentCache(); psc = path_send_cache.getptr(from_path); psc->id = last_send_cache_id++; } - //create base packet, lots of hardcode because it must be tight + // Create base packet, lots of hardcode because it must be tight. int ofs = 0; #define MAKE_ROOM(m_amount) \ if (packet_cache.size() < m_amount) packet_cache.resize(m_amount); - //encode type + // Encode type. MAKE_ROOM(1); packet_cache.write[0] = p_set ? NETWORK_COMMAND_REMOTE_SET : NETWORK_COMMAND_REMOTE_CALL; ofs += 1; - //encode ID + // Encode ID. MAKE_ROOM(ofs + 4); encode_uint32(psc->id, &(packet_cache.write[ofs])); ofs += 4; - //encode function name + // Encode function name. CharString name = String(p_name).utf8(); int len = encode_cstring(name.get_data(), NULL); MAKE_ROOM(ofs + len); @@ -498,20 +522,22 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p ofs += len; if (p_set) { - //set argument + // Set argument. Error err = encode_variant(*p_arg[0], NULL, len); + ERR_EXPLAIN("Unable to encode RSET value. THIS IS LIKELY A BUG IN THE ENGINE!"); ERR_FAIL_COND(err != OK); MAKE_ROOM(ofs + len); encode_variant(*p_arg[0], &(packet_cache.write[ofs]), len); ofs += len; } else { - //call arguments + // Call arguments. MAKE_ROOM(ofs + 1); 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_EXPLAIN("Unable to encode RPC argument. THIS IS LIKELY A BUG IN THE ENGINE!"); ERR_FAIL_COND(err != OK); MAKE_ROOM(ofs + len); encode_variant(*p_arg[i], &(packet_cache.write[ofs]), len); @@ -519,21 +545,21 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p } } - //see if all peers have cached path (is so, call can be fast) + // See if all peers have cached path (is so, call can be fast). bool has_all_peers = _send_confirm_path(from_path, psc, p_to); - //take chance and set transfer mode, since all send methods will use it + // Take chance and set transfer mode, since all send methods will use it. network_peer->set_transfer_mode(p_unreliable ? NetworkedMultiplayerPeer::TRANSFER_MODE_UNRELIABLE : NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); if (has_all_peers) { - //they all have verified paths, so send fast - network_peer->set_target_peer(p_to); //to all of you - network_peer->put_packet(packet_cache.ptr(), ofs); //a message with love + // They all have verified paths, so send fast. + network_peer->set_target_peer(p_to); // To all of you. + network_peer->put_packet(packet_cache.ptr(), ofs); // A message with love. } else { - //not all verified path, so send one by one + // Not all verified path, so send one by one. - //apend path at the end, since we will need it for some packets + // Append path at the end, since we will need it for some packets. CharString pname = String(from_path).utf8(); int path_len = encode_cstring(pname.get_data(), NULL); MAKE_ROOM(ofs + path_len); @@ -542,23 +568,23 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) { if (p_to < 0 && E->get() == -p_to) - continue; //continue, excluded + continue; // Continue, excluded. if (p_to > 0 && E->get() != p_to) - continue; //continue, not for this peer + continue; // Continue, not for this peer. Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get()); - ERR_CONTINUE(!F); //should never happen + ERR_CONTINUE(!F); // Should never happen. - network_peer->set_target_peer(E->get()); //to this one specifically + network_peer->set_target_peer(E->get()); // To this one specifically. - if (F->get() == true) { - //this one confirmed path, so use id + if (F->get()) { + // This one confirmed path, so use id. 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.write[1])); //offset to path and flag + // This one did not confirm path yet, so use entire path (sorry!). + encode_uint32(0x80000000 | ofs, &(packet_cache.write[1])); // Offset to path and flag. network_peer->put_packet(packet_cache.ptr(), ofs + path_len); } } @@ -573,7 +599,7 @@ void MultiplayerAPI::_add_peer(int p_id) { void MultiplayerAPI::_del_peer(int p_id) { connected_peers.erase(p_id); - path_get_cache.erase(p_id); //I no longer need your cache, sorry + path_get_cache.erase(p_id); // I no longer need your cache, sorry. emit_signal("network_peer_disconnected", p_id); } @@ -594,8 +620,12 @@ void MultiplayerAPI::_server_disconnected() { void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount) { - ERR_FAIL_COND(!p_node->is_inside_tree()); + ERR_EXPLAIN("Trying to call an RPC while no network peer is active."); ERR_FAIL_COND(!network_peer.is_valid()); + ERR_EXPLAIN("Trying to call an RPC on a node which is not inside SceneTree."); + ERR_FAIL_COND(!p_node->is_inside_tree()); + ERR_EXPLAIN("Trying to call an RPC via a network peer which is not connected."); + ERR_FAIL_COND(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED); int node_id = network_peer->get_unique_id(); bool skip_rpc = false; @@ -604,7 +634,7 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const bool is_master = p_node->is_network_master(); if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) { - //check that send mode can use local call + // Check that send mode can use local call. const Map<StringName, RPCMode>::Element *E = p_node->get_node_rpc_mode(p_method); if (E) { @@ -612,9 +642,9 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const } if (call_local_native) { - // done below + // Done below. } else if (p_node->get_script_instance()) { - //attempt with script + // Attempt with script. RPCMode rpc_mode = p_node->get_script_instance()->get_rpc_mode(p_method); call_local_script = _should_call_local(rpc_mode, is_master, skip_rpc); } @@ -650,15 +680,19 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value) { - ERR_FAIL_COND(!p_node->is_inside_tree()); + ERR_EXPLAIN("Trying to RSET while no network peer is active."); ERR_FAIL_COND(!network_peer.is_valid()); + ERR_EXPLAIN("Trying to RSET on a node which is not inside SceneTree."); + ERR_FAIL_COND(!p_node->is_inside_tree()); + ERR_EXPLAIN("Trying to send an RSET via a network peer which is not connected."); + ERR_FAIL_COND(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED); int node_id = network_peer->get_unique_id(); bool is_master = p_node->is_network_master(); bool skip_rset = false; if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) { - //check that send mode can use local call + // Check that send mode can use local call. bool set_local = false; @@ -678,7 +712,7 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const return; } } else if (p_node->get_script_instance()) { - //attempt with script + // Attempt with script. RPCMode rpc_mode = p_node->get_script_instance()->get_rset_mode(p_property); set_local = _should_call_local(rpc_mode, is_master, skip_rset); @@ -706,8 +740,11 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const Error MultiplayerAPI::send_bytes(PoolVector<uint8_t> p_data, int p_to, NetworkedMultiplayerPeer::TransferMode p_mode) { + ERR_EXPLAIN("Trying to send an empty raw packet."); ERR_FAIL_COND_V(p_data.size() < 1, ERR_INVALID_DATA); + ERR_EXPLAIN("Trying to send a raw packet while no network peer is active."); ERR_FAIL_COND_V(!network_peer.is_valid(), ERR_UNCONFIGURED); + ERR_EXPLAIN("Trying to send a raw packet via a network peer which is not connected."); ERR_FAIL_COND_V(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED, ERR_UNCONFIGURED); MAKE_ROOM(p_data.size() + 1); @@ -723,6 +760,7 @@ Error MultiplayerAPI::send_bytes(PoolVector<uint8_t> p_data, int p_to, Networked void MultiplayerAPI::_process_raw(int p_from, const uint8_t *p_packet, int p_packet_len) { + ERR_EXPLAIN("Invalid packet received. Size too small."); ERR_FAIL_COND(p_packet_len < 2); PoolVector<uint8_t> out; @@ -737,30 +775,36 @@ void MultiplayerAPI::_process_raw(int p_from, const uint8_t *p_packet, int p_pac int MultiplayerAPI::get_network_unique_id() const { + ERR_EXPLAIN("No network peer is assigned. Unable to get unique network ID."); ERR_FAIL_COND_V(!network_peer.is_valid(), 0); return network_peer->get_unique_id(); } bool MultiplayerAPI::is_network_server() const { + // XXX Maybe fail silently? Maybe should actually return true to make development of both local and online multiplayer easier? + ERR_EXPLAIN("No network peer is assigned. I can't be a server."); ERR_FAIL_COND_V(!network_peer.is_valid(), false); return network_peer->is_server(); } void MultiplayerAPI::set_refuse_new_network_connections(bool p_refuse) { + ERR_EXPLAIN("No network peer is assigned. Unable to set 'refuse_new_connections'."); ERR_FAIL_COND(!network_peer.is_valid()); network_peer->set_refuse_new_connections(p_refuse); } bool MultiplayerAPI::is_refusing_new_network_connections() const { + ERR_EXPLAIN("No network peer is assigned. Unable to get 'refuse_new_connections'."); ERR_FAIL_COND_V(!network_peer.is_valid(), false); return network_peer->is_refusing_new_connections(); } Vector<int> MultiplayerAPI::get_network_connected_peers() const { + ERR_EXPLAIN("No network peer is assigned. Assume no peers are connected."); ERR_FAIL_COND_V(!network_peer.is_valid(), Vector<int>()); Vector<int> ret; @@ -803,12 +847,13 @@ void MultiplayerAPI::_bind_methods() { BIND_ENUM_CONSTANT(RPC_MODE_DISABLED); BIND_ENUM_CONSTANT(RPC_MODE_REMOTE); - BIND_ENUM_CONSTANT(RPC_MODE_SYNC); BIND_ENUM_CONSTANT(RPC_MODE_MASTER); - BIND_ENUM_CONSTANT(RPC_MODE_SLAVE); + BIND_ENUM_CONSTANT(RPC_MODE_PUPPET); + BIND_ENUM_CONSTANT(RPC_MODE_SLAVE); // Deprecated. BIND_ENUM_CONSTANT(RPC_MODE_REMOTESYNC); + BIND_ENUM_CONSTANT(RPC_MODE_SYNC); // Deprecated. BIND_ENUM_CONSTANT(RPC_MODE_MASTERSYNC); - BIND_ENUM_CONSTANT(RPC_MODE_SLAVESYNC); + BIND_ENUM_CONSTANT(RPC_MODE_PUPPETSYNC); } MultiplayerAPI::MultiplayerAPI() { diff --git a/core/io/multiplayer_api.h b/core/io/multiplayer_api.h index e47b1830e8..c86e76e91a 100644 --- a/core/io/multiplayer_api.h +++ b/core/io/multiplayer_api.h @@ -91,12 +91,13 @@ public: RPC_MODE_DISABLED, // No rpc for this method, calls to this will be blocked (default) RPC_MODE_REMOTE, // Using rpc() on it will call method / set property in all remote peers - RPC_MODE_SYNC, // Using rpc() on it will call method / set property in all remote peers and locally RPC_MODE_MASTER, // Using rpc() on it will call method on wherever the master is, be it local or remote - RPC_MODE_SLAVE, // Using rpc() on it will call method for all slaves - RPC_MODE_REMOTESYNC, // Same as RPC_MODE_SYNC, compatibility + RPC_MODE_PUPPET, // Using rpc() on it will call method for all puppets + RPC_MODE_SLAVE = RPC_MODE_PUPPET, // Deprecated, same as puppet + RPC_MODE_REMOTESYNC, // Using rpc() on it will call method / set property in all remote peers and locally + RPC_MODE_SYNC = RPC_MODE_REMOTESYNC, // Deprecated. Same as RPC_MODE_REMOTESYNC RPC_MODE_MASTERSYNC, // Using rpc() on it will call method / set property in the master peer and locally - RPC_MODE_SLAVESYNC, // Using rpc() on it will call method / set property in all slave peers and locally + RPC_MODE_PUPPETSYNC, // Using rpc() on it will call method / set property in all puppets peers and locally }; void poll(); diff --git a/editor/editor_initialize_ssl.cpp b/core/io/net_socket.cpp index aedbfb7bd7..10bcf62eda 100644 --- a/editor/editor_initialize_ssl.cpp +++ b/core/io/net_socket.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* editor_initialize_ssl.cpp */ +/* net_socket.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,21 +28,15 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "editor_initialize_ssl.h" +#include "net_socket.h" -#include "certs_compressed.gen.h" -#include "io/compression.h" -#include "io/stream_peer_ssl.h" +NetSocket *(*NetSocket::_create)() = NULL; -void editor_initialize_certificates() { +NetSocket *NetSocket::create() { - PoolByteArray data; - data.resize(_certs_uncompressed_size + 1); - { - PoolByteArray::Write w = data.write(); - Compression::decompress(w.ptr(), _certs_uncompressed_size, _certs_compressed, _certs_compressed_size, Compression::MODE_DEFLATE); - w[_certs_uncompressed_size] = 0; //make sure it ends at zero - } + if (_create) + return _create(); - StreamPeerSSL::load_certs_from_memory(data); + ERR_PRINT("Unable to create network socket, platform not supported"); + return NULL; } diff --git a/drivers/unix/stream_peer_tcp_posix.h b/core/io/net_socket.h index bcebe57771..0665bec9fd 100644 --- a/drivers/unix/stream_peer_tcp_posix.h +++ b/core/io/net_socket.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* stream_peer_tcp_posix.h */ +/* net_socket.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,63 +28,52 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef UNIX_ENABLED +#ifndef NET_SOCKET_H +#define NET_SOCKET_H -#ifndef STREAM_PEER_TCP_POSIX_H -#define STREAM_PEER_TCP_POSIX_H +#include "core/io/ip.h" +#include "core/reference.h" -#include "core/io/ip_address.h" -#include "core/io/stream_peer_tcp.h" -#include "error_list.h" - -class StreamPeerTCPPosix : public StreamPeerTCP { +class NetSocket : public Reference { protected: - mutable Status status; - - IP::Type sock_type; - int sockfd; - - Error _block(int p_sockfd, bool p_read, bool p_write) const; - - Error _poll_connection() const; - - IP_Address peer_host; - int peer_port; - - Error write(const uint8_t *p_data, int p_bytes, int &r_sent, bool p_block); - Error read(uint8_t *p_buffer, int p_bytes, int &r_received, bool p_block); - - static StreamPeerTCP *_create(); + static NetSocket *(*_create)(); public: - virtual Error connect_to_host(const IP_Address &p_host, uint16_t p_port); - - virtual Error put_data(const uint8_t *p_data, int p_bytes); - virtual Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent); - - virtual Error get_data(uint8_t *p_buffer, int p_bytes); - virtual Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received); - - virtual int get_available_bytes() const; - - void set_socket(int p_sockfd, IP_Address p_host, int p_port, IP::Type p_sock_type); - - virtual IP_Address get_connected_host() const; - virtual uint16_t get_connected_port() const; - - virtual bool is_connected_to_host() const; - virtual Status get_status() const; - virtual void disconnect_from_host(); - - virtual void set_no_delay(bool p_enabled); - - static void make_default(); - - StreamPeerTCPPosix(); - ~StreamPeerTCPPosix(); + static NetSocket *create(); + + enum PollType { + POLL_TYPE_IN, + POLL_TYPE_OUT, + POLL_TYPE_IN_OUT + }; + + enum Type { + TYPE_NONE, + TYPE_TCP, + TYPE_UDP, + }; + + virtual Error open(Type p_type, IP::Type &ip_type) = 0; + virtual void close() = 0; + virtual Error bind(IP_Address p_addr, uint16_t p_port) = 0; + virtual Error listen(int p_max_pending) = 0; + virtual Error connect_to_host(IP_Address p_addr, uint16_t p_port) = 0; + virtual Error poll(PollType p_type, int timeout) const = 0; + virtual Error recv(uint8_t *p_buffer, int p_len, int &r_read) = 0; + virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IP_Address &r_ip, uint16_t &r_port) = 0; + virtual Error send(const uint8_t *p_buffer, int p_len, int &r_sent) = 0; + virtual Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IP_Address p_ip, uint16_t p_port) = 0; + virtual Ref<NetSocket> accept(IP_Address &r_ip, uint16_t &r_port) = 0; + + virtual bool is_open() const = 0; + virtual int get_available_bytes() const = 0; + + virtual void set_broadcasting_enabled(bool p_enabled) = 0; + virtual void set_blocking_enabled(bool p_enabled) = 0; + virtual void set_ipv6_only_enabled(bool p_enabled) = 0; + virtual void set_tcp_no_delay_enabled(bool p_enabled) = 0; + virtual void set_reuse_address_enabled(bool p_enabled) = 0; }; -#endif // TCP_CLIENT_POSIX_H - -#endif +#endif // NET_SOCKET_H diff --git a/core/io/networked_multiplayer_peer.h b/core/io/networked_multiplayer_peer.h index 66089c27b9..8e83c528a0 100644 --- a/core/io/networked_multiplayer_peer.h +++ b/core/io/networked_multiplayer_peer.h @@ -31,7 +31,7 @@ #ifndef NETWORKED_MULTIPLAYER_PEER_H #define NETWORKED_MULTIPLAYER_PEER_H -#include "io/packet_peer.h" +#include "core/io/packet_peer.h" class NetworkedMultiplayerPeer : public PacketPeer { diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp index dc4997dfc2..b6dd4eaf6f 100644 --- a/core/io/packet_peer.cpp +++ b/core/io/packet_peer.cpp @@ -30,8 +30,9 @@ #include "packet_peer.h" -#include "io/marshalls.h" -#include "project_settings.h" +#include "core/io/marshalls.h" +#include "core/project_settings.h" + /* helpers / binders */ PacketPeer::PacketPeer() { diff --git a/core/io/packet_peer.h b/core/io/packet_peer.h index b10152e96b..a6559df460 100644 --- a/core/io/packet_peer.h +++ b/core/io/packet_peer.h @@ -31,9 +31,10 @@ #ifndef PACKET_PEER_H #define PACKET_PEER_H -#include "io/stream_peer.h" -#include "object.h" -#include "ring_buffer.h" +#include "core/io/stream_peer.h" +#include "core/object.h" +#include "core/ring_buffer.h" + class PacketPeer : public Reference { GDCLASS(PacketPeer, Reference); diff --git a/core/io/packet_peer_udp.cpp b/core/io/packet_peer_udp.cpp index bfbea15582..d33ba6f855 100644 --- a/core/io/packet_peer_udp.cpp +++ b/core/io/packet_peer_udp.cpp @@ -29,9 +29,8 @@ /*************************************************************************/ #include "packet_peer_udp.h" -#include "io/ip.h" -PacketPeerUDP *(*PacketPeerUDP::_create)() = NULL; +#include "core/io/ip.h" void PacketPeerUDP::set_blocking_mode(bool p_enable) { @@ -58,6 +57,177 @@ Error PacketPeerUDP::_set_dest_address(const String &p_address, int p_port) { return OK; } +int PacketPeerUDP::get_available_packet_count() const { + + // TODO we should deprecate this, and expose poll instead! + Error err = const_cast<PacketPeerUDP *>(this)->_poll(); + if (err != OK) + return -1; + + return queue_count; +} + +Error PacketPeerUDP::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { + + Error err = _poll(); + if (err != OK) + return err; + if (queue_count == 0) + return ERR_UNAVAILABLE; + + uint32_t size = 0; + uint8_t ipv6[16]; + rb.read(ipv6, 16, true); + packet_ip.set_ipv6(ipv6); + rb.read((uint8_t *)&packet_port, 4, true); + rb.read((uint8_t *)&size, 4, true); + rb.read(packet_buffer, size, true); + --queue_count; + *r_buffer = packet_buffer; + r_buffer_size = size; + return OK; +} + +Error PacketPeerUDP::put_packet(const uint8_t *p_buffer, int p_buffer_size) { + + ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); + ERR_FAIL_COND_V(!peer_addr.is_valid(), ERR_UNCONFIGURED); + + Error err; + int sent = -1; + + if (!_sock->is_open()) { + IP::Type ip_type = peer_addr.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; + err = _sock->open(NetSocket::TYPE_UDP, ip_type); + ERR_FAIL_COND_V(err != OK, err); + _sock->set_blocking_enabled(false); + } + + do { + err = _sock->sendto(p_buffer, p_buffer_size, sent, peer_addr, peer_port); + if (err != OK) { + if (err != ERR_BUSY) + return FAILED; + else if (!blocking) + return ERR_BUSY; + // Keep trying to send full packet + continue; + } + return OK; + + } while (sent != p_buffer_size); + + return OK; +} + +int PacketPeerUDP::get_max_packet_size() const { + + return 512; // uhm maybe not +} + +Error PacketPeerUDP::listen(int p_port, const IP_Address &p_bind_address, int p_recv_buffer_size) { + + ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); + ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE); + ERR_FAIL_COND_V(!p_bind_address.is_valid() && !p_bind_address.is_wildcard(), ERR_INVALID_PARAMETER); + + Error err; + IP::Type ip_type = IP::TYPE_ANY; + + if (p_bind_address.is_valid()) + ip_type = p_bind_address.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; + + err = _sock->open(NetSocket::TYPE_UDP, ip_type); + + if (err != OK) + return ERR_CANT_CREATE; + + _sock->set_blocking_enabled(false); + _sock->set_reuse_address_enabled(true); + err = _sock->bind(p_bind_address, p_port); + + if (err != OK) { + _sock->close(); + return err; + } + rb.resize(nearest_shift(p_recv_buffer_size)); + return OK; +} + +void PacketPeerUDP::close() { + + if (_sock.is_valid()) + _sock->close(); + rb.resize(16); + queue_count = 0; +} + +Error PacketPeerUDP::wait() { + + ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); + return _sock->poll(NetSocket::POLL_TYPE_IN, -1); +} + +Error PacketPeerUDP::_poll() { + + ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); + + if (!_sock->is_open()) { + return FAILED; + } + + Error err; + int read; + IP_Address ip; + uint16_t port; + + while (true) { + err = _sock->recvfrom(recv_buffer, sizeof(recv_buffer), read, ip, port); + + if (err != OK) { + if (err == ERR_BUSY) + break; + return FAILED; + } + + if (rb.space_left() < read + 24) { +#ifdef TOOLS_ENABLED + WARN_PRINTS("Buffer full, dropping packets!"); +#endif + continue; + } + + uint32_t port32 = port; + rb.write(ip.get_ipv6(), 16); + rb.write((uint8_t *)&port32, 4); + rb.write((uint8_t *)&read, 4); + rb.write(recv_buffer, read); + ++queue_count; + } + + return OK; +} +bool PacketPeerUDP::is_listening() const { + + return _sock.is_valid() && _sock->is_open(); +} + +IP_Address PacketPeerUDP::get_packet_address() const { + + return packet_ip; +} + +int PacketPeerUDP::get_packet_port() const { + + return packet_port; +} + +void PacketPeerUDP::set_dest_address(const IP_Address &p_address, int p_port) { + + peer_addr = p_address; + peer_port = p_port; +} + void PacketPeerUDP::_bind_methods() { ClassDB::bind_method(D_METHOD("listen", "port", "bind_address", "recv_buf_size"), &PacketPeerUDP::listen, DEFVAL("*"), DEFVAL(65536)); @@ -65,26 +235,21 @@ void PacketPeerUDP::_bind_methods() { ClassDB::bind_method(D_METHOD("wait"), &PacketPeerUDP::wait); ClassDB::bind_method(D_METHOD("is_listening"), &PacketPeerUDP::is_listening); ClassDB::bind_method(D_METHOD("get_packet_ip"), &PacketPeerUDP::_get_packet_ip); - //ClassDB::bind_method(D_METHOD("get_packet_address"),&PacketPeerUDP::_get_packet_address); ClassDB::bind_method(D_METHOD("get_packet_port"), &PacketPeerUDP::get_packet_port); ClassDB::bind_method(D_METHOD("set_dest_address", "host", "port"), &PacketPeerUDP::_set_dest_address); } -Ref<PacketPeerUDP> PacketPeerUDP::create_ref() { - - if (!_create) - return Ref<PacketPeerUDP>(); - return Ref<PacketPeerUDP>(_create()); -} - -PacketPeerUDP *PacketPeerUDP::create() { +PacketPeerUDP::PacketPeerUDP() { - if (!_create) - return NULL; - return _create(); + _sock = Ref<NetSocket>(NetSocket::create()); + blocking = true; + packet_port = 0; + queue_count = 0; + peer_port = 0; + rb.resize(16); } -PacketPeerUDP::PacketPeerUDP() { +PacketPeerUDP::~PacketPeerUDP() { - blocking = true; + close(); } diff --git a/core/io/packet_peer_udp.h b/core/io/packet_peer_udp.h index 035f4ad1c9..4366b0eb82 100644 --- a/core/io/packet_peer_udp.h +++ b/core/io/packet_peer_udp.h @@ -31,37 +31,55 @@ #ifndef PACKET_PEER_UDP_H #define PACKET_PEER_UDP_H -#include "io/ip.h" -#include "io/packet_peer.h" +#include "core/io/ip.h" +#include "core/io/net_socket.h" +#include "core/io/packet_peer.h" class PacketPeerUDP : public PacketPeer { GDCLASS(PacketPeerUDP, PacketPeer); protected: + enum { + PACKET_BUFFER_SIZE = 65536 + }; + + RingBuffer<uint8_t> rb; + uint8_t recv_buffer[PACKET_BUFFER_SIZE]; + uint8_t packet_buffer[PACKET_BUFFER_SIZE]; + IP_Address packet_ip; + int packet_port; + int queue_count; + + IP_Address peer_addr; + int peer_port; bool blocking; + Ref<NetSocket> _sock; - static PacketPeerUDP *(*_create)(); static void _bind_methods(); String _get_packet_ip() const; Error _set_dest_address(const String &p_address, int p_port); + Error _poll(); public: void set_blocking_mode(bool p_enable); - virtual Error listen(int p_port, const IP_Address &p_bind_address = IP_Address("*"), int p_recv_buffer_size = 65536) = 0; - virtual void close() = 0; - virtual Error wait() = 0; - virtual bool is_listening() const = 0; - virtual IP_Address get_packet_address() const = 0; - virtual int get_packet_port() const = 0; - virtual void set_dest_address(const IP_Address &p_address, int p_port) = 0; + Error listen(int p_port, const IP_Address &p_bind_address = IP_Address("*"), int p_recv_buffer_size = 65536); + void close(); + Error wait(); + bool is_listening() const; + IP_Address get_packet_address() const; + int get_packet_port() const; + void set_dest_address(const IP_Address &p_address, int p_port); - static Ref<PacketPeerUDP> create_ref(); - static PacketPeerUDP *create(); + Error put_packet(const uint8_t *p_buffer, int p_buffer_size); + Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); + int get_available_packet_count() const; + int get_max_packet_size() const; PacketPeerUDP(); + ~PacketPeerUDP(); }; #endif // PACKET_PEER_UDP_H diff --git a/core/io/pck_packer.cpp b/core/io/pck_packer.cpp index 2fd73db27d..3df8c01760 100644 --- a/core/io/pck_packer.cpp +++ b/core/io/pck_packer.cpp @@ -29,8 +29,9 @@ /*************************************************************************/ #include "pck_packer.h" + #include "core/os/file_access.h" -#include "version.h" +#include "core/version.h" static uint64_t _align(uint64_t p_n, int p_alignment) { diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index 02c2c6ce1a..aa73d7bc5c 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -1309,7 +1309,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia case Variant::INT: { int64_t val = p_property; - if (val > 0x7FFFFFFF || val < -0x80000000) { + if (val > 0x7FFFFFFF || val < -(int64_t)0x80000000) { f->store_32(VARIANT_INT64); f->store_64(val); @@ -1718,7 +1718,7 @@ void ResourceFormatSaverBinaryInstance::save_unicode_string(FileAccess *f, const CharString utf8 = p_string.utf8(); if (p_bit_on_len) { - f->store_32(utf8.length() + 1 | 0x80000000); + f->store_32((utf8.length() + 1) | 0x80000000); } else { f->store_32(utf8.length() + 1); } diff --git a/core/io/resource_format_binary.h b/core/io/resource_format_binary.h index 021f7f6a2f..513252055f 100644 --- a/core/io/resource_format_binary.h +++ b/core/io/resource_format_binary.h @@ -31,9 +31,9 @@ #ifndef RESOURCE_FORMAT_BINARY_H #define RESOURCE_FORMAT_BINARY_H -#include "io/resource_loader.h" -#include "io/resource_saver.h" -#include "os/file_access.h" +#include "core/io/resource_loader.h" +#include "core/io/resource_saver.h" +#include "core/os/file_access.h" class ResourceInteractiveLoaderBinary : public ResourceInteractiveLoader { @@ -119,7 +119,6 @@ class ResourceFormatSaverBinaryInstance { bool skip_editor; bool big_endian; bool takeover_paths; - int bin_meta_idx; FileAccess *f; String magic; Set<RES> resource_set; diff --git a/core/io/resource_import.cpp b/core/io/resource_import.cpp index 83e8a40da9..ffea43b6bf 100644 --- a/core/io/resource_import.cpp +++ b/core/io/resource_import.cpp @@ -30,8 +30,8 @@ #include "resource_import.h" -#include "os/os.h" -#include "variant_parser.h" +#include "core/os/os.h" +#include "core/variant_parser.h" Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndType &r_path_and_type, bool *r_valid) const { @@ -78,7 +78,7 @@ Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndTy if (assign != String()) { if (!path_found && assign.begins_with("path.") && r_path_and_type.path == String()) { String feature = assign.get_slicec('.', 1); - if (feature == "fallback" || OS::get_singleton()->has_feature(feature)) { + if (OS::get_singleton()->has_feature(feature)) { r_path_and_type.path = value; path_found = true; //first match must have priority } diff --git a/core/io/resource_import.h b/core/io/resource_import.h index 80e0743eda..53718bd789 100644 --- a/core/io/resource_import.h +++ b/core/io/resource_import.h @@ -31,7 +31,8 @@ #ifndef RESOURCE_IMPORT_H #define RESOURCE_IMPORT_H -#include "io/resource_loader.h" +#include "core/io/resource_loader.h" + class ResourceImporter; class ResourceFormatImporter : public ResourceFormatLoader { diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index c01aff9144..71b01aa94a 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -29,14 +29,16 @@ /*************************************************************************/ #include "resource_loader.h" -#include "io/resource_import.h" -#include "os/file_access.h" -#include "os/os.h" -#include "path_remap.h" -#include "print_string.h" -#include "project_settings.h" -#include "translation.h" -#include "variant_parser.h" + +#include "core/io/resource_import.h" +#include "core/os/file_access.h" +#include "core/os/os.h" +#include "core/path_remap.h" +#include "core/print_string.h" +#include "core/project_settings.h" +#include "core/translation.h" +#include "core/variant_parser.h" + ResourceFormatLoader *ResourceLoader::loader[MAX_LOADERS]; int ResourceLoader::loader_count = 0; @@ -202,12 +204,32 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p else local_path = ProjectSettings::get_singleton()->localize_path(p_path); - if (!p_no_cache && ResourceCache::has(local_path)) { + if (!p_no_cache) { + //lock first if possible + if (ResourceCache::lock) { + ResourceCache::lock->read_lock(); + } - print_verbose("Loading resource: " + local_path + " (cached)"); - if (r_error) - *r_error = OK; - return RES(ResourceCache::get(local_path)); + //get ptr + Resource **rptr = ResourceCache::resources.getptr(local_path); + + if (rptr) { + RES res(*rptr); + //it is possible this resource was just freed in a thread. If so, this referencing will not work and resource is considered not cached + if (res.is_valid()) { + //referencing is fine + if (r_error) + *r_error = OK; + if (ResourceCache::lock) { + ResourceCache::lock->read_unlock(); + } + print_verbose("Loading resource: " + local_path + " (cached)"); + return res; + } + } + if (ResourceCache::lock) { + ResourceCache::lock->read_unlock(); + } } bool xl_remapped = false; @@ -237,6 +259,10 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p } #endif + if (_loaded_callback) { + _loaded_callback(res, p_path); + } + return res; } @@ -613,6 +639,12 @@ void ResourceLoader::clear_path_remaps() { path_remaps.clear(); } +void ResourceLoader::set_load_callback(ResourceLoadedCallback p_callback) { + _loaded_callback = p_callback; +} + +ResourceLoadedCallback ResourceLoader::_loaded_callback = NULL; + ResourceLoadErrorNotify ResourceLoader::err_notify = NULL; void *ResourceLoader::err_notify_ud = NULL; @@ -625,3 +657,5 @@ bool ResourceLoader::timestamp_on_load = false; SelfList<Resource>::List ResourceLoader::remapped_list; HashMap<String, Vector<String> > ResourceLoader::translation_remaps; HashMap<String, String> ResourceLoader::path_remaps; + +ResourceLoaderImport ResourceLoader::import = NULL; diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h index f78464ef0c..7ade4a2dfc 100644 --- a/core/io/resource_loader.h +++ b/core/io/resource_loader.h @@ -31,7 +31,7 @@ #ifndef RESOURCE_LOADER_H #define RESOURCE_LOADER_H -#include "resource.h" +#include "core/resource.h" /** @author Juan Linietsky <reduzio@gmail.com> @@ -77,6 +77,9 @@ public: typedef void (*ResourceLoadErrorNotify)(void *p_ud, const String &p_text); typedef void (*DependencyErrorNotify)(void *p_ud, const String &p_loading, const String &p_which, const String &p_type); +typedef Error (*ResourceLoaderImport)(const String &p_path); +typedef void (*ResourceLoadedCallback)(RES p_resource, const String &p_path); + class ResourceLoader { enum { @@ -104,6 +107,8 @@ class ResourceLoader { //internal load function static RES _load(const String &p_path, const String &p_original_path, const String &p_type_hint, bool p_no_cache, Error *r_error); + static ResourceLoadedCallback _loaded_callback; + 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); @@ -147,6 +152,9 @@ public: static void reload_translation_remaps(); static void load_translation_remaps(); static void clear_translation_remaps(); + + static void set_load_callback(ResourceLoadedCallback p_callback); + static ResourceLoaderImport import; }; #endif diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp index 3dcd94880a..097e81e308 100644 --- a/core/io/resource_saver.cpp +++ b/core/io/resource_saver.cpp @@ -29,10 +29,11 @@ /*************************************************************************/ #include "resource_saver.h" -#include "os/file_access.h" -#include "project_settings.h" -#include "resource_loader.h" -#include "script_language.h" + +#include "core/io/resource_loader.h" +#include "core/os/file_access.h" +#include "core/project_settings.h" +#include "core/script_language.h" ResourceFormatSaver *ResourceSaver::saver[MAX_SAVERS]; @@ -89,7 +90,7 @@ Error ResourceSaver::save(const String &p_path, const RES &p_resource, uint32_t rwcopy->set_path(old_path); if (save_callback && p_path.begins_with("res://")) - save_callback(p_path); + save_callback(p_resource, p_path); return OK; } else { diff --git a/core/io/resource_saver.h b/core/io/resource_saver.h index 396f37d414..6134d9db57 100644 --- a/core/io/resource_saver.h +++ b/core/io/resource_saver.h @@ -31,7 +31,7 @@ #ifndef RESOURCE_SAVER_H #define RESOURCE_SAVER_H -#include "resource.h" +#include "core/resource.h" /** @author Juan Linietsky <reduzio@gmail.com> @@ -46,7 +46,7 @@ public: virtual ~ResourceFormatSaver() {} }; -typedef void (*ResourceSavedCallback)(const String &p_path); +typedef void (*ResourceSavedCallback)(Ref<Resource> p_resource, const String &p_path); class ResourceSaver { diff --git a/core/io/stream_peer.cpp b/core/io/stream_peer.cpp index 3e0ee088c2..3f608b720c 100644 --- a/core/io/stream_peer.cpp +++ b/core/io/stream_peer.cpp @@ -29,7 +29,8 @@ /*************************************************************************/ #include "stream_peer.h" -#include "io/marshalls.h" + +#include "core/io/marshalls.h" Error StreamPeer::_put_data(const PoolVector<uint8_t> &p_data) { @@ -208,6 +209,12 @@ void StreamPeer::put_double(double p_val) { } put_data(buf, 8); } +void StreamPeer::put_string(const String &p_string) { + + CharString cs = p_string.ascii(); + put_u32(cs.length()); + put_data((const uint8_t *)cs.get_data(), cs.length()); +} void StreamPeer::put_utf8_string(const String &p_string) { CharString cs = p_string.utf8(); @@ -324,6 +331,8 @@ double StreamPeer::get_double() { } String StreamPeer::get_string(int p_bytes) { + if (p_bytes < 0) + p_bytes = get_u32(); ERR_FAIL_COND_V(p_bytes < 0, String()); Vector<char> buf; @@ -336,6 +345,8 @@ String StreamPeer::get_string(int p_bytes) { } String StreamPeer::get_utf8_string(int p_bytes) { + if (p_bytes < 0) + p_bytes = get_u32(); ERR_FAIL_COND_V(p_bytes < 0, String()); Vector<uint8_t> buf; @@ -385,6 +396,7 @@ void StreamPeer::_bind_methods() { ClassDB::bind_method(D_METHOD("put_u64", "value"), &StreamPeer::put_u64); ClassDB::bind_method(D_METHOD("put_float", "value"), &StreamPeer::put_float); ClassDB::bind_method(D_METHOD("put_double", "value"), &StreamPeer::put_double); + ClassDB::bind_method(D_METHOD("put_string", "value"), &StreamPeer::put_string); ClassDB::bind_method(D_METHOD("put_utf8_string", "value"), &StreamPeer::put_utf8_string); ClassDB::bind_method(D_METHOD("put_var", "value"), &StreamPeer::put_var); @@ -398,8 +410,8 @@ void StreamPeer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_u64"), &StreamPeer::get_u64); ClassDB::bind_method(D_METHOD("get_float"), &StreamPeer::get_float); ClassDB::bind_method(D_METHOD("get_double"), &StreamPeer::get_double); - ClassDB::bind_method(D_METHOD("get_string", "bytes"), &StreamPeer::get_string); - ClassDB::bind_method(D_METHOD("get_utf8_string", "bytes"), &StreamPeer::get_utf8_string); + ClassDB::bind_method(D_METHOD("get_string", "bytes"), &StreamPeer::get_string, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("get_utf8_string", "bytes"), &StreamPeer::get_utf8_string, DEFVAL(-1)); ClassDB::bind_method(D_METHOD("get_var"), &StreamPeer::get_var); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "big_endian"), "set_big_endian", "is_big_endian_enabled"); diff --git a/core/io/stream_peer.h b/core/io/stream_peer.h index 605b0a7980..f189960cbd 100644 --- a/core/io/stream_peer.h +++ b/core/io/stream_peer.h @@ -31,7 +31,7 @@ #ifndef STREAM_PEER_H #define STREAM_PEER_H -#include "reference.h" +#include "core/reference.h" class StreamPeer : public Reference { GDCLASS(StreamPeer, Reference); @@ -71,6 +71,7 @@ public: void put_u64(uint64_t p_val); void put_float(float p_val); void put_double(double p_val); + void put_string(const String &p_string); void put_utf8_string(const String &p_string); void put_var(const Variant &p_variant); @@ -84,8 +85,8 @@ public: int64_t get_64(); float get_float(); double get_double(); - String get_string(int p_bytes); - String get_utf8_string(int p_bytes); + String get_string(int p_bytes = -1); + String get_utf8_string(int p_bytes = -1); Variant get_var(); StreamPeer() { big_endian = false; } diff --git a/core/io/stream_peer_ssl.cpp b/core/io/stream_peer_ssl.cpp index 25adb6a6ee..138f91301e 100644 --- a/core/io/stream_peer_ssl.cpp +++ b/core/io/stream_peer_ssl.cpp @@ -29,8 +29,11 @@ /*************************************************************************/ #include "stream_peer_ssl.h" -#include "os/file_access.h" -#include "project_settings.h" + +#include "core/io/certs_compressed.gen.h" +#include "core/io/compression.h" +#include "core/os/file_access.h" +#include "core/project_settings.h" StreamPeerSSL *(*StreamPeerSSL::_create)() = NULL; @@ -41,13 +44,20 @@ StreamPeerSSL *StreamPeerSSL::create() { StreamPeerSSL::LoadCertsFromMemory StreamPeerSSL::load_certs_func = NULL; bool StreamPeerSSL::available = false; -bool StreamPeerSSL::initialize_certs = true; void StreamPeerSSL::load_certs_from_memory(const PoolByteArray &p_memory) { if (load_certs_func) load_certs_func(p_memory); } +void StreamPeerSSL::load_certs_from_file(String p_path) { + if (p_path != "") { + PoolByteArray certs = get_cert_file_as_array(p_path); + if (certs.size() > 0) + load_certs_func(certs); + } +} + bool StreamPeerSSL::is_available() { return available; } @@ -60,6 +70,25 @@ bool StreamPeerSSL::is_blocking_handshake_enabled() const { return blocking_handshake; } +PoolByteArray StreamPeerSSL::get_cert_file_as_array(String p_path) { + + PoolByteArray out; + FileAccess *f = FileAccess::open(p_path, FileAccess::READ); + if (f) { + int flen = f->get_len(); + out.resize(flen + 1); + PoolByteArray::Write w = out.write(); + f->get_buffer(w.ptr(), flen); + w[flen] = 0; // Make sure it ends with string terminator + memdelete(f); +#ifdef DEBUG_ENABLED + print_verbose(vformat("Loaded certs from '%s'.", p_path)); +#endif + } + + return out; +} + PoolByteArray StreamPeerSSL::get_project_cert_array() { PoolByteArray out; @@ -67,24 +96,21 @@ PoolByteArray StreamPeerSSL::get_project_cert_array() { ProjectSettings::get_singleton()->set_custom_property_info("network/ssl/certificates", PropertyInfo(Variant::STRING, "network/ssl/certificates", PROPERTY_HINT_FILE, "*.crt")); if (certs_path != "") { - - FileAccess *f = FileAccess::open(certs_path, FileAccess::READ); - if (f) { - int flen = f->get_len(); - out.resize(flen + 1); - { - PoolByteArray::Write w = out.write(); - f->get_buffer(w.ptr(), flen); - w[flen] = 0; //end f string - } - - memdelete(f); - + // Use certs defined in project settings. + return get_cert_file_as_array(certs_path); + } +#ifdef BUILTIN_CERTS_ENABLED + else { + // Use builtin certs only if user did not override it in project settings. + out.resize(_certs_uncompressed_size + 1); + PoolByteArray::Write w = out.write(); + Compression::decompress(w.ptr(), _certs_uncompressed_size, _certs_compressed, _certs_compressed_size, Compression::MODE_DEFLATE); + w[_certs_uncompressed_size] = 0; // Make sure it ends with string terminator #ifdef DEBUG_ENABLED - print_verbose(vformat("Loaded certs from '%s'.", certs_path)); + print_verbose("Loaded builtin certs"); #endif - } } +#endif return out; } @@ -102,6 +128,7 @@ void StreamPeerSSL::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "blocking_handshake"), "set_blocking_handshake_enabled", "is_blocking_handshake_enabled"); BIND_ENUM_CONSTANT(STATUS_DISCONNECTED); + BIND_ENUM_CONSTANT(STATUS_HANDSHAKING); BIND_ENUM_CONSTANT(STATUS_CONNECTED); BIND_ENUM_CONSTANT(STATUS_ERROR); BIND_ENUM_CONSTANT(STATUS_ERROR_HOSTNAME_MISMATCH); diff --git a/core/io/stream_peer_ssl.h b/core/io/stream_peer_ssl.h index 870704e875..8ce36d7e7d 100644 --- a/core/io/stream_peer_ssl.h +++ b/core/io/stream_peer_ssl.h @@ -31,7 +31,7 @@ #ifndef STREAM_PEER_SSL_H #define STREAM_PEER_SSL_H -#include "io/stream_peer.h" +#include "core/io/stream_peer.h" class StreamPeerSSL : public StreamPeer { GDCLASS(StreamPeerSSL, StreamPeer); @@ -46,9 +46,6 @@ protected: static LoadCertsFromMemory load_certs_func; static bool available; - friend class Main; - static bool initialize_certs; - bool blocking_handshake; public: @@ -72,7 +69,9 @@ public: static StreamPeerSSL *create(); + static PoolByteArray get_cert_file_as_array(String p_path); static PoolByteArray get_project_cert_array(); + static void load_certs_from_file(String p_path); static void load_certs_from_memory(const PoolByteArray &p_memory); static bool is_available(); diff --git a/core/io/stream_peer_tcp.cpp b/core/io/stream_peer_tcp.cpp index 5d008904ff..28561e8cbc 100644 --- a/core/io/stream_peer_tcp.cpp +++ b/core/io/stream_peer_tcp.cpp @@ -30,7 +30,294 @@ #include "stream_peer_tcp.h" -StreamPeerTCP *(*StreamPeerTCP::_create)() = NULL; +Error StreamPeerTCP::_poll_connection() { + + ERR_FAIL_COND_V(status != STATUS_CONNECTING || !_sock.is_valid() || !_sock->is_open(), FAILED); + + Error err = _sock->connect_to_host(peer_host, peer_port); + + if (err == OK) { + status = STATUS_CONNECTED; + return OK; + } else if (err == ERR_BUSY) { + // Still trying to connect + return OK; + } + + disconnect_from_host(); + status = STATUS_ERROR; + return ERR_CONNECTION_ERROR; +} + +void StreamPeerTCP::accept_socket(Ref<NetSocket> p_sock, IP_Address p_host, uint16_t p_port) { + + _sock = p_sock; + _sock->set_blocking_enabled(false); + + status = STATUS_CONNECTING; + + peer_host = p_host; + peer_port = p_port; +} + +Error StreamPeerTCP::connect_to_host(const IP_Address &p_host, uint16_t p_port) { + + ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); + ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE); + ERR_FAIL_COND_V(!p_host.is_valid(), ERR_INVALID_PARAMETER); + + Error err; + IP::Type ip_type = p_host.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; + + err = _sock->open(NetSocket::TYPE_TCP, ip_type); + ERR_FAIL_COND_V(err != OK, FAILED); + + _sock->set_blocking_enabled(false); + + err = _sock->connect_to_host(p_host, p_port); + + if (err == OK) { + status = STATUS_CONNECTED; + } else if (err == ERR_BUSY) { + status = STATUS_CONNECTING; + } else { + ERR_PRINT("Connection to remote host failed!"); + disconnect_from_host(); + return FAILED; + } + + peer_host = p_host; + peer_port = p_port; + + return OK; +} + +Error StreamPeerTCP::write(const uint8_t *p_data, int p_bytes, int &r_sent, bool p_block) { + + ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); + + if (status == STATUS_NONE || status == STATUS_ERROR) { + + return FAILED; + } + + if (status != STATUS_CONNECTED) { + + if (_poll_connection() != OK) { + + return FAILED; + } + + if (status != STATUS_CONNECTED) { + r_sent = 0; + return OK; + } + } + + if (!_sock->is_open()) + return FAILED; + + Error err; + int data_to_send = p_bytes; + const uint8_t *offset = p_data; + int total_sent = 0; + + while (data_to_send) { + + int sent_amount = 0; + err = _sock->send(offset, data_to_send, sent_amount); + + if (err != OK) { + + if (err != ERR_BUSY) { + disconnect_from_host(); + return FAILED; + } + + if (!p_block) { + r_sent = total_sent; + return OK; + } + + // Block and wait for the socket to accept more data + err = _sock->poll(NetSocket::POLL_TYPE_OUT, -1); + if (err != OK) { + disconnect_from_host(); + return FAILED; + } + } else { + + data_to_send -= sent_amount; + offset += sent_amount; + total_sent += sent_amount; + } + } + + r_sent = total_sent; + + return OK; +} + +Error StreamPeerTCP::read(uint8_t *p_buffer, int p_bytes, int &r_received, bool p_block) { + + if (!is_connected_to_host()) { + + return FAILED; + } + + if (status == STATUS_CONNECTING) { + + if (_poll_connection() != OK) { + + return FAILED; + } + + if (status != STATUS_CONNECTED) { + r_received = 0; + return OK; + } + } + + Error err; + int to_read = p_bytes; + int total_read = 0; + r_received = 0; + + while (to_read) { + + int read = 0; + err = _sock->recv(p_buffer + total_read, to_read, read); + + if (err != OK) { + + if (err != ERR_BUSY) { + disconnect_from_host(); + return FAILED; + } + + if (!p_block) { + r_received = total_read; + return OK; + } + + err = _sock->poll(NetSocket::POLL_TYPE_IN, -1); + + if (err != OK) { + disconnect_from_host(); + return FAILED; + } + + } else if (read == 0) { + + disconnect_from_host(); + r_received = total_read; + return ERR_FILE_EOF; + + } else { + + to_read -= read; + total_read += read; + } + } + + r_received = total_read; + + return OK; +} + +void StreamPeerTCP::set_no_delay(bool p_enabled) { + + ERR_FAIL_COND(!is_connected_to_host()); + _sock->set_tcp_no_delay_enabled(p_enabled); +} + +bool StreamPeerTCP::is_connected_to_host() const { + + if (status == STATUS_NONE || status == STATUS_ERROR) { + + return false; + } + + if (status != STATUS_CONNECTED) { + return true; + } + + return _sock.is_valid() && _sock->is_open(); +} + +StreamPeerTCP::Status StreamPeerTCP::get_status() { + + if (status == STATUS_CONNECTING) { + _poll_connection(); + } else if (status == STATUS_CONNECTED) { + Error err; + err = _sock->poll(NetSocket::POLL_TYPE_IN, 0); + if (err == OK) { + // FIN received + if (_sock->get_available_bytes() == 0) { + disconnect_from_host(); + return status; + } + } + // Also poll write + err = _sock->poll(NetSocket::POLL_TYPE_IN_OUT, 0); + if (err != OK && err != ERR_BUSY) { + // Got an error + disconnect_from_host(); + status = STATUS_ERROR; + } + } + + return status; +} + +void StreamPeerTCP::disconnect_from_host() { + + if (_sock.is_valid() && _sock->is_open()) + _sock->close(); + + status = STATUS_NONE; + peer_host = IP_Address(); + peer_port = 0; +} + +Error StreamPeerTCP::put_data(const uint8_t *p_data, int p_bytes) { + + int total; + return write(p_data, p_bytes, total, true); +} + +Error StreamPeerTCP::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) { + + return write(p_data, p_bytes, r_sent, false); +} + +Error StreamPeerTCP::get_data(uint8_t *p_buffer, int p_bytes) { + + int total; + return read(p_buffer, p_bytes, total, true); +} + +Error StreamPeerTCP::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) { + + return read(p_buffer, p_bytes, r_received, false); +} + +int StreamPeerTCP::get_available_bytes() const { + + ERR_FAIL_COND_V(!_sock.is_valid(), -1); + return _sock->get_available_bytes(); +} + +IP_Address StreamPeerTCP::get_connected_host() const { + + return peer_host; +} + +uint16_t StreamPeerTCP::get_connected_port() const { + + return peer_port; +} Error StreamPeerTCP::_connect(const String &p_address, int p_port) { @@ -43,8 +330,7 @@ Error StreamPeerTCP::_connect(const String &p_address, int p_port) { return ERR_CANT_RESOLVE; } - connect_to_host(ip, p_port); - return OK; + return connect_to_host(ip, p_port); } void StreamPeerTCP::_bind_methods() { @@ -63,23 +349,15 @@ void StreamPeerTCP::_bind_methods() { BIND_ENUM_CONSTANT(STATUS_ERROR); } -Ref<StreamPeerTCP> StreamPeerTCP::create_ref() { +StreamPeerTCP::StreamPeerTCP() { - if (!_create) - return Ref<StreamPeerTCP>(); - return Ref<StreamPeerTCP>(_create()); + _sock = Ref<NetSocket>(NetSocket::create()); + status = STATUS_NONE; + peer_host = IP_Address(); + peer_port = 0; } -StreamPeerTCP *StreamPeerTCP::create() { +StreamPeerTCP::~StreamPeerTCP() { - if (!_create) - return NULL; - return _create(); + disconnect_from_host(); } - -StreamPeerTCP::StreamPeerTCP() { -} - -StreamPeerTCP::~StreamPeerTCP(){ - -}; diff --git a/core/io/stream_peer_tcp.h b/core/io/stream_peer_tcp.h index 8a16d820f2..de364915cd 100644 --- a/core/io/stream_peer_tcp.h +++ b/core/io/stream_peer_tcp.h @@ -31,10 +31,10 @@ #ifndef STREAM_PEER_TCP_H #define STREAM_PEER_TCP_H -#include "stream_peer.h" - -#include "io/ip.h" -#include "ip_address.h" +#include "core/io/ip.h" +#include "core/io/ip_address.h" +#include "core/io/net_socket.h" +#include "core/io/stream_peer.h" class StreamPeerTCP : public StreamPeer { @@ -51,24 +51,37 @@ public: }; protected: - virtual Error _connect(const String &p_address, int p_port); - static StreamPeerTCP *(*_create)(); + Ref<NetSocket> _sock; + Status status; + IP_Address peer_host; + uint16_t peer_port; + + Error _connect(const String &p_address, int p_port); + Error _poll_connection(); + Error write(const uint8_t *p_data, int p_bytes, int &r_sent, bool p_block); + Error read(uint8_t *p_buffer, int p_bytes, int &r_received, bool p_block); + static void _bind_methods(); public: - virtual Error connect_to_host(const IP_Address &p_host, uint16_t p_port) = 0; + void accept_socket(Ref<NetSocket> p_sock, IP_Address p_host, uint16_t p_port); + + Error connect_to_host(const IP_Address &p_host, uint16_t p_port); + bool is_connected_to_host() const; + IP_Address get_connected_host() const; + uint16_t get_connected_port() const; + void disconnect_from_host(); - //read/write from streampeer + int get_available_bytes() const; + Status get_status(); - virtual bool is_connected_to_host() const = 0; - virtual Status get_status() const = 0; - virtual void disconnect_from_host() = 0; - virtual IP_Address get_connected_host() const = 0; - virtual uint16_t get_connected_port() const = 0; - virtual void set_no_delay(bool p_enabled) = 0; + void set_no_delay(bool p_enabled); - static Ref<StreamPeerTCP> create_ref(); - static StreamPeerTCP *create(); + // Read/Write from StreamPeer + Error put_data(const uint8_t *p_data, int p_bytes); + Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent); + Error get_data(uint8_t *p_buffer, int p_bytes); + Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received); StreamPeerTCP(); ~StreamPeerTCP(); diff --git a/core/io/tcp_server.cpp b/core/io/tcp_server.cpp index 5916d58390..b8194cb17f 100644 --- a/core/io/tcp_server.cpp +++ b/core/io/tcp_server.cpp @@ -30,29 +30,98 @@ #include "tcp_server.h" -TCP_Server *(*TCP_Server::_create)() = NULL; +void TCP_Server::_bind_methods() { + + ClassDB::bind_method(D_METHOD("listen", "port", "bind_address"), &TCP_Server::listen, DEFVAL("*")); + ClassDB::bind_method(D_METHOD("is_connection_available"), &TCP_Server::is_connection_available); + ClassDB::bind_method(D_METHOD("take_connection"), &TCP_Server::take_connection); + ClassDB::bind_method(D_METHOD("stop"), &TCP_Server::stop); +} + +Error TCP_Server::listen(uint16_t p_port, const IP_Address &p_bind_address) { + + ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); + ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE); + ERR_FAIL_COND_V(!p_bind_address.is_valid() && !p_bind_address.is_wildcard(), ERR_INVALID_PARAMETER); + + Error err; + IP::Type ip_type = IP::TYPE_ANY; + + // If the bind address is valid use its type as the socket type + if (p_bind_address.is_valid()) + ip_type = p_bind_address.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; + + err = _sock->open(NetSocket::TYPE_TCP, ip_type); + + ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); + + _sock->set_blocking_enabled(false); + _sock->set_reuse_address_enabled(true); + + err = _sock->bind(p_bind_address, p_port); + + if (err != OK) { + + _sock->close(); + return ERR_ALREADY_IN_USE; + } -Ref<TCP_Server> TCP_Server::create_ref() { + err = _sock->listen(MAX_PENDING_CONNECTIONS); - if (!_create) - return NULL; - return Ref<TCP_Server>(_create()); + if (err != OK) { + _sock->close(); + return FAILED; + } + return OK; } -TCP_Server *TCP_Server::create() { +bool TCP_Server::is_connection_available() const { - if (!_create) - return NULL; - return _create(); + ERR_FAIL_COND_V(!_sock.is_valid(), false); + + if (!_sock->is_open()) + return false; + + Error err = _sock->poll(NetSocket::POLL_TYPE_IN, 0); + if (err != OK) { + return false; + } + + return true; } -void TCP_Server::_bind_methods() { +Ref<StreamPeerTCP> TCP_Server::take_connection() { - ClassDB::bind_method(D_METHOD("listen", "port", "bind_address"), &TCP_Server::listen, DEFVAL("*")); - ClassDB::bind_method(D_METHOD("is_connection_available"), &TCP_Server::is_connection_available); - ClassDB::bind_method(D_METHOD("take_connection"), &TCP_Server::take_connection); - ClassDB::bind_method(D_METHOD("stop"), &TCP_Server::stop); + Ref<StreamPeerTCP> conn; + if (!is_connection_available()) { + return conn; + } + + Ref<NetSocket> ns; + IP_Address ip; + uint16_t port = 0; + ns = _sock->accept(ip, port); + if (!ns.is_valid()) + return conn; + + conn = Ref<StreamPeerTCP>(memnew(StreamPeerTCP)); + conn->accept_socket(ns, ip, port); + return conn; +} + +void TCP_Server::stop() { + + if (_sock.is_valid()) { + _sock->close(); + } } TCP_Server::TCP_Server() { + + _sock = Ref<NetSocket>(NetSocket::create()); +} + +TCP_Server::~TCP_Server() { + + stop(); } diff --git a/core/io/tcp_server.h b/core/io/tcp_server.h index a250e8b249..4c89197d9a 100644 --- a/core/io/tcp_server.h +++ b/core/io/tcp_server.h @@ -31,31 +31,32 @@ #ifndef TCP_SERVER_H #define TCP_SERVER_H -#include "io/ip.h" -#include "io/stream_peer.h" -#include "stream_peer_tcp.h" +#include "core/io/ip.h" +#include "core/io/net_socket.h" +#include "core/io/stream_peer.h" +#include "core/io/stream_peer_tcp.h" class TCP_Server : public Reference { GDCLASS(TCP_Server, Reference); protected: - static TCP_Server *(*_create)(); + enum { + MAX_PENDING_CONNECTIONS = 8 + }; - //bind helper + Ref<NetSocket> _sock; static void _bind_methods(); public: - virtual Error listen(uint16_t p_port, const IP_Address &p_bind_address = IP_Address("*")) = 0; - virtual bool is_connection_available() const = 0; - virtual Ref<StreamPeerTCP> take_connection() = 0; + Error listen(uint16_t p_port, const IP_Address &p_bind_address = IP_Address("*")); + bool is_connection_available() const; + Ref<StreamPeerTCP> take_connection(); - virtual void stop() = 0; //stop listening - - static Ref<TCP_Server> create_ref(); - static TCP_Server *create(); + void stop(); // Stop listening TCP_Server(); + ~TCP_Server(); }; #endif // TCP_SERVER_H diff --git a/core/io/translation_loader_po.cpp b/core/io/translation_loader_po.cpp index 85c1fc5ddf..830c2b6694 100644 --- a/core/io/translation_loader_po.cpp +++ b/core/io/translation_loader_po.cpp @@ -29,8 +29,9 @@ /*************************************************************************/ #include "translation_loader_po.h" -#include "os/file_access.h" -#include "translation.h" + +#include "core/os/file_access.h" +#include "core/translation.h" RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const String &p_path) { diff --git a/core/io/translation_loader_po.h b/core/io/translation_loader_po.h index 33cf9bd8b4..670a9fdd7e 100644 --- a/core/io/translation_loader_po.h +++ b/core/io/translation_loader_po.h @@ -31,9 +31,10 @@ #ifndef TRANSLATION_LOADER_PO_H #define TRANSLATION_LOADER_PO_H -#include "io/resource_loader.h" -#include "os/file_access.h" -#include "translation.h" +#include "core/io/resource_loader.h" +#include "core/os/file_access.h" +#include "core/translation.h" + class TranslationLoaderPO : public ResourceFormatLoader { public: static RES load_translation(FileAccess *f, Error *r_error, const String &p_path = String()); diff --git a/core/io/xml_parser.cpp b/core/io/xml_parser.cpp index 33c9b56d5a..8c0cbd6e55 100644 --- a/core/io/xml_parser.cpp +++ b/core/io/xml_parser.cpp @@ -29,7 +29,9 @@ /*************************************************************************/ #include "xml_parser.h" -#include "print_string.h" + +#include "core/print_string.h" + //#define DEBUG_XML VARIANT_ENUM_CAST(XMLParser::NodeType); diff --git a/core/io/xml_parser.h b/core/io/xml_parser.h index 297b57ffdc..bbc7671a0e 100644 --- a/core/io/xml_parser.h +++ b/core/io/xml_parser.h @@ -31,10 +31,10 @@ #ifndef XML_PARSER_H #define XML_PARSER_H -#include "os/file_access.h" -#include "reference.h" -#include "ustring.h" -#include "vector.h" +#include "core/os/file_access.h" +#include "core/reference.h" +#include "core/ustring.h" +#include "core/vector.h" /* Based on irrXML (see their zlib license). Added mainly for compatibility with their Collada loader. diff --git a/core/io/zip_io.cpp b/core/io/zip_io.cpp new file mode 100644 index 0000000000..b7f841b66f --- /dev/null +++ b/core/io/zip_io.cpp @@ -0,0 +1,137 @@ +/*************************************************************************/ +/* zip_io.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 "zip_io.h" + +#include "core/os/copymem.h" + +void *zipio_open(void *data, const char *p_fname, int mode) { + + FileAccess *&f = *(FileAccess **)data; + + String fname; + fname.parse_utf8(p_fname); + + if (mode & ZLIB_FILEFUNC_MODE_WRITE) { + f = FileAccess::open(fname, FileAccess::WRITE); + } else { + + f = FileAccess::open(fname, FileAccess::READ); + } + + if (!f) + return NULL; + + return data; +} + +uLong zipio_read(void *data, void *fdata, void *buf, uLong size) { + + FileAccess *f = *(FileAccess **)data; + return f->get_buffer((uint8_t *)buf, size); +} + +uLong zipio_write(voidpf opaque, voidpf stream, const void *buf, uLong size) { + + FileAccess *f = *(FileAccess **)opaque; + f->store_buffer((uint8_t *)buf, size); + return size; +} + +long zipio_tell(voidpf opaque, voidpf stream) { + + FileAccess *f = *(FileAccess **)opaque; + return f->get_position(); +} + +long zipio_seek(voidpf opaque, voidpf stream, uLong offset, int origin) { + + FileAccess *f = *(FileAccess **)opaque; + + int pos = offset; + switch (origin) { + + case ZLIB_FILEFUNC_SEEK_CUR: + pos = f->get_position() + offset; + break; + case ZLIB_FILEFUNC_SEEK_END: + pos = f->get_len() + offset; + break; + default: + break; + }; + + f->seek(pos); + return 0; +} + +int zipio_close(voidpf opaque, voidpf stream) { + + FileAccess *&f = *(FileAccess **)opaque; + if (f) { + f->close(); + f = NULL; + } + return 0; +} + +int zipio_testerror(voidpf opaque, voidpf stream) { + + FileAccess *f = *(FileAccess **)opaque; + return (f && f->get_error() != OK) ? 1 : 0; +} + +voidpf zipio_alloc(voidpf opaque, uInt items, uInt size) { + + voidpf ptr = memalloc(items * size); + zeromem(ptr, items * size); + return ptr; +} + +void zipio_free(voidpf opaque, voidpf address) { + + memfree(address); +} + +zlib_filefunc_def zipio_create_io_from_file(FileAccess **p_file) { + + zlib_filefunc_def io; + io.opaque = p_file; + io.zopen_file = zipio_open; + io.zread_file = zipio_read; + io.zwrite_file = zipio_write; + io.ztell_file = zipio_tell; + io.zseek_file = zipio_seek; + io.zclose_file = zipio_close; + io.zerror_file = zipio_testerror; + io.alloc_mem = zipio_alloc; + io.free_mem = zipio_free; + return io; +} diff --git a/core/io/zip_io.h b/core/io/zip_io.h index 3a7fdb0302..bba7d67332 100644 --- a/core/io/zip_io.h +++ b/core/io/zip_io.h @@ -31,114 +31,28 @@ #ifndef ZIP_IO_H #define ZIP_IO_H -#include "os/copymem.h" -#include "os/file_access.h" +#include "core/os/file_access.h" +// Not direclty used in this header, but assumed available in downstream users +// like platform/*/export/export.cpp. Could be fixed, but probably better to have +// thirdparty includes in as little headers as possible. #include "thirdparty/minizip/unzip.h" #include "thirdparty/minizip/zip.h" -static void *zipio_open(void *data, const char *p_fname, int mode) { +void *zipio_open(void *data, const char *p_fname, int mode); +uLong zipio_read(void *data, void *fdata, void *buf, uLong size); +uLong zipio_write(voidpf opaque, voidpf stream, const void *buf, uLong size); - FileAccess *&f = *(FileAccess **)data; +long zipio_tell(voidpf opaque, voidpf stream); +long zipio_seek(voidpf opaque, voidpf stream, uLong offset, int origin); - String fname; - fname.parse_utf8(p_fname); +int zipio_close(voidpf opaque, voidpf stream); - if (mode & ZLIB_FILEFUNC_MODE_WRITE) { - f = FileAccess::open(fname, FileAccess::WRITE); - } else { +int zipio_testerror(voidpf opaque, voidpf stream); - f = FileAccess::open(fname, FileAccess::READ); - } +voidpf zipio_alloc(voidpf opaque, uInt items, uInt size); +void zipio_free(voidpf opaque, voidpf address); - if (!f) - return NULL; - - return data; -}; - -static uLong zipio_read(void *data, void *fdata, void *buf, uLong size) { - - FileAccess *f = *(FileAccess **)data; - return f->get_buffer((uint8_t *)buf, size); -}; - -static uLong zipio_write(voidpf opaque, voidpf stream, const void *buf, uLong size) { - - FileAccess *f = *(FileAccess **)opaque; - f->store_buffer((uint8_t *)buf, size); - return size; -}; - -static long zipio_tell(voidpf opaque, voidpf stream) { - - FileAccess *f = *(FileAccess **)opaque; - return f->get_position(); -}; - -static long zipio_seek(voidpf opaque, voidpf stream, uLong offset, int origin) { - - FileAccess *f = *(FileAccess **)opaque; - - int pos = offset; - switch (origin) { - - case ZLIB_FILEFUNC_SEEK_CUR: - pos = f->get_position() + offset; - break; - case ZLIB_FILEFUNC_SEEK_END: - pos = f->get_len() + offset; - break; - default: - break; - }; - - f->seek(pos); - return 0; -}; - -static int zipio_close(voidpf opaque, voidpf stream) { - - FileAccess *&f = *(FileAccess **)opaque; - if (f) { - f->close(); - f = NULL; - } - return 0; -}; - -static int zipio_testerror(voidpf opaque, voidpf stream) { - - FileAccess *f = *(FileAccess **)opaque; - return (f && f->get_error() != OK) ? 1 : 0; -}; - -static voidpf zipio_alloc(voidpf opaque, uInt items, uInt size) { - - voidpf ptr = memalloc(items * size); - zeromem(ptr, items * size); - return ptr; -} - -static void zipio_free(voidpf opaque, voidpf address) { - - memfree(address); -} - -static zlib_filefunc_def zipio_create_io_from_file(FileAccess **p_file) { - - zlib_filefunc_def io; - io.opaque = p_file; - io.zopen_file = zipio_open; - io.zread_file = zipio_read; - io.zwrite_file = zipio_write; - io.ztell_file = zipio_tell; - io.zseek_file = zipio_seek; - io.zclose_file = zipio_close; - io.zerror_file = zipio_testerror; - io.alloc_mem = zipio_alloc; - io.free_mem = zipio_free; - return io; -} +zlib_filefunc_def zipio_create_io_from_file(FileAccess **p_file); #endif // ZIP_IO_H diff --git a/core/list.h b/core/list.h index f977df4634..281f253a59 100644 --- a/core/list.h +++ b/core/list.h @@ -31,8 +31,8 @@ #ifndef GLOBALS_LIST_H #define GLOBALS_LIST_H -#include "os/memory.h" -#include "sort.h" +#include "core/os/memory.h" +#include "core/sort.h" /** * Generic Templatized Linked List Implementation. diff --git a/core/map.h b/core/map.h index 700d4b8693..cab8965bb3 100644 --- a/core/map.h +++ b/core/map.h @@ -31,7 +31,7 @@ #ifndef MAP_H #define MAP_H -#include "set.h" +#include "core/set.h" /** @author Juan Linietsky <reduzio@gmail.com> diff --git a/core/math/SCsub b/core/math/SCsub index 4efc902717..1c5f954470 100644 --- a/core/math/SCsub +++ b/core/math/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.core_sources, "*.cpp") - -Export('env') diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp index 021391da83..451c45cade 100644 --- a/core/math/a_star.cpp +++ b/core/math/a_star.cpp @@ -29,9 +29,10 @@ /*************************************************************************/ #include "a_star.h" -#include "geometry.h" + +#include "core/math/geometry.h" +#include "core/script_language.h" #include "scene/scene_string_names.h" -#include "script_language.h" int AStar::get_available_point_id() const { @@ -249,14 +250,9 @@ bool AStar::_solve(Point *begin_point, Point *end_point) { n->distance = _compute_cost(begin_point->id, n->id) * n->weight_scale; n->last_pass = pass; open_list.add(&n->list); - - if (end_point == n) { - found_route = true; - break; - } } - while (!found_route) { + while (true) { if (open_list.first() == NULL) { // No path found @@ -276,13 +272,16 @@ bool AStar::_solve(Point *begin_point, Point *end_point) { cost += _estimate_cost(p->id, end_point->id); if (cost < least_cost) { - least_cost_point = E; least_cost = cost; } } Point *p = least_cost_point->self(); + if (p == end_point) { + found_route = true; + break; + } for (Set<Point *>::Element *E = p->neighbours.front(); E; E = E->next()) { @@ -294,7 +293,6 @@ bool AStar::_solve(Point *begin_point, Point *end_point) { // Already visited, is this cheaper? if (e->distance > distance) { - e->prev_point = p; e->distance = distance; } @@ -305,18 +303,9 @@ bool AStar::_solve(Point *begin_point, Point *end_point) { e->distance = distance; e->last_pass = pass; // Mark as used open_list.add(&e->list); - - if (e == end_point) { - // End reached; stop algorithm - found_route = true; - break; - } } } - if (found_route) - break; - open_list.remove(least_cost_point); } diff --git a/core/math/a_star.h b/core/math/a_star.h index 8c1b5f64cb..d2ef765006 100644 --- a/core/math/a_star.h +++ b/core/math/a_star.h @@ -31,8 +31,9 @@ #ifndef ASTAR_H #define ASTAR_H -#include "reference.h" -#include "self_list.h" +#include "core/reference.h" +#include "core/self_list.h" + /** A* pathfinding algorithm diff --git a/core/math/aabb.cpp b/core/math/aabb.cpp index e2e71dda92..d0cb2b5195 100644 --- a/core/math/aabb.cpp +++ b/core/math/aabb.cpp @@ -30,7 +30,7 @@ #include "aabb.h" -#include "print_string.h" +#include "core/print_string.h" real_t AABB::get_area() const { diff --git a/core/math/aabb.h b/core/math/aabb.h index cdb8eb48a3..0b03b7d314 100644 --- a/core/math/aabb.h +++ b/core/math/aabb.h @@ -31,9 +31,9 @@ #ifndef AABB_H #define AABB_H -#include "math_defs.h" -#include "plane.h" -#include "vector3.h" +#include "core/math/math_defs.h" +#include "core/math/plane.h" +#include "core/math/vector3.h" /** * AABB / AABB (Axis Aligned Bounding Box) diff --git a/core/math/audio_frame.h b/core/math/audio_frame.h index 67ba025e1c..fde26e8056 100644 --- a/core/math/audio_frame.h +++ b/core/math/audio_frame.h @@ -31,7 +31,7 @@ #ifndef AUDIOFRAME_H #define AUDIOFRAME_H -#include "typedefs.h" +#include "core/typedefs.h" static inline float undenormalise(volatile float f) { union { diff --git a/core/math/bsp_tree.cpp b/core/math/bsp_tree.cpp index 24096de551..6ffc963783 100644 --- a/core/math/bsp_tree.cpp +++ b/core/math/bsp_tree.cpp @@ -29,8 +29,9 @@ /*************************************************************************/ #include "bsp_tree.h" -#include "error_macros.h" -#include "print_string.h" + +#include "core/error_macros.h" +#include "core/print_string.h" void BSP_Tree::from_aabb(const AABB &p_aabb) { @@ -164,7 +165,6 @@ int BSP_Tree::get_points_inside(const Vector3 *p_points, int p_point_count) cons int pass_count = 0; const Node *nodesptr = &nodes[0]; const Plane *planesptr = &planes[0]; - int plane_count = planes.size(); int node_count = nodes.size(); if (node_count == 0) // no nodes! @@ -191,9 +191,9 @@ int BSP_Tree::get_points_inside(const Vector3 *p_points, int p_point_count) cons break; } - 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 diff --git a/core/math/bsp_tree.h b/core/math/bsp_tree.h index fb16818ae7..b06e6b8539 100644 --- a/core/math/bsp_tree.h +++ b/core/math/bsp_tree.h @@ -31,13 +31,13 @@ #ifndef BSP_TREE_H #define BSP_TREE_H -#include "aabb.h" -#include "dvector.h" -#include "face3.h" -#include "method_ptrcall.h" -#include "plane.h" -#include "variant.h" -#include "vector.h" +#include "core/dvector.h" +#include "core/math/aabb.h" +#include "core/math/face3.h" +#include "core/math/plane.h" +#include "core/method_ptrcall.h" +#include "core/variant.h" +#include "core/vector.h" /** @author Juan Linietsky <reduzio@gmail.com> */ diff --git a/core/math/camera_matrix.cpp b/core/math/camera_matrix.cpp index 1ab9b3532e..3a082d5720 100644 --- a/core/math/camera_matrix.cpp +++ b/core/math/camera_matrix.cpp @@ -29,8 +29,9 @@ /*************************************************************************/ #include "camera_matrix.h" -#include "math_funcs.h" -#include "print_string.h" + +#include "core/math/math_funcs.h" +#include "core/print_string.h" void CameraMatrix::set_identity() { diff --git a/core/math/camera_matrix.h b/core/math/camera_matrix.h index a689c7238a..bd20908ad9 100644 --- a/core/math/camera_matrix.h +++ b/core/math/camera_matrix.h @@ -31,8 +31,9 @@ #ifndef CAMERA_MATRIX_H #define CAMERA_MATRIX_H -#include "rect2.h" -#include "transform.h" +#include "core/math/rect2.h" +#include "core/math/transform.h" + /** @author Juan Linietsky <reduzio@gmail.com> */ diff --git a/core/math/delaunay.cpp b/core/math/delaunay.cpp deleted file mode 100644 index 8cae92b7c0..0000000000 --- a/core/math/delaunay.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "delaunay.h" diff --git a/core/math/delaunay.h b/core/math/delaunay.h index 46535d5ce9..9c5eef9069 100644 --- a/core/math/delaunay.h +++ b/core/math/delaunay.h @@ -1,7 +1,37 @@ +/*************************************************************************/ +/* delaunay.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 DELAUNAY_H #define DELAUNAY_H -#include "rect2.h" +#include "core/math/rect2.h" class Delaunay2D { public: diff --git a/core/math/expression.cpp b/core/math/expression.cpp index ba40cb4586..0cfb54234c 100644 --- a/core/math/expression.cpp +++ b/core/math/expression.cpp @@ -1,12 +1,42 @@ +/*************************************************************************/ +/* expression.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 "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" +#include "core/class_db.h" +#include "core/func_ref.h" +#include "core/io/marshalls.h" +#include "core/math/math_funcs.h" +#include "core/os/os.h" +#include "core/reference.h" +#include "core/variant_parser.h" const char *Expression::func_name[Expression::FUNC_MAX] = { "sin", @@ -726,6 +756,10 @@ void Expression::exec_func(BuiltinFunc p_func, const Variant **p_inputs, Variant //////// +static bool _is_number(CharType c) { + return (c >= '0' && c <= '9'); +} + Error Expression::_get_token(Token &r_token) { while (true) { @@ -783,17 +817,12 @@ Error Expression::_get_token(Token &r_token) { 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') { + if (!_is_number(expression[str_ofs])) { _set_error("Expected number after '$'"); r_token.type = TK_ERROR; return ERR_PARSE_ERROR; @@ -802,7 +831,7 @@ Error Expression::_get_token(Token &r_token) { index += expression[str_ofs] - '0'; str_ofs++; - } while (expression[str_ofs] >= '0' && expression[str_ofs] <= '9'); + } while (_is_number(expression[str_ofs])); r_token.value = index; return OK; @@ -949,14 +978,14 @@ Error Expression::_get_token(Token &r_token) { r_token.type = TK_ERROR; return ERR_PARSE_ERROR; } - if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) { + if (!(_is_number(c) || (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') { + if (_is_number(c)) { v = c - '0'; } else if (c >= 'a' && c <= 'f') { v = c - 'a'; @@ -1002,7 +1031,8 @@ Error Expression::_get_token(Token &r_token) { break; } - if (cchar >= '0' && cchar <= '9') { + CharType next_char = (str_ofs >= expression.length()) ? 0 : expression[str_ofs]; + if (_is_number(cchar) || (cchar == '.' && _is_number(next_char))) { //a number String num; @@ -1023,7 +1053,7 @@ Error Expression::_get_token(Token &r_token) { switch (reading) { case READING_INT: { - if (c >= '0' && c <= '9') { + if (_is_number(c)) { //pass } else if (c == '.') { reading = READING_DEC; @@ -1037,7 +1067,7 @@ Error Expression::_get_token(Token &r_token) { } break; case READING_DEC: { - if (c >= '0' && c <= '9') { + if (_is_number(c)) { } else if (c == 'e') { reading = READING_EXP; @@ -1049,7 +1079,7 @@ Error Expression::_get_token(Token &r_token) { } break; case READING_EXP: { - if (c >= '0' && c <= '9') { + if (_is_number(c)) { exp_beg = true; } else if ((c == '-' || c == '+') && !exp_sign && !exp_beg) { @@ -1084,7 +1114,7 @@ Error Expression::_get_token(Token &r_token) { String id; bool first = true; - while ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_' || (!first && cchar >= '0' && cchar <= '9')) { + while ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_' || (!first && _is_number(cchar))) { id += String::chr(cchar); cchar = GET_CHAR(); @@ -1146,6 +1176,12 @@ Error Expression::_get_token(Token &r_token) { } return OK; + + } else if (cchar == '.') { + // Handled down there as we support '.[0-9]' as numbers above + r_token.type = TK_PERIOD; + return OK; + } else { _set_error("Unexpected character."); r_token.type = TK_ERROR; @@ -1153,6 +1189,7 @@ Error Expression::_get_token(Token &r_token) { } } } +#undef GET_CHAR } r_token.type = TK_ERROR; @@ -2083,6 +2120,10 @@ Error Expression::parse(const String &p_expression, const Vector<String> &p_inpu } Variant Expression::execute(Array p_inputs, Object *p_base, bool p_show_error) { + if (error_set) { + ERR_EXPLAIN("There was previously a parse error: " + error_str); + ERR_FAIL_V(Variant()); + } execution_error = false; Variant output; diff --git a/core/math/expression.h b/core/math/expression.h index 7a7639cf0b..ac2416d0dd 100644 --- a/core/math/expression.h +++ b/core/math/expression.h @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* expression.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 EXPRESSION_H #define EXPRESSION_H diff --git a/core/math/face3.cpp b/core/math/face3.cpp index 801f2a3b4d..8366137131 100644 --- a/core/math/face3.cpp +++ b/core/math/face3.cpp @@ -29,7 +29,8 @@ /*************************************************************************/ #include "face3.h" -#include "geometry.h" + +#include "core/math/geometry.h" int Face3::split_by_plane(const Plane &p_plane, Face3 p_res[3], bool p_is_point_over[3]) const { @@ -201,11 +202,12 @@ bool Face3::intersects_aabb(const AABB &p_aabb) const { { \ real_t aabb_min = p_aabb.position.m_ax; \ real_t aabb_max = p_aabb.position.m_ax + p_aabb.size.m_ax; \ - real_t tri_min, tri_max; \ - for (int i = 0; i < 3; i++) { \ - if (i == 0 || vertex[i].m_ax > tri_max) \ + real_t tri_min = vertex[0].m_ax; \ + real_t tri_max = vertex[0].m_ax; \ + for (int i = 1; i < 3; i++) { \ + if (vertex[i].m_ax > tri_max) \ tri_max = vertex[i].m_ax; \ - if (i == 0 || vertex[i].m_ax < tri_min) \ + if (vertex[i].m_ax < tri_min) \ tri_min = vertex[i].m_ax; \ } \ \ diff --git a/core/math/face3.h b/core/math/face3.h index faed0fa8d4..b41daf04d4 100644 --- a/core/math/face3.h +++ b/core/math/face3.h @@ -31,10 +31,10 @@ #ifndef FACE3_H #define FACE3_H -#include "aabb.h" -#include "plane.h" -#include "transform.h" -#include "vector3.h" +#include "core/math/aabb.h" +#include "core/math/plane.h" +#include "core/math/transform.h" +#include "core/math/vector3.h" class Face3 { public: diff --git a/core/math/geometry.cpp b/core/math/geometry.cpp index d8cb657b5e..be5e40e4e6 100644 --- a/core/math/geometry.cpp +++ b/core/math/geometry.cpp @@ -29,7 +29,8 @@ /*************************************************************************/ #include "geometry.h" -#include "print_string.h" + +#include "core/print_string.h" bool Geometry::is_point_in_polygon(const Vector2 &p_point, const Vector<Vector2> &p_polygon) { diff --git a/core/math/geometry.h b/core/math/geometry.h index 83b9467a30..a813a90774 100644 --- a/core/math/geometry.h +++ b/core/math/geometry.h @@ -31,14 +31,15 @@ #ifndef GEOMETRY_H #define GEOMETRY_H -#include "dvector.h" -#include "face3.h" -#include "object.h" -#include "print_string.h" -#include "rect2.h" -#include "triangulate.h" -#include "vector.h" -#include "vector3.h" +#include "core/dvector.h" +#include "core/math/face3.h" +#include "core/math/rect2.h" +#include "core/math/triangulate.h" +#include "core/math/vector3.h" +#include "core/object.h" +#include "core/print_string.h" +#include "core/vector.h" + /** @author Juan Linietsky <reduzio@gmail.com> */ diff --git a/core/helper/math_fieldwise.cpp b/core/math/math_fieldwise.cpp index ff3f8b3520..20b2341ab0 100644 --- a/core/helper/math_fieldwise.cpp +++ b/core/math/math_fieldwise.cpp @@ -30,7 +30,7 @@ #ifdef TOOLS_ENABLED -#include "core/helper/math_fieldwise.h" +#include "math_fieldwise.h" #define SETUP_TYPE(m_type) \ m_type source = p_source; \ diff --git a/core/helper/math_fieldwise.h b/core/math/math_fieldwise.h index 0e7cc3ea4a..0e7cc3ea4a 100644 --- a/core/helper/math_fieldwise.h +++ b/core/math/math_fieldwise.h diff --git a/core/math/math_funcs.cpp b/core/math/math_funcs.cpp index 5c8512d8bd..0c06d2a2b5 100644 --- a/core/math/math_funcs.cpp +++ b/core/math/math_funcs.cpp @@ -57,7 +57,7 @@ uint32_t Math::rand() { } int Math::step_decimals(double p_step) { - static const int maxn = 9; + static const int maxn = 10; static const double sd[maxn] = { 0.9999, // somehow compensate for floating point error 0.09999, @@ -67,17 +67,19 @@ int Math::step_decimals(double p_step) { 0.000009999, 0.0000009999, 0.00000009999, - 0.000000009999 + 0.000000009999, + 0.0000000009999 }; - double as = Math::abs(p_step); + double abs = Math::abs(p_step); + double decs = abs - (int)abs; // Strip away integer part for (int i = 0; i < maxn; i++) { - if (as >= sd[i]) { + if (decs >= sd[i]) { return i; } } - return maxn; + return 0; } double Math::dectime(double p_value, double p_amount, double p_step) { diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h index 992084a653..65c318448c 100644 --- a/core/math/math_funcs.h +++ b/core/math/math_funcs.h @@ -31,8 +31,8 @@ #ifndef MATH_FUNCS_H #define MATH_FUNCS_H -#include "math_defs.h" -#include "typedefs.h" +#include "core/math/math_defs.h" +#include "core/typedefs.h" #include "thirdparty/misc/pcg.h" @@ -46,7 +46,7 @@ class Math { public: Math() {} // useless to instance - static const uint64_t RANDOM_MAX = 4294967295; + static const uint64_t RANDOM_MAX = 0xFFFFFFFF; static _ALWAYS_INLINE_ double sin(double p_x) { return ::sin(p_x); } static _ALWAYS_INLINE_ float sin(float p_x) { return ::sinf(p_x); } @@ -305,16 +305,6 @@ public: return b; } -#if defined(__GNUC__) - - static _ALWAYS_INLINE_ int64_t dtoll(double p_double) { return (int64_t)p_double; } ///@TODO OPTIMIZE - static _ALWAYS_INLINE_ int64_t dtoll(float p_float) { return (int64_t)p_float; } ///@TODO OPTIMIZE and rename -#else - - static _ALWAYS_INLINE_ int64_t dtoll(double p_double) { return (int64_t)p_double; } ///@TODO OPTIMIZE - static _ALWAYS_INLINE_ int64_t dtoll(float p_float) { return (int64_t)p_float; } ///@TODO OPTIMIZE and rename -#endif - static _ALWAYS_INLINE_ uint32_t halfbits_to_floatbits(uint16_t h) { uint16_t h_exp, h_sig; uint32_t f_sgn, f_exp, f_sig; diff --git a/core/math/matrix3.cpp b/core/math/matrix3.cpp index 7db41756ed..925a7b3f1e 100644 --- a/core/math/matrix3.cpp +++ b/core/math/matrix3.cpp @@ -29,9 +29,11 @@ /*************************************************************************/ #include "matrix3.h" -#include "math_funcs.h" -#include "os/copymem.h" -#include "print_string.h" + +#include "core/math/math_funcs.h" +#include "core/os/copymem.h" +#include "core/print_string.h" + #define cofac(row1, col1, row2, col2) \ (elements[row1][col1] * elements[row2][col2] - elements[row1][col2] * elements[row2][col1]) @@ -297,14 +299,14 @@ Vector3 Basis::rotref_posscale_decomposition(Basis &rotref) const { ERR_FAIL_COND_V(determinant() == 0, Vector3()); Basis m = transposed() * (*this); - ERR_FAIL_COND_V(m.is_diagonal() == false, Vector3()); + ERR_FAIL_COND_V(!m.is_diagonal(), Vector3()); #endif Vector3 scale = get_scale(); Basis inv_scale = Basis().scaled(scale.inverse()); // this will also absorb the sign of scale rotref = (*this) * inv_scale; #ifdef MATH_CHECKS - ERR_FAIL_COND_V(rotref.is_orthogonal() == false, Vector3()); + ERR_FAIL_COND_V(!rotref.is_orthogonal(), Vector3()); #endif return scale.abs(); } @@ -428,7 +430,7 @@ Vector3 Basis::get_euler_xyz() const { Vector3 euler; #ifdef MATH_CHECKS - ERR_FAIL_COND_V(is_rotation() == false, euler); + ERR_FAIL_COND_V(!is_rotation(), euler); #endif real_t sy = elements[0][2]; if (sy < 1.0) { @@ -495,7 +497,7 @@ Vector3 Basis::get_euler_yxz() const { Vector3 euler; #ifdef MATH_CHECKS - ERR_FAIL_COND_V(is_rotation() == false, euler); + ERR_FAIL_COND_V(!is_rotation(), euler); #endif real_t m12 = elements[1][2]; @@ -554,7 +556,7 @@ bool Basis::is_equal_approx(const Basis &a, const Basis &b) const { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { - if (Math::is_equal_approx(a.elements[i][j], b.elements[i][j]) == false) + if (!Math::is_equal_approx(a.elements[i][j], b.elements[i][j])) return false; } } @@ -598,7 +600,7 @@ Basis::operator String() const { Quat Basis::get_quat() const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V(is_rotation() == false, Quat()); + ERR_FAIL_COND_V(!is_rotation(), Quat()); #endif real_t trace = elements[0][0] + elements[1][1] + elements[2][2]; real_t temp[4]; @@ -695,7 +697,7 @@ void Basis::set_orthogonal_index(int p_index) { void Basis::get_axis_angle(Vector3 &r_axis, real_t &r_angle) const { #ifdef MATH_CHECKS - ERR_FAIL_COND(is_rotation() == false); + ERR_FAIL_COND(!is_rotation()); #endif real_t angle, x, y, z; // variables for result real_t epsilon = 0.01; // margin to allow for rounding errors @@ -783,7 +785,7 @@ void Basis::set_quat(const Quat &p_quat) { void Basis::set_axis_angle(const Vector3 &p_axis, real_t p_phi) { // Rotation matrix from axis and angle, see https://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_angle #ifdef MATH_CHECKS - ERR_FAIL_COND(p_axis.is_normalized() == false); + ERR_FAIL_COND(!p_axis.is_normalized()); #endif Vector3 axis_sq(p_axis.x * p_axis.x, p_axis.y * p_axis.y, p_axis.z * p_axis.z); @@ -835,8 +837,8 @@ void Basis::set_diagonal(const Vector3 p_diag) { Basis Basis::slerp(const Basis &target, const real_t &t) const { // TODO: implement this directly without using quaternions to make it more efficient #ifdef MATH_CHECKS - ERR_FAIL_COND_V(is_rotation() == false, Basis()); - ERR_FAIL_COND_V(target.is_rotation() == false, Basis()); + ERR_FAIL_COND_V(!is_rotation(), Basis()); + ERR_FAIL_COND_V(!target.is_rotation(), Basis()); #endif Quat from(*this); diff --git a/core/math/matrix3.h b/core/math/matrix3.h index 9ff1a97dc9..35bf75bbe4 100644 --- a/core/math/matrix3.h +++ b/core/math/matrix3.h @@ -28,16 +28,18 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "vector3.h" +// Circular dependency between Vector3 and Basis :/ +#include "core/math/vector3.h" #ifndef MATRIX3_H #define MATRIX3_H -#include "quat.h" +#include "core/math/quat.h" /** @author Juan Linietsky <reduzio@gmail.com> */ + class Basis { public: Vector3 elements[3]; diff --git a/core/math/octree.h b/core/math/octree.h index 4e3d6257f0..cd89743a5a 100644 --- a/core/math/octree.h +++ b/core/math/octree.h @@ -31,12 +31,12 @@ #ifndef OCTREE_H #define OCTREE_H -#include "aabb.h" -#include "list.h" -#include "map.h" -#include "print_string.h" -#include "variant.h" -#include "vector3.h" +#include "core/list.h" +#include "core/map.h" +#include "core/math/aabb.h" +#include "core/math/vector3.h" +#include "core/print_string.h" +#include "core/variant.h" /** @author Juan Linietsky <reduzio@gmail.com> @@ -478,7 +478,7 @@ void Octree<T, use_pairs, AL>::_insert_element(Element *p_element, Octant *p_oct splits++; } } else { - /* check againt AABB where child should be */ + /* check against AABB where child should be */ AABB aabb = p_octant->aabb; aabb.size *= 0.5; diff --git a/core/math/plane.cpp b/core/math/plane.cpp index 78bb1771a4..3c597d57f8 100644 --- a/core/math/plane.cpp +++ b/core/math/plane.cpp @@ -30,7 +30,7 @@ #include "plane.h" -#include "math_funcs.h" +#include "core/math/math_funcs.h" #define _PLANE_EQ_DOT_EPSILON 0.999 #define _PLANE_EQ_D_EPSILON 0.0001 diff --git a/core/math/plane.h b/core/math/plane.h index e567422dd0..4eedebb79e 100644 --- a/core/math/plane.h +++ b/core/math/plane.h @@ -31,7 +31,7 @@ #ifndef PLANE_H #define PLANE_H -#include "vector3.h" +#include "core/math/vector3.h" class Plane { public: diff --git a/core/math/quat.cpp b/core/math/quat.cpp index 67c9048a41..791e84f089 100644 --- a/core/math/quat.cpp +++ b/core/math/quat.cpp @@ -29,8 +29,9 @@ /*************************************************************************/ #include "quat.h" -#include "matrix3.h" -#include "print_string.h" + +#include "core/math/matrix3.h" +#include "core/print_string.h" // set_euler_xyz expects a vector containing the Euler angles in the format // (ax,ay,az), where ax is the angle of rotation around x axis, @@ -99,7 +100,7 @@ void Quat::set_euler_yxz(const Vector3 &p_euler) { // This implementation uses YXZ convention (Z is the first rotation). Vector3 Quat::get_euler_yxz() const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V(is_normalized() == false, Vector3(0, 0, 0)); + ERR_FAIL_COND_V(!is_normalized(), Vector3(0, 0, 0)); #endif Basis m(*this); return m.get_euler_yxz(); @@ -134,20 +135,20 @@ Quat Quat::normalized() const { } bool Quat::is_normalized() const { - return Math::is_equal_approx(length(), 1.0); + return Math::is_equal_approx(length_squared(), 1.0); } Quat Quat::inverse() const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V(is_normalized() == false, Quat()); + ERR_FAIL_COND_V(!is_normalized(), Quat()); #endif return Quat(-x, -y, -z, w); } Quat Quat::slerp(const Quat &q, const real_t &t) const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V(is_normalized() == false, Quat()); - ERR_FAIL_COND_V(q.is_normalized() == false, Quat()); + ERR_FAIL_COND_V(!is_normalized(), Quat()); + ERR_FAIL_COND_V(!q.is_normalized(), Quat()); #endif Quat to1; real_t omega, cosom, sinom, scale0, scale1; @@ -193,8 +194,8 @@ Quat Quat::slerp(const Quat &q, const real_t &t) const { Quat Quat::slerpni(const Quat &q, const real_t &t) const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V(is_normalized() == false, Quat()); - ERR_FAIL_COND_V(q.is_normalized() == false, Quat()); + ERR_FAIL_COND_V(!is_normalized(), Quat()); + ERR_FAIL_COND_V(!q.is_normalized(), Quat()); #endif const Quat &from = *this; @@ -215,8 +216,8 @@ Quat Quat::slerpni(const Quat &q, const real_t &t) const { Quat Quat::cubic_slerp(const Quat &q, const Quat &prep, const Quat &postq, const real_t &t) const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V(is_normalized() == false, Quat()); - ERR_FAIL_COND_V(q.is_normalized() == false, Quat()); + ERR_FAIL_COND_V(!is_normalized(), Quat()); + ERR_FAIL_COND_V(!q.is_normalized(), Quat()); #endif //the only way to do slerp :| real_t t2 = (1.0 - t) * t * 2; @@ -232,7 +233,7 @@ Quat::operator String() const { void Quat::set_axis_angle(const Vector3 &axis, const real_t &angle) { #ifdef MATH_CHECKS - ERR_FAIL_COND(axis.is_normalized() == false); + ERR_FAIL_COND(!axis.is_normalized()); #endif real_t d = axis.length(); if (d == 0) diff --git a/core/math/quat.h b/core/math/quat.h index 6dc8d66f60..c4f9b3a732 100644 --- a/core/math/quat.h +++ b/core/math/quat.h @@ -28,18 +28,20 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "vector3.h" +// Circular dependency between Vector3 and Basis :/ +#include "core/math/vector3.h" #ifndef QUAT_H #define QUAT_H -#include "math_defs.h" -#include "math_funcs.h" -#include "ustring.h" +#include "core/math/math_defs.h" +#include "core/math/math_funcs.h" +#include "core/ustring.h" /** @author Juan Linietsky <reduzio@gmail.com> */ + class Quat { public: real_t x, y, z, w; @@ -85,7 +87,7 @@ public: _FORCE_INLINE_ Vector3 xform(const Vector3 &v) const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V(is_normalized() == false, v); + ERR_FAIL_COND_V(!is_normalized(), v); #endif Vector3 u(x, y, z); Vector3 uv = u.cross(v); diff --git a/core/math/quick_hull.cpp b/core/math/quick_hull.cpp index 9d4f4f66b7..23823b339a 100644 --- a/core/math/quick_hull.cpp +++ b/core/math/quick_hull.cpp @@ -29,7 +29,8 @@ /*************************************************************************/ #include "quick_hull.h" -#include "map.h" + +#include "core/map.h" uint32_t QuickHull::debug_stop_after = 0xFFFFFFFF; diff --git a/core/math/quick_hull.h b/core/math/quick_hull.h index eef4a9adff..0ac2758323 100644 --- a/core/math/quick_hull.h +++ b/core/math/quick_hull.h @@ -31,10 +31,10 @@ #ifndef QUICK_HULL_H #define QUICK_HULL_H -#include "aabb.h" -#include "geometry.h" -#include "list.h" -#include "set.h" +#include "core/list.h" +#include "core/math/aabb.h" +#include "core/math/geometry.h" +#include "core/set.h" class QuickHull { diff --git a/core/math/rect2.cpp b/core/math/rect2.cpp index 480bccdff1..24c1c8c984 100644 --- a/core/math/rect2.cpp +++ b/core/math/rect2.cpp @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "transform_2d.h" // Includes rect2.h but Rect2 needs Transform2D +#include "core/math/transform_2d.h" // Includes rect2.h but Rect2 needs Transform2D bool Rect2::intersects_segment(const Point2 &p_from, const Point2 &p_to, Point2 *r_pos, Point2 *r_normal) const { diff --git a/core/math/rect2.h b/core/math/rect2.h index 20329bee0d..96c0e177d3 100644 --- a/core/math/rect2.h +++ b/core/math/rect2.h @@ -31,7 +31,7 @@ #ifndef RECT2_H #define RECT2_H -#include "vector2.h" // also includes math_funcs and ustring +#include "core/math/vector2.h" // also includes math_funcs and ustring struct Transform2D; diff --git a/core/math/transform.cpp b/core/math/transform.cpp index 976e0f174e..75257a6e60 100644 --- a/core/math/transform.cpp +++ b/core/math/transform.cpp @@ -29,9 +29,10 @@ /*************************************************************************/ #include "transform.h" -#include "math_funcs.h" -#include "os/copymem.h" -#include "print_string.h" + +#include "core/math/math_funcs.h" +#include "core/os/copymem.h" +#include "core/print_string.h" void Transform::affine_invert() { diff --git a/core/math/transform.h b/core/math/transform.h index c06eaec604..97c8bf9ab0 100644 --- a/core/math/transform.h +++ b/core/math/transform.h @@ -31,12 +31,14 @@ #ifndef TRANSFORM_H #define TRANSFORM_H -#include "aabb.h" -#include "matrix3.h" -#include "plane.h" +#include "core/math/aabb.h" +#include "core/math/matrix3.h" +#include "core/math/plane.h" + /** @author Juan Linietsky <reduzio@gmail.com> */ + class Transform { public: Basis basis; diff --git a/core/math/transform_2d.h b/core/math/transform_2d.h index bf73755f0d..c8fc3c39e3 100644 --- a/core/math/transform_2d.h +++ b/core/math/transform_2d.h @@ -31,7 +31,7 @@ #ifndef TRANSFORM_2D_H #define TRANSFORM_2D_H -#include "rect2.h" // also includes vector2, math_funcs, and ustring +#include "core/math/rect2.h" // also includes vector2, math_funcs, and ustring struct Transform2D { // Warning #1: basis of Transform2D is stored differently from Basis. In terms of elements array, the basis matrix looks like "on paper": diff --git a/core/math/triangle_mesh.cpp b/core/math/triangle_mesh.cpp index 5475f733c3..6b8dc5eeb3 100644 --- a/core/math/triangle_mesh.cpp +++ b/core/math/triangle_mesh.cpp @@ -29,7 +29,8 @@ /*************************************************************************/ #include "triangle_mesh.h" -#include "sort.h" + +#include "core/sort.h" int TriangleMesh::_create_bvh(BVH *p_bvh, BVH **p_bb, int p_from, int p_size, int p_depth, int &max_depth, int &max_alloc) { diff --git a/core/math/triangle_mesh.h b/core/math/triangle_mesh.h index bf793fc50f..e5f181fba7 100644 --- a/core/math/triangle_mesh.h +++ b/core/math/triangle_mesh.h @@ -31,8 +31,9 @@ #ifndef TRIANGLE_MESH_H #define TRIANGLE_MESH_H -#include "face3.h" -#include "reference.h" +#include "core/math/face3.h" +#include "core/reference.h" + class TriangleMesh : public Reference { GDCLASS(TriangleMesh, Reference); diff --git a/core/math/triangulate.cpp b/core/math/triangulate.cpp index 0edc0ea039..69ffc95946 100644 --- a/core/math/triangulate.cpp +++ b/core/math/triangulate.cpp @@ -186,7 +186,7 @@ bool Triangulate::triangulate(const Vector<Vector2> &contour, Vector<int> &resul nv--; - /* resest error detection counter */ + /* reset error detection counter */ count = 2 * nv; } } diff --git a/core/math/triangulate.h b/core/math/triangulate.h index a0f56f5f27..2b0557ee55 100644 --- a/core/math/triangulate.h +++ b/core/math/triangulate.h @@ -31,7 +31,7 @@ #ifndef TRIANGULATE_H #define TRIANGULATE_H -#include "vector2.h" +#include "core/math/vector2.h" /* http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml diff --git a/core/math/vector2.cpp b/core/math/vector2.cpp index 84c9f0fca6..7c6f056f09 100644 --- a/core/math/vector2.cpp +++ b/core/math/vector2.cpp @@ -167,7 +167,7 @@ Vector2 Vector2::cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, c // slide returns the component of the vector along the given plane, specified by its normal vector. Vector2 Vector2::slide(const Vector2 &p_normal) const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V(p_normal.is_normalized() == false, Vector2()); + ERR_FAIL_COND_V(!p_normal.is_normalized(), Vector2()); #endif return *this - p_normal * this->dot(p_normal); } @@ -178,7 +178,7 @@ Vector2 Vector2::bounce(const Vector2 &p_normal) const { Vector2 Vector2::reflect(const Vector2 &p_normal) const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V(p_normal.is_normalized() == false, Vector2()); + ERR_FAIL_COND_V(!p_normal.is_normalized(), Vector2()); #endif return 2.0 * p_normal * this->dot(p_normal) - *this; } diff --git a/core/math/vector2.h b/core/math/vector2.h index fbcdc80b60..e5e555597d 100644 --- a/core/math/vector2.h +++ b/core/math/vector2.h @@ -31,8 +31,8 @@ #ifndef VECTOR2_H #define VECTOR2_H -#include "math_funcs.h" -#include "ustring.h" +#include "core/math/math_funcs.h" +#include "core/ustring.h" struct Vector2i; @@ -230,7 +230,7 @@ Vector2 Vector2::linear_interpolate(const Vector2 &p_b, real_t p_t) const { Vector2 Vector2::slerp(const Vector2 &p_b, real_t p_t) const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V(is_normalized() == false, Vector2()); + ERR_FAIL_COND_V(!is_normalized(), Vector2()); #endif real_t theta = angle_to(p_b); return rotated(theta * p_t); diff --git a/core/math/vector3.cpp b/core/math/vector3.cpp index 78d52d5cd1..5dbb01493d 100644 --- a/core/math/vector3.cpp +++ b/core/math/vector3.cpp @@ -29,7 +29,8 @@ /*************************************************************************/ #include "vector3.h" -#include "matrix3.h" + +#include "core/math/matrix3.h" void Vector3::rotate(const Vector3 &p_axis, real_t p_phi) { diff --git a/core/math/vector3.h b/core/math/vector3.h index 5f0e8919ff..16feba6a0c 100644 --- a/core/math/vector3.h +++ b/core/math/vector3.h @@ -31,10 +31,10 @@ #ifndef VECTOR3_H #define VECTOR3_H -#include "math_defs.h" -#include "math_funcs.h" -#include "typedefs.h" -#include "ustring.h" +#include "core/math/math_defs.h" +#include "core/math/math_funcs.h" +#include "core/typedefs.h" +#include "core/ustring.h" class Basis; @@ -150,13 +150,8 @@ struct Vector3 { } }; -#ifdef VECTOR3_IMPL_OVERRIDE - -#include "vector3_inline.h" - -#else - -#include "matrix3.h" +// Should be included after class definition, otherwise we get circular refs +#include "core/math/matrix3.h" Vector3 Vector3::cross(const Vector3 &p_b) const { @@ -223,7 +218,7 @@ Vector3 Vector3::linear_interpolate(const Vector3 &p_b, real_t p_t) const { Vector3 Vector3::slerp(const Vector3 &p_b, real_t p_t) const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V(is_normalized() == false, Vector3()); + ERR_FAIL_COND_V(!is_normalized(), Vector3()); #endif real_t theta = angle_to(p_b); @@ -435,7 +430,7 @@ void Vector3::zero() { // slide returns the component of the vector along the given plane, specified by its normal vector. Vector3 Vector3::slide(const Vector3 &p_normal) const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V(p_normal.is_normalized() == false, Vector3()); + ERR_FAIL_COND_V(!p_normal.is_normalized(), Vector3()); #endif return *this - p_normal * this->dot(p_normal); } @@ -446,11 +441,9 @@ Vector3 Vector3::bounce(const Vector3 &p_normal) const { Vector3 Vector3::reflect(const Vector3 &p_normal) const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V(p_normal.is_normalized() == false, Vector3()); + ERR_FAIL_COND_V(!p_normal.is_normalized(), Vector3()); #endif return 2.0 * p_normal * this->dot(p_normal) - *this; } -#endif - #endif // VECTOR3_H diff --git a/core/message_queue.cpp b/core/message_queue.cpp index 97ee236a46..abfc73407a 100644 --- a/core/message_queue.cpp +++ b/core/message_queue.cpp @@ -30,8 +30,8 @@ #include "message_queue.h" -#include "project_settings.h" -#include "script_language.h" +#include "core/project_settings.h" +#include "core/script_language.h" MessageQueue *MessageQueue::singleton = NULL; @@ -338,6 +338,7 @@ MessageQueue::MessageQueue() { buffer_end = 0; buffer_max_used = 0; buffer_size = GLOBAL_DEF_RST("memory/limits/message_queue/max_size_kb", DEFAULT_QUEUE_SIZE_KB); + ProjectSettings::get_singleton()->set_custom_property_info("memory/limits/message_queue/max_size_kb", PropertyInfo(Variant::INT, "memory/limits/message_queue/max_size_kb", PROPERTY_HINT_RANGE, "0,2048,1,or_greater")); buffer_size *= 1024; buffer = memnew_arr(uint8_t, buffer_size); } diff --git a/core/message_queue.h b/core/message_queue.h index be5ffe4fae..2083bb0639 100644 --- a/core/message_queue.h +++ b/core/message_queue.h @@ -31,9 +31,8 @@ #ifndef MESSAGE_QUEUE_H #define MESSAGE_QUEUE_H -#include "object.h" -#include "os/mutex.h" -#include "os/thread_safe.h" +#include "core/object.h" +#include "core/os/thread_safe.h" class MessageQueue { @@ -44,8 +43,6 @@ class MessageQueue { DEFAULT_QUEUE_SIZE_KB = 1024 }; - Mutex *mutex; - enum { TYPE_CALL, TYPE_NOTIFICATION, diff --git a/core/method_bind.cpp b/core/method_bind.cpp index 52ee9e0848..fa5b88b5ac 100644 --- a/core/method_bind.cpp +++ b/core/method_bind.cpp @@ -30,7 +30,7 @@ // object.h needs to be the first include *before* method_bind.h // FIXME: Find out why and fix potential cyclical dependencies. -#include "object.h" +#include "core/object.h" #include "method_bind.h" diff --git a/core/method_bind.h b/core/method_bind.h index 7ee687ee40..7788de919c 100644 --- a/core/method_bind.h +++ b/core/method_bind.h @@ -31,10 +31,11 @@ #ifndef METHOD_BIND_H #define METHOD_BIND_H -#include "list.h" -#include "method_ptrcall.h" -#include "object.h" -#include "variant.h" +#include "core/list.h" +#include "core/method_ptrcall.h" +#include "core/object.h" +#include "core/variant.h" + #include <stdio.h> /** @@ -45,7 +46,7 @@ #define DEBUG_METHODS_ENABLED #endif -#include "type_info.h" +#include "core/type_info.h" enum MethodFlags { diff --git a/core/method_ptrcall.h b/core/method_ptrcall.h index 6a33cf4d70..b706e65d53 100644 --- a/core/method_ptrcall.h +++ b/core/method_ptrcall.h @@ -31,9 +31,9 @@ #ifndef METHOD_PTRCALL_H #define METHOD_PTRCALL_H -#include "transform_2d.h" -#include "typedefs.h" -#include "variant.h" +#include "core/math/transform_2d.h" +#include "core/typedefs.h" +#include "core/variant.h" #ifdef PTRCALL_ENABLED diff --git a/core/node_path.cpp b/core/node_path.cpp index 7d4116fa1e..91e2aa5f4e 100644 --- a/core/node_path.cpp +++ b/core/node_path.cpp @@ -30,7 +30,7 @@ #include "node_path.h" -#include "print_string.h" +#include "core/print_string.h" void NodePath::_update_hash_cache() const { @@ -276,7 +276,7 @@ NodePath NodePath::get_as_property_path() const { String initial_subname = data->path[0]; - for (size_t i = 1; i < data->path.size(); i++) { + for (int i = 1; i < data->path.size(); i++) { initial_subname += "/" + data->path[i]; } new_path.insert(0, initial_subname); diff --git a/core/node_path.h b/core/node_path.h index 71235029af..4a72d4a4d5 100644 --- a/core/node_path.h +++ b/core/node_path.h @@ -31,8 +31,9 @@ #ifndef NODE_PATH_H #define NODE_PATH_H -#include "string_db.h" -#include "ustring.h" +#include "core/string_db.h" +#include "core/ustring.h" + /** @author Juan Linietsky <reduzio@gmail.com> */ diff --git a/core/oa_hash_map.h b/core/oa_hash_map.h index 3a17fc21f3..9840442519 100644 --- a/core/oa_hash_map.h +++ b/core/oa_hash_map.h @@ -31,10 +31,10 @@ #ifndef OA_HASH_MAP_H #define OA_HASH_MAP_H -#include "hashfuncs.h" -#include "math_funcs.h" -#include "os/copymem.h" -#include "os/memory.h" +#include "core/hashfuncs.h" +#include "core/math/math_funcs.h" +#include "core/os/copymem.h" +#include "core/os/memory.h" /** * A HashMap implementation that uses open addressing with robinhood hashing. @@ -125,7 +125,7 @@ private: while (42) { if (hashes[pos] == EMPTY_HASH) { - _construct(pos, hash, p_key, p_value); + _construct(pos, hash, key, value); return; } @@ -136,7 +136,7 @@ private: if (hashes[pos] & DELETED_HASH_BIT) { // we found a place where we can fit in! - _construct(pos, hash, p_key, p_value); + _construct(pos, hash, key, value); return; } @@ -166,7 +166,7 @@ private: values = memnew_arr(TValue, capacity); hashes = memnew_arr(uint32_t, capacity); - for (int i = 0; i < capacity; i++) { + for (uint32_t i = 0; i < capacity; i++) { hashes[i] = 0; } @@ -311,7 +311,7 @@ public: values = memnew_arr(TValue, p_initial_capacity); hashes = memnew_arr(uint32_t, p_initial_capacity); - for (int i = 0; i < p_initial_capacity; i++) { + for (uint32_t i = 0; i < p_initial_capacity; i++) { hashes[i] = 0; } } diff --git a/core/object.cpp b/core/object.cpp index e83abaece7..374e10726a 100644 --- a/core/object.cpp +++ b/core/object.cpp @@ -30,14 +30,14 @@ #include "object.h" -#include "class_db.h" -#include "core_string_names.h" -#include "message_queue.h" -#include "os/os.h" -#include "print_string.h" -#include "resource.h" -#include "script_language.h" -#include "translation.h" +#include "core/class_db.h" +#include "core/core_string_names.h" +#include "core/message_queue.h" +#include "core/os/os.h" +#include "core/print_string.h" +#include "core/resource.h" +#include "core/script_language.h" +#include "core/translation.h" #ifdef DEBUG_ENABLED @@ -277,8 +277,8 @@ MethodInfo::MethodInfo(Variant::Type ret, const String &p_name, const PropertyIn MethodInfo::MethodInfo(const PropertyInfo &p_ret, const String &p_name) : name(p_name), - flags(METHOD_FLAG_NORMAL), return_val(p_ret), + flags(METHOD_FLAG_NORMAL), id(0) { } @@ -440,16 +440,6 @@ void Object::set(const StringName &p_name, const Variant &p_value, bool *r_valid if (r_valid) *r_valid = true; return; -#ifdef TOOLS_ENABLED - } else if (p_name == CoreStringNames::get_singleton()->_sections_unfolded) { - Array arr = p_value; - for (int i = 0; i < arr.size(); i++) { - editor_section_folding.insert(arr[i]); - } - if (r_valid) - *r_valid = true; - return; -#endif } //something inside the object... :| @@ -520,16 +510,7 @@ Variant Object::get(const StringName &p_name, bool *r_valid) const { if (r_valid) *r_valid = true; return ret; -#ifdef TOOLS_ENABLED - } else if (p_name == CoreStringNames::get_singleton()->_sections_unfolded) { - Array array; - for (Set<String>::Element *E = editor_section_folding.front(); E; E = E->next()) { - array.push_back(E->get()); - } - if (r_valid) - *r_valid = true; - return array; -#endif + } else { //something inside the object... :| bool success = _getv(p_name, ret); @@ -657,11 +638,6 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons #endif p_list->push_back(PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NONZERO)); } -#ifdef TOOLS_ENABLED - if (editor_section_folding.size()) { - p_list->push_back(PropertyInfo(Variant::ARRAY, CoreStringNames::get_singleton()->_sections_unfolded, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); - } -#endif if (!metadata.empty()) p_list->push_back(PropertyInfo(Variant::DICTIONARY, "__meta__", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_STORE_IF_NONZERO)); if (script_instance && !p_reversed) { @@ -1254,7 +1230,10 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int target->call(c.method, args, argc, ce); if (ce.error != Variant::CallError::CALL_OK) { - +#ifdef DEBUG_ENABLED + if (c.flags & CONNECT_PERSIST && Engine::get_singleton()->is_editor_hint() && (script.is_null() || !Ref<Script>(script)->is_tool())) + continue; +#endif if (ce.error == Variant::CallError::CALL_ERROR_INVALID_METHOD && !ClassDB::class_exists(target->get_class_name())) { //most likely object is not initialized yet, do not throw error. } else { @@ -1916,7 +1895,11 @@ void *Object::get_script_instance_binding(int p_script_language_index) { //as it should not really affect performance much (won't be called too often), as in far most caes the condition below will be false afterwards if (!_script_instance_bindings[p_script_language_index]) { - _script_instance_bindings[p_script_language_index] = ScriptServer::get_language(p_script_language_index)->alloc_instance_binding_data(this); + void *script_data = ScriptServer::get_language(p_script_language_index)->alloc_instance_binding_data(this); + if (script_data) { + atomic_increment(&instance_binding_count); + _script_instance_bindings[p_script_language_index] = script_data; + } } return _script_instance_bindings[p_script_language_index]; @@ -1931,6 +1914,7 @@ Object::Object() { _instance_ID = ObjectDB::add_instance(this); _can_translate = true; _is_queued_for_deletion = false; + instance_binding_count = 0; memset(_script_instance_bindings, 0, sizeof(void *) * MAX_SCRIPT_INSTANCE_BINDINGS); script_instance = NULL; #ifdef TOOLS_ENABLED @@ -2009,11 +1993,13 @@ ObjectID ObjectDB::add_instance(Object *p_object) { ERR_FAIL_COND_V(p_object->get_instance_id() != 0, 0); rw_lock->write_lock(); - instances[++instance_counter] = p_object; - instance_checks[p_object] = instance_counter; + ObjectID instance_id = ++instance_counter; + instances[instance_id] = p_object; + instance_checks[p_object] = instance_id; + rw_lock->write_unlock(); - return instance_counter; + return instance_id; } void ObjectDB::remove_instance(Object *p_object) { @@ -2090,6 +2076,5 @@ void ObjectDB::cleanup() { instances.clear(); instance_checks.clear(); rw_lock->write_unlock(); - memdelete(rw_lock); } diff --git a/core/object.h b/core/object.h index d741371306..0fee7c2157 100644 --- a/core/object.h +++ b/core/object.h @@ -31,13 +31,13 @@ #ifndef OBJECT_H #define OBJECT_H -#include "hash_map.h" -#include "list.h" -#include "map.h" -#include "os/rw_lock.h" -#include "set.h" -#include "variant.h" -#include "vmap.h" +#include "core/hash_map.h" +#include "core/list.h" +#include "core/map.h" +#include "core/os/rw_lock.h" +#include "core/set.h" +#include "core/variant.h" +#include "core/vmap.h" #define VARIANT_ARG_LIST const Variant &p_arg1 = Variant(), const Variant &p_arg2 = Variant(), const Variant &p_arg3 = Variant(), const Variant &p_arg4 = Variant(), const Variant &p_arg5 = Variant() #define VARIANT_ARG_PASS p_arg1, p_arg2, p_arg3, p_arg4, p_arg5 @@ -116,6 +116,7 @@ enum PropertyUsageFlags { PROPERTY_USAGE_NIL_IS_VARIANT = 1 << 19, PROPERTY_USAGE_INTERNAL = 1 << 20, PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE = 1 << 21, // If the object is duplicated also this property will be duplicated + PROPERTY_USAGE_HIGH_END_GFX = 1 << 22, PROPERTY_USAGE_DEFAULT = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK, PROPERTY_USAGE_DEFAULT_INTL = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK | PROPERTY_USAGE_INTERNATIONALIZED, @@ -187,11 +188,11 @@ Array convert_property_list(const List<PropertyInfo> *p_list); struct MethodInfo { String name; - List<PropertyInfo> arguments; - Vector<Variant> default_arguments; PropertyInfo return_val; uint32_t flags; int id; + List<PropertyInfo> arguments; + Vector<Variant> default_arguments; inline bool operator==(const MethodInfo &p_method) const { return id == p_method.id; } inline bool operator<(const MethodInfo &p_method) const { return id == p_method.id ? (name < p_method.name) : (id < p_method.id); } @@ -317,7 +318,7 @@ protected: virtual void _initialize_classv() { \ initialize_class(); \ } \ - _FORCE_INLINE_ bool (Object::*(_get_get() const))(const StringName &p_name, Variant &) const { \ + _FORCE_INLINE_ bool (Object::*_get_get() const)(const StringName &p_name, Variant &) const { \ return (bool (Object::*)(const StringName &, Variant &) const) & m_class::_get; \ } \ virtual bool _getv(const StringName &p_name, Variant &r_ret) const { \ @@ -327,7 +328,7 @@ protected: } \ return m_inherits::_getv(p_name, r_ret); \ } \ - _FORCE_INLINE_ bool (Object::*(_get_set() const))(const StringName &p_name, const Variant &p_property) { \ + _FORCE_INLINE_ bool (Object::*_get_set() const)(const StringName &p_name, const Variant &p_property) { \ return (bool (Object::*)(const StringName &, const Variant &)) & m_class::_set; \ } \ virtual bool _setv(const StringName &p_name, const Variant &p_property) { \ @@ -337,7 +338,7 @@ protected: } \ return false; \ } \ - _FORCE_INLINE_ void (Object::*(_get_get_property_list() const))(List<PropertyInfo> * p_list) const { \ + _FORCE_INLINE_ void (Object::*_get_get_property_list() const)(List<PropertyInfo> * p_list) const { \ return (void (Object::*)(List<PropertyInfo> *) const) & m_class::_get_property_list; \ } \ virtual void _get_property_listv(List<PropertyInfo> *p_list, bool p_reversed) const { \ @@ -356,7 +357,7 @@ protected: m_inherits::_get_property_listv(p_list, p_reversed); \ } \ } \ - _FORCE_INLINE_ void (Object::*(_get_notification() const))(int) { \ + _FORCE_INLINE_ void (Object::*_get_notification() const)(int) { \ return (void (Object::*)(int)) & m_class::_notification; \ } \ virtual void _notificationv(int p_notification, bool p_reversed) { \ @@ -421,7 +422,7 @@ private: }; #ifdef DEBUG_ENABLED - friend class _ObjectDebugLock; + friend struct _ObjectDebugLock; #endif friend bool predelete_handler(Object *); friend void postinitialize_handler(Object *); @@ -490,10 +491,12 @@ private: void _set_indexed_bind(const NodePath &p_name, const Variant &p_value); Variant _get_indexed_bind(const NodePath &p_name) const; - void *_script_instance_bindings[MAX_SCRIPT_INSTANCE_BINDINGS]; - void property_list_changed_notify(); + friend class Reference; + uint32_t instance_binding_count; + void *_script_instance_bindings[MAX_SCRIPT_INSTANCE_BINDINGS]; + protected: virtual void _initialize_classv() { initialize_class(); } virtual bool _setv(const StringName &p_name, const Variant &p_property) { return false; }; @@ -511,16 +514,16 @@ protected: _FORCE_INLINE_ static void (*_get_bind_methods())() { return &Object::_bind_methods; } - _FORCE_INLINE_ bool (Object::*(_get_get() const))(const StringName &p_name, Variant &r_ret) const { + _FORCE_INLINE_ bool (Object::*_get_get() const)(const StringName &p_name, Variant &r_ret) const { return &Object::_get; } - _FORCE_INLINE_ bool (Object::*(_get_set() const))(const StringName &p_name, const Variant &p_property) { + _FORCE_INLINE_ bool (Object::*_get_set() const)(const StringName &p_name, const Variant &p_property) { return &Object::_set; } - _FORCE_INLINE_ void (Object::*(_get_get_property_list() const))(List<PropertyInfo> *p_list) const { + _FORCE_INLINE_ void (Object::*_get_get_property_list() const)(List<PropertyInfo> *p_list) const { return &Object::_get_property_list; } - _FORCE_INLINE_ void (Object::*(_get_notification() const))(int) { + _FORCE_INLINE_ void (Object::*_get_notification() const)(int) { return &Object::_notification; } static void get_valid_parents_static(List<String> *p_parents); @@ -720,6 +723,9 @@ public: #ifdef TOOLS_ENABLED void editor_set_section_unfold(const String &p_section, bool p_unfolded); bool editor_is_section_unfolded(const String &p_section); + const Set<String> &editor_get_section_folding() const { return editor_section_folding; } + void editor_clear_section_folding() { editor_section_folding.clear(); } + #endif //used by script languages to store binding data @@ -777,6 +783,6 @@ public: }; //needed by macros -#include "class_db.h" +#include "core/class_db.h" #endif diff --git a/core/ordered_hash_map.h b/core/ordered_hash_map.h index 93ce9a90a7..092bf314d0 100644 --- a/core/ordered_hash_map.h +++ b/core/ordered_hash_map.h @@ -31,9 +31,9 @@ #ifndef ORDERED_HASH_MAP_H #define ORDERED_HASH_MAP_H -#include "hash_map.h" -#include "list.h" -#include "pair.h" +#include "core/hash_map.h" +#include "core/list.h" +#include "core/pair.h" /** * A hash map which allows to iterate elements in insertion order. diff --git a/core/os/SCsub b/core/os/SCsub index 4efc902717..1c5f954470 100644 --- a/core/os/SCsub +++ b/core/os/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.core_sources, "*.cpp") - -Export('env') diff --git a/core/os/copymem.h b/core/os/copymem.h index 87d77bd426..999c770e85 100644 --- a/core/os/copymem.h +++ b/core/os/copymem.h @@ -31,7 +31,7 @@ #ifndef COPYMEM_H #define COPYMEM_H -#include "typedefs.h" +#include "core/typedefs.h" #ifdef PLATFORM_COPYMEM diff --git a/core/os/dir_access.cpp b/core/os/dir_access.cpp index e631d6e994..daa3eacd5f 100644 --- a/core/os/dir_access.cpp +++ b/core/os/dir_access.cpp @@ -29,10 +29,11 @@ /*************************************************************************/ #include "dir_access.h" -#include "os/file_access.h" -#include "os/memory.h" -#include "os/os.h" -#include "project_settings.h" + +#include "core/os/file_access.h" +#include "core/os/memory.h" +#include "core/os/os.h" +#include "core/project_settings.h" String DirAccess::_get_root_path() const { @@ -226,6 +227,7 @@ String DirAccess::fix_path(String p_path) const { return p_path; } break; + case ACCESS_MAX: break; // Can't happen, but silences warning } return p_path; diff --git a/core/os/dir_access.h b/core/os/dir_access.h index 4df0618021..773f0bcd41 100644 --- a/core/os/dir_access.h +++ b/core/os/dir_access.h @@ -31,14 +31,14 @@ #ifndef DIR_ACCESS_H #define DIR_ACCESS_H -#include "typedefs.h" -#include "ustring.h" +#include "core/typedefs.h" +#include "core/ustring.h" /** @author Juan Linietsky <reduzio@gmail.com> */ -//@ TOOD, excellent candidate for THREAD_SAFE MACRO, should go through all these and add THREAD_SAFE where it applies +//@ TODO, excellent candidate for THREAD_SAFE MACRO, should go through all these and add THREAD_SAFE where it applies class DirAccess { public: enum AccessType { diff --git a/core/os/file_access.cpp b/core/os/file_access.cpp index 59f07c03e7..e09e5e16ad 100644 --- a/core/os/file_access.cpp +++ b/core/os/file_access.cpp @@ -32,8 +32,8 @@ #include "core/io/file_access_pack.h" #include "core/io/marshalls.h" -#include "os/os.h" -#include "project_settings.h" +#include "core/os/os.h" +#include "core/project_settings.h" #include "thirdparty/misc/md5.h" #include "thirdparty/misc/sha256.h" @@ -46,7 +46,6 @@ bool FileAccess::backup_save = false; FileAccess *FileAccess::create(AccessType p_access) { - ERR_FAIL_COND_V(!create_func, 0); ERR_FAIL_INDEX_V(p_access, ACCESS_MAX, 0); FileAccess *ret = create_func[p_access](); @@ -166,6 +165,7 @@ String FileAccess::fix_path(const String &p_path) const { return r_path; } break; + case ACCESS_MAX: break; // Can't happen, but silences warning } return r_path; diff --git a/core/os/file_access.h b/core/os/file_access.h index c4635fdfbb..b7d93e9f5d 100644 --- a/core/os/file_access.h +++ b/core/os/file_access.h @@ -31,10 +31,11 @@ #ifndef FILE_ACCESS_H #define FILE_ACCESS_H -#include "math_defs.h" -#include "os/memory.h" -#include "typedefs.h" -#include "ustring.h" +#include "core/math/math_defs.h" +#include "core/os/memory.h" +#include "core/typedefs.h" +#include "core/ustring.h" + /** * Multi-Platform abstraction for accessing to files. */ diff --git a/core/os/input.cpp b/core/os/input.cpp index a5b0f91e63..4cd1f0b24a 100644 --- a/core/os/input.cpp +++ b/core/os/input.cpp @@ -29,9 +29,11 @@ /*************************************************************************/ #include "input.h" -#include "input_map.h" -#include "os/os.h" -#include "project_settings.h" + +#include "core/input_map.h" +#include "core/os/os.h" +#include "core/project_settings.h" + Input *Input::singleton = NULL; Input *Input::get_singleton() { @@ -40,7 +42,7 @@ Input *Input::get_singleton() { } void Input::set_mouse_mode(MouseMode p_mode) { - ERR_FAIL_INDEX(p_mode, 4); + ERR_FAIL_INDEX((int)p_mode, 4); OS::get_singleton()->set_mouse_mode((OS::MouseMode)p_mode); } diff --git a/core/os/input.h b/core/os/input.h index 001871c5dc..db523d6789 100644 --- a/core/os/input.h +++ b/core/os/input.h @@ -31,9 +31,9 @@ #ifndef INPUT_H #define INPUT_H -#include "object.h" -#include "os/main_loop.h" -#include "os/thread_safe.h" +#include "core/object.h" +#include "core/os/main_loop.h" +#include "core/os/thread_safe.h" class Input : public Object { diff --git a/core/os/input_event.cpp b/core/os/input_event.cpp index 12c6ef7d3b..5bbdd7efb2 100644 --- a/core/os/input_event.cpp +++ b/core/os/input_event.cpp @@ -30,8 +30,8 @@ #include "input_event.h" -#include "input_map.h" -#include "os/keyboard.h" +#include "core/input_map.h" +#include "core/os/keyboard.h" void InputEvent::set_device(int p_device) { device = p_device; @@ -962,6 +962,22 @@ bool InputEventAction::is_action(const StringName &p_action) const { return action == p_action; } +bool InputEventAction::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const { + + Ref<InputEventAction> act = p_event; + if (act.is_null()) + return false; + + bool match = action == act->action; + if (match) { + if (p_pressed != NULL) + *p_pressed = act->pressed; + if (p_strength != NULL) + *p_strength = (*p_pressed) ? 1.0f : 0.0f; + } + return match; +} + String InputEventAction::as_text() const { return "InputEventAction : action=" + action + ", pressed=(" + (pressed ? "true" : "false"); diff --git a/core/os/input_event.h b/core/os/input_event.h index 8732c7e377..789d19c5b2 100644 --- a/core/os/input_event.h +++ b/core/os/input_event.h @@ -31,11 +31,12 @@ #ifndef INPUT_EVENT_H #define INPUT_EVENT_H -#include "os/copymem.h" -#include "resource.h" -#include "transform_2d.h" -#include "typedefs.h" -#include "ustring.h" +#include "core/math/transform_2d.h" +#include "core/os/copymem.h" +#include "core/resource.h" +#include "core/typedefs.h" +#include "core/ustring.h" + /** @author Juan Linietsky <reduzio@gmail.com> */ @@ -480,6 +481,8 @@ public: virtual bool is_action(const StringName &p_action) const; + virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const; + virtual bool shortcut_match(const Ref<InputEvent> &p_event) const; virtual bool is_action_type() const { return true; } virtual String as_text() const; diff --git a/core/os/keyboard.cpp b/core/os/keyboard.cpp index 9dfc91e308..abc579c58d 100644 --- a/core/os/keyboard.cpp +++ b/core/os/keyboard.cpp @@ -29,7 +29,8 @@ /*************************************************************************/ #include "keyboard.h" -#include "os/os.h" + +#include "core/os/os.h" struct _KeyCodeText { int code; diff --git a/core/os/keyboard.h b/core/os/keyboard.h index a0e6f8b2ef..17bf5eaa48 100644 --- a/core/os/keyboard.h +++ b/core/os/keyboard.h @@ -31,13 +31,10 @@ #ifndef KEYBOARD_H #define KEYBOARD_H -#include "ustring.h" -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ +#include "core/ustring.h" /** -@author Juan Linietsky <reduzio@gmail.com> + @author Juan Linietsky <reduzio@gmail.com> */ /* diff --git a/core/os/main_loop.cpp b/core/os/main_loop.cpp index c51801e3e2..0945cdd512 100644 --- a/core/os/main_loop.cpp +++ b/core/os/main_loop.cpp @@ -29,18 +29,19 @@ /*************************************************************************/ #include "main_loop.h" -#include "script_language.h" + +#include "core/script_language.h" void MainLoop::_bind_methods() { - ClassDB::bind_method(D_METHOD("input_event", "ev"), &MainLoop::input_event); + ClassDB::bind_method(D_METHOD("input_event", "event"), &MainLoop::input_event); ClassDB::bind_method(D_METHOD("input_text", "text"), &MainLoop::input_text); ClassDB::bind_method(D_METHOD("init"), &MainLoop::init); ClassDB::bind_method(D_METHOD("iteration", "delta"), &MainLoop::iteration); ClassDB::bind_method(D_METHOD("idle", "delta"), &MainLoop::idle); ClassDB::bind_method(D_METHOD("finish"), &MainLoop::finish); - BIND_VMETHOD(MethodInfo("_input_event", PropertyInfo(Variant::OBJECT, "ev", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"))); + BIND_VMETHOD(MethodInfo("_input_event", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"))); BIND_VMETHOD(MethodInfo("_input_text", PropertyInfo(Variant::STRING, "text"))); BIND_VMETHOD(MethodInfo("_initialize")); BIND_VMETHOD(MethodInfo("_iteration", PropertyInfo(Variant::REAL, "delta"))); diff --git a/core/os/main_loop.h b/core/os/main_loop.h index f96e46141e..43f74302a8 100644 --- a/core/os/main_loop.h +++ b/core/os/main_loop.h @@ -31,12 +31,14 @@ #ifndef MAIN_LOOP_H #define MAIN_LOOP_H -#include "os/input_event.h" -#include "reference.h" -#include "script_language.h" +#include "core/os/input_event.h" +#include "core/reference.h" +#include "core/script_language.h" + /** @author Juan Linietsky <reduzio@gmail.com> */ + class MainLoop : public Object { GDCLASS(MainLoop, Object); diff --git a/core/os/memory.cpp b/core/os/memory.cpp index 3eab4343a9..f25e40ef78 100644 --- a/core/os/memory.cpp +++ b/core/os/memory.cpp @@ -29,9 +29,11 @@ /*************************************************************************/ #include "memory.h" -#include "copymem.h" + +#include "core/error_macros.h" +#include "core/os/copymem.h" #include "core/safe_refcount.h" -#include "error_macros.h" + #include <stdio.h> #include <stdlib.h> @@ -170,9 +172,9 @@ void Memory::free_static(void *p_ptr, bool p_pad_align) { if (prepad) { mem -= PAD_ALIGN; - uint64_t *s = (uint64_t *)mem; #ifdef DEBUG_ENABLED + uint64_t *s = (uint64_t *)mem; atomic_sub(&mem_usage, *s); #endif diff --git a/core/os/memory.h b/core/os/memory.h index f5c6c0b38a..3501c3f14e 100644 --- a/core/os/memory.h +++ b/core/os/memory.h @@ -31,7 +31,8 @@ #ifndef MEMORY_H #define MEMORY_H -#include "safe_refcount.h" +#include "core/safe_refcount.h" + #include <stddef.h> /** diff --git a/core/os/midi_driver.cpp b/core/os/midi_driver.cpp index 7b4f84473c..2b20a708ed 100644 --- a/core/os/midi_driver.cpp +++ b/core/os/midi_driver.cpp @@ -30,8 +30,8 @@ #include "midi_driver.h" +#include "core/os/os.h" #include "main/input_default.h" -#include "os/os.h" MIDIDriver *MIDIDriver::singleton = NULL; MIDIDriver *MIDIDriver::get_singleton() { diff --git a/core/os/midi_driver.h b/core/os/midi_driver.h index 1a3a67a411..ceb4e71d66 100644 --- a/core/os/midi_driver.h +++ b/core/os/midi_driver.h @@ -31,8 +31,9 @@ #ifndef MIDI_DRIVER_H #define MIDI_DRIVER_H +#include "core/typedefs.h" #include "core/variant.h" -#include "typedefs.h" + /** * Multi-Platform abstraction for accessing to MIDI. */ diff --git a/core/os/mutex.cpp b/core/os/mutex.cpp index 7c4ea2323c..0c01b06bd6 100644 --- a/core/os/mutex.cpp +++ b/core/os/mutex.cpp @@ -29,7 +29,9 @@ /*************************************************************************/ #include "mutex.h" -#include "error_macros.h" + +#include "core/error_macros.h" + #include <stddef.h> Mutex *(*Mutex::create_func)(bool) = 0; diff --git a/core/os/mutex.h b/core/os/mutex.h index 9debe7f41b..788cc00397 100644 --- a/core/os/mutex.h +++ b/core/os/mutex.h @@ -31,7 +31,7 @@ #ifndef MUTEX_H #define MUTEX_H -#include "error_list.h" +#include "core/error_list.h" /** * @class Mutex diff --git a/core/os/os.cpp b/core/os/os.cpp index e90d714450..7547b6a042 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -30,13 +30,13 @@ #include "os.h" -#include "dir_access.h" -#include "input.h" -#include "os/file_access.h" -#include "os/midi_driver.h" -#include "project_settings.h" +#include "core/os/dir_access.h" +#include "core/os/file_access.h" +#include "core/os/input.h" +#include "core/os/midi_driver.h" +#include "core/project_settings.h" +#include "core/version_generated.gen.h" #include "servers/audio_server.h" -#include "version_generated.gen.h" #include <stdarg.h> @@ -632,10 +632,13 @@ void OS::center_window() { if (is_window_fullscreen()) return; + Point2 sp = get_screen_position(get_current_screen()); Size2 scr = get_screen_size(get_current_screen()); Size2 wnd = get_real_window_size(); - int x = scr.width / 2 - wnd.width / 2; - int y = scr.height / 2 - wnd.height / 2; + + int x = sp.width + (scr.width - wnd.width) / 2; + int y = sp.height + (scr.height - wnd.height) / 2; + set_window_position(Vector2(x, y)); } diff --git a/core/os/os.h b/core/os/os.h index 6f9a72d451..7786ffb26e 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -31,13 +31,14 @@ #ifndef OS_H #define OS_H -#include "engine.h" -#include "image.h" -#include "io/logger.h" -#include "list.h" -#include "os/main_loop.h" -#include "ustring.h" -#include "vector.h" +#include "core/engine.h" +#include "core/image.h" +#include "core/io/logger.h" +#include "core/list.h" +#include "core/os/main_loop.h" +#include "core/ustring.h" +#include "core/vector.h" + #include <stdarg.h> /** @@ -128,7 +129,7 @@ protected: RenderThreadMode _render_thread_mode; - // functions used by main to initialize/deintialize the OS + // functions used by main to initialize/deinitialize the OS void add_logger(Logger *p_logger); virtual void initialize_core() = 0; @@ -256,7 +257,7 @@ public: virtual String get_executable_path() const; virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false) = 0; - virtual Error kill(const ProcessID &p_pid, const int p_max_wait_msec = -1) = 0; + virtual Error kill(const ProcessID &p_pid) = 0; virtual int get_process_id() const; virtual Error shell_open(String p_uri); diff --git a/core/os/rw_lock.cpp b/core/os/rw_lock.cpp index 35489490ed..5e51a1dbce 100644 --- a/core/os/rw_lock.cpp +++ b/core/os/rw_lock.cpp @@ -30,7 +30,7 @@ #include "rw_lock.h" -#include "error_macros.h" +#include "core/error_macros.h" #include <stddef.h> diff --git a/core/os/rw_lock.h b/core/os/rw_lock.h index 3e53300c9f..4333d9a016 100644 --- a/core/os/rw_lock.h +++ b/core/os/rw_lock.h @@ -31,7 +31,7 @@ #ifndef RWLOCK_H #define RWLOCK_H -#include "error_list.h" +#include "core/error_list.h" class RWLock { protected: @@ -57,9 +57,7 @@ class RWLockRead { public: RWLockRead(const RWLock *p_lock) { - if (p_lock) { - lock = const_cast<RWLock *>(p_lock); - } + lock = const_cast<RWLock *>(p_lock); if (lock) lock->read_lock(); } ~RWLockRead() { diff --git a/core/os/semaphore.cpp b/core/os/semaphore.cpp index 0377aeeb29..448d17dd14 100644 --- a/core/os/semaphore.cpp +++ b/core/os/semaphore.cpp @@ -29,7 +29,8 @@ /*************************************************************************/ #include "semaphore.h" -#include "error_macros.h" + +#include "core/error_macros.h" Semaphore *(*Semaphore::create_func)() = 0; diff --git a/core/os/semaphore.h b/core/os/semaphore.h index f3021bf74c..6cec06ea28 100644 --- a/core/os/semaphore.h +++ b/core/os/semaphore.h @@ -31,11 +31,12 @@ #ifndef SEMAPHORE_H #define SEMAPHORE_H -#include "error_list.h" +#include "core/error_list.h" /** @author Juan Linietsky <reduzio@gmail.com> */ + class Semaphore { protected: static Semaphore *(*create_func)(); diff --git a/core/os/shell.h b/core/os/shell.h index d3d92028ea..146c35cbc8 100644 --- a/core/os/shell.h +++ b/core/os/shell.h @@ -31,8 +31,8 @@ #ifndef SHELL_H #define SHELL_H -#include "typedefs.h" -#include "ustring.h" +#include "core/typedefs.h" +#include "core/ustring.h" /** @author Juan Linietsky <reduzio@gmail.com> diff --git a/core/os/thread.h b/core/os/thread.h index c2947bccab..97e97652a5 100644 --- a/core/os/thread.h +++ b/core/os/thread.h @@ -31,13 +31,13 @@ #ifndef THREAD_H #define THREAD_H -#include "typedefs.h" +#include "core/typedefs.h" +#include "core/ustring.h" + /** @author Juan Linietsky <reduzio@gmail.com> */ -#include "ustring.h" - typedef void (*ThreadCreateCallback)(void *p_userdata); class Thread { diff --git a/core/os/thread_dummy.cpp b/core/os/thread_dummy.cpp index b6371235c4..9e0adf9994 100644 --- a/core/os/thread_dummy.cpp +++ b/core/os/thread_dummy.cpp @@ -30,7 +30,7 @@ #include "thread_dummy.h" -#include "memory.h" +#include "core/os/memory.h" Thread *ThreadDummy::create(ThreadCreateCallback p_callback, void *p_user, const Thread::Settings &p_settings) { return memnew(ThreadDummy); diff --git a/core/os/thread_dummy.h b/core/os/thread_dummy.h index 74957b95fe..6f46a4a20c 100644 --- a/core/os/thread_dummy.h +++ b/core/os/thread_dummy.h @@ -31,10 +31,10 @@ #ifndef THREAD_DUMMY_H #define THREAD_DUMMY_H -#include "mutex.h" -#include "rw_lock.h" -#include "semaphore.h" -#include "thread.h" +#include "core/os/mutex.h" +#include "core/os/rw_lock.h" +#include "core/os/semaphore.h" +#include "core/os/thread.h" class ThreadDummy : public Thread { diff --git a/core/os/thread_safe.cpp b/core/os/thread_safe.cpp index acb37df02b..3fe039e1b6 100644 --- a/core/os/thread_safe.cpp +++ b/core/os/thread_safe.cpp @@ -29,8 +29,9 @@ /*************************************************************************/ #include "thread_safe.h" -#include "error_macros.h" -#include "os/memory.h" + +#include "core/error_macros.h" +#include "core/os/memory.h" ThreadSafe::ThreadSafe() { diff --git a/core/os/thread_safe.h b/core/os/thread_safe.h index f0876f38a1..a17ceeed1d 100644 --- a/core/os/thread_safe.h +++ b/core/os/thread_safe.h @@ -31,7 +31,7 @@ #ifndef THREAD_SAFE_H #define THREAD_SAFE_H -#include "os/mutex.h" +#include "core/os/mutex.h" class ThreadSafe { diff --git a/core/os/threaded_array_processor.h b/core/os/threaded_array_processor.h index 3ff7db2a44..89c3b7cec3 100644 --- a/core/os/threaded_array_processor.h +++ b/core/os/threaded_array_processor.h @@ -31,11 +31,11 @@ #ifndef THREADED_ARRAY_PROCESSOR_H #define THREADED_ARRAY_PROCESSOR_H -#include "os/mutex.h" -#include "os/os.h" -#include "os/thread.h" -#include "safe_refcount.h" -#include "thread_safe.h" +#include "core/os/mutex.h" +#include "core/os/os.h" +#include "core/os/thread.h" +#include "core/os/thread_safe.h" +#include "core/safe_refcount.h" template <class C, class U> struct ThreadArrayProcessData { diff --git a/core/packed_data_container.cpp b/core/packed_data_container.cpp index 45e060fa4a..e4ca8fafa5 100644 --- a/core/packed_data_container.cpp +++ b/core/packed_data_container.cpp @@ -30,8 +30,8 @@ #include "packed_data_container.h" -#include "core_string_names.h" -#include "io/marshalls.h" +#include "core/core_string_names.h" +#include "core/io/marshalls.h" Variant PackedDataContainer::getvar(const Variant &p_key, bool *r_valid) const { diff --git a/core/packed_data_container.h b/core/packed_data_container.h index fe36417000..0e1dff1f70 100644 --- a/core/packed_data_container.h +++ b/core/packed_data_container.h @@ -31,7 +31,7 @@ #ifndef PACKED_DATA_CONTAINER_H #define PACKED_DATA_CONTAINER_H -#include "resource.h" +#include "core/resource.h" class PackedDataContainer : public Resource { diff --git a/core/pool_allocator.cpp b/core/pool_allocator.cpp index 8952314212..6997c993f4 100644 --- a/core/pool_allocator.cpp +++ b/core/pool_allocator.cpp @@ -30,11 +30,11 @@ #include "pool_allocator.h" +#include "core/error_macros.h" +#include "core/os/copymem.h" +#include "core/os/memory.h" #include "core/os/os.h" -#include "error_macros.h" -#include "os/copymem.h" -#include "os/memory.h" -#include "print_string.h" +#include "core/print_string.h" #include <assert.h> diff --git a/core/pool_allocator.h b/core/pool_allocator.h index d9731aa3eb..12e2b597db 100644 --- a/core/pool_allocator.h +++ b/core/pool_allocator.h @@ -31,7 +31,7 @@ #ifndef POOL_ALLOCATOR_H #define POOL_ALLOCATOR_H -#include "typedefs.h" +#include "core/typedefs.h" /** @author Juan Linietsky <reduzio@gmail.com> diff --git a/core/print_string.cpp b/core/print_string.cpp index e1e42d2b56..f53d50e078 100644 --- a/core/print_string.cpp +++ b/core/print_string.cpp @@ -30,7 +30,7 @@ #include "print_string.h" -#include "os/os.h" +#include "core/os/os.h" #include <stdio.h> diff --git a/core/print_string.h b/core/print_string.h index c1d2d0ff3a..8f4fbb1f7b 100644 --- a/core/print_string.h +++ b/core/print_string.h @@ -31,7 +31,7 @@ #ifndef PRINT_STRING_H #define PRINT_STRING_H -#include "ustring.h" +#include "core/ustring.h" extern void (*_print_func)(String); diff --git a/core/project_settings.cpp b/core/project_settings.cpp index 890789ec6f..407bb78375 100644 --- a/core/project_settings.cpp +++ b/core/project_settings.cpp @@ -30,16 +30,17 @@ #include "project_settings.h" -#include "bind/core_bind.h" -#include "core_string_names.h" -#include "io/file_access_network.h" -#include "io/file_access_pack.h" -#include "io/marshalls.h" -#include "os/dir_access.h" -#include "os/file_access.h" -#include "os/keyboard.h" -#include "os/os.h" -#include "variant_parser.h" +#include "core/bind/core_bind.h" +#include "core/core_string_names.h" +#include "core/io/file_access_network.h" +#include "core/io/file_access_pack.h" +#include "core/io/marshalls.h" +#include "core/os/dir_access.h" +#include "core/os/file_access.h" +#include "core/os/keyboard.h" +#include "core/os/os.h" +#include "core/variant_parser.h" + #include <zlib.h> #define FORMAT_VERSION 4 @@ -59,7 +60,7 @@ String ProjectSettings::get_resource_path() const { String ProjectSettings::localize_path(const String &p_path) const { if (resource_path == "") - return p_path; //not initialied yet + return p_path; //not initialized yet if (p_path.begins_with("res://") || p_path.begins_with("user://") || (p_path.is_abs_path() && !p_path.begins_with(resource_path))) @@ -287,9 +288,28 @@ void ProjectSettings::_convert_to_last_version() { } } +/* + * This method is responsible for loading a project.godot file and/or data file + * using the following merit order: + * - If using NetworkClient, try to lookup project file or fail. + * - If --main-pack was passed by the user (`p_main_pack`), load it or fail. + * - Search for .pck file matching binary name. There are two possibilities: + * o exec_path.get_basename() + '.pck' (e.g. 'win_game.exe' -> 'win_game.pck') + * o exec_path + '.pck' (e.g. 'linux_game' -> 'linux_game.pck') + * For each tentative, if the file exists, load it or fail. + * - On relevant platforms (Android/iOS), lookup project file in OS resource path. + * If found, load it or fail. + * - Lookup project file in passed `p_path` (--path passed by the user), i.e. we + * are running from source code. + * If not found and `p_upwards` is true (--upwards passed by the user), look for + * project files in parent folders up to the system root (used to run a game + * from command line while in a subfolder). + * If a project file is found, load it or fail. + * If nothing was found, error out. + */ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bool p_upwards) { - //If looking for files in network, just use network! + // If looking for files in a network client, use it directly if (FileAccessNetworkClient::get_singleton()) { @@ -301,9 +321,7 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo return err; } - String exec_path = OS::get_singleton()->get_executable_path(); - - //Attempt with a passed main pack first + // Attempt with a user-defined main pack first if (p_main_pack != "") { @@ -319,25 +337,39 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo return err; } - //Attempt with execname.pck + // Attempt with exec_name.pck + // (This is the usual case when distributing a Godot game.) + + // Based on the OS, it can be the exec path + '.pck' (Linux w/o extension, macOS in .app bundle) + // or the exec path's basename + '.pck' (Windows). + // We need to test both possibilities as extensions for Linux binaries are optional + // (so both 'mygame.bin' and 'mygame' should be able to find 'mygame.pck'). + + String exec_path = OS::get_singleton()->get_executable_path(); + if (exec_path != "") { bool found = false; - // get our filename without our path (note, using exec_path.get_file before get_basename anymore because not all file systems have dots in their file names!) - String filebase_name = exec_path.get_file().get_basename(); + String exec_dir = exec_path.get_base_dir(); + String exec_filename = exec_path.get_file(); + String exec_basename = exec_filename.get_basename(); + + // Try to load data pack at the location of the executable + // As mentioned above, we have two potential names to attempt - // try to open at the location of executable - String datapack_name = exec_path.get_base_dir().plus_file(filebase_name) + ".pck"; - if (_load_resource_pack(datapack_name)) { + if (_load_resource_pack(exec_dir.plus_file(exec_basename + ".pck")) || + _load_resource_pack(exec_dir.plus_file(exec_filename + ".pck"))) { found = true; } else { - datapack_name = filebase_name + ".pck"; - if (_load_resource_pack(datapack_name)) { + // If we couldn't find them next to the executable, we attempt + // the current working directory. Same story, two tests. + if (_load_resource_pack(exec_basename + ".pck") || + _load_resource_pack(exec_filename + ".pck")) { found = true; } } - // if we opened our package, try and load our project... + // If we opened our package, try and load our project if (found) { Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary"); if (err == OK) { @@ -349,17 +381,15 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo } } - //Try to use the filesystem for files, according to OS. (only Android -when reading from pck- and iOS use this) - if (OS::get_singleton()->get_resource_dir() != "") { - //OS will call Globals->get_resource_path which will be empty if not overridden! - //if the OS would rather use somewhere else, then it will not be empty. + // Try to use the filesystem for files, according to OS. (only Android -when reading from pck- and iOS use this) + if (OS::get_singleton()->get_resource_dir() != "") { + // OS will call ProjectSettings->get_resource_path which will be empty if not overridden! + // If the OS would rather use a specific location, then it will not be empty. resource_path = OS::get_singleton()->get_resource_dir().replace("\\", "/"); - if (resource_path.length() && resource_path[resource_path.length() - 1] == '/') + if (resource_path != "" && resource_path[resource_path.length() - 1] == '/') { resource_path = resource_path.substr(0, resource_path.length() - 1); // chop end - - // data.pck and data.zip are deprecated and no longer supported, apologies. - // make sure this is loaded from the resource path + } Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary"); if (err == OK) { @@ -370,21 +400,19 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo return err; } - //Nothing was found, try to find a project.godot somewhere! + // Nothing was found, try to find a project file in provided path (`p_path`) + // or, if requested (`p_upwards`) in parent directories. DirAccess *d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); ERR_FAIL_COND_V(!d, ERR_CANT_CREATE); - d->change_dir(p_path); - String candidate = d->get_current_dir(); String current_dir = d->get_current_dir(); - + String candidate = current_dir; bool found = false; Error err; while (true) { - err = _load_settings_text_or_binary(current_dir.plus_file("project.godot"), current_dir.plus_file("project.binary")); if (err == OK) { // Optional, we don't mind if it fails @@ -395,10 +423,10 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo } if (p_upwards) { - // Try to load settings ascending through dirs shape! + // Try to load settings ascending through parent directories d->change_dir(".."); if (d->get_current_dir() == current_dir) - break; //not doing anything useful + break; // not doing anything useful current_dir = d->get_current_dir(); } else { break; @@ -415,6 +443,8 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo if (resource_path.length() && resource_path[resource_path.length() - 1] == '/') resource_path = resource_path.substr(0, resource_path.length() - 1); // chop end + // If we're loading a project.godot from source code, we can operate some + // ProjectSettings conversions if need be. _convert_to_last_version(); return OK; @@ -1132,6 +1162,7 @@ ProjectSettings::ProjectSettings() { custom_prop_info["rendering/quality/intended_usage/framebuffer_allocation"] = PropertyInfo(Variant::INT, "rendering/quality/intended_usage/framebuffer_allocation", PROPERTY_HINT_ENUM, "2D,2D Without Sampling,3D,3D Without Effects"); GLOBAL_DEF("debug/settings/profiler/max_functions", 16384); + custom_prop_info["debug/settings/profiler/max_functions"] = PropertyInfo(Variant::INT, "debug/settings/profiler/max_functions", PROPERTY_HINT_RANGE, "128,65535,1"); //assigning here, because using GLOBAL_GET on every block for compressing can be slow Compression::zstd_long_distance_matching = GLOBAL_DEF("compression/formats/zstd/long_distance_matching", false); diff --git a/core/project_settings.h b/core/project_settings.h index 75ebc5acc8..611355f2ef 100644 --- a/core/project_settings.h +++ b/core/project_settings.h @@ -31,9 +31,10 @@ #ifndef GLOBAL_CONFIG_H #define GLOBAL_CONFIG_H -#include "object.h" -#include "os/thread_safe.h" -#include "set.h" +#include "core/object.h" +#include "core/os/thread_safe.h" +#include "core/set.h" + /** @author Juan Linietsky <reduzio@gmail.com> */ diff --git a/core/ref_ptr.cpp b/core/ref_ptr.cpp index e3ef817df1..7f0cc0cd75 100644 --- a/core/ref_ptr.cpp +++ b/core/ref_ptr.cpp @@ -30,8 +30,8 @@ #include "ref_ptr.h" -#include "reference.h" -#include "resource.h" +#include "core/reference.h" +#include "core/resource.h" void RefPtr::operator=(const RefPtr &p_other) { diff --git a/core/ref_ptr.h b/core/ref_ptr.h index a074718d22..3f15c680b8 100644 --- a/core/ref_ptr.h +++ b/core/ref_ptr.h @@ -36,7 +36,7 @@ * It's basically an opaque container of a Reference reference, so Variant can use it. */ -#include "rid.h" +#include "core/rid.h" class RefPtr { diff --git a/core/reference.cpp b/core/reference.cpp index c33a7c683c..b79ad0bf3d 100644 --- a/core/reference.cpp +++ b/core/reference.cpp @@ -30,7 +30,7 @@ #include "reference.h" -#include "script_language.h" +#include "core/script_language.h" bool Reference::init_ref() { @@ -66,8 +66,17 @@ int Reference::reference_get_count() const { bool Reference::reference() { bool success = refcount.ref(); - if (success && get_script_instance()) { - get_script_instance()->refcount_incremented(); + if (success && refcount.get() <= 2 /* higher is not relevant */) { + if (get_script_instance()) { + get_script_instance()->refcount_incremented(); + } + if (instance_binding_count > 0) { + for (int i = 0; i < MAX_SCRIPT_INSTANCE_BINDINGS; i++) { + if (_script_instance_bindings[i]) { + ScriptServer::get_language(i)->refcount_incremented_instance_binding(this); + } + } + } } return success; @@ -77,9 +86,19 @@ bool Reference::unreference() { bool die = refcount.unref(); - if (get_script_instance()) { - bool script_ret = get_script_instance()->refcount_decremented(); - die = die && script_ret; + if (refcount.get() <= 1 /* higher is not relevant */) { + if (get_script_instance()) { + bool script_ret = get_script_instance()->refcount_decremented(); + die = die && script_ret; + } + if (instance_binding_count > 0) { + for (int i = 0; i < MAX_SCRIPT_INSTANCE_BINDINGS; i++) { + if (_script_instance_bindings[i]) { + bool script_ret = ScriptServer::get_language(i)->refcount_decremented_instance_binding(this); + die = die && script_ret; + } + } + } } return die; diff --git a/core/reference.h b/core/reference.h index 25e02180fa..4c607fed87 100644 --- a/core/reference.h +++ b/core/reference.h @@ -31,10 +31,10 @@ #ifndef REFERENCE_H #define REFERENCE_H -#include "class_db.h" -#include "object.h" -#include "ref_ptr.h" -#include "safe_refcount.h" +#include "core/class_db.h" +#include "core/object.h" +#include "core/ref_ptr.h" +#include "core/safe_refcount.h" /** @author Juan Linietsky <reduzio@gmail.com> diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index 859015f44b..430004bb96 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -30,40 +30,40 @@ #include "register_core_types.h" -#include "bind/core_bind.h" -#include "class_db.h" -#include "compressed_translation.h" +#include "core/bind/core_bind.h" +#include "core/class_db.h" +#include "core/compressed_translation.h" +#include "core/core_string_names.h" +#include "core/engine.h" +#include "core/func_ref.h" +#include "core/input_map.h" +#include "core/io/config_file.h" +#include "core/io/http_client.h" +#include "core/io/image_loader.h" +#include "core/io/marshalls.h" +#include "core/io/multiplayer_api.h" +#include "core/io/networked_multiplayer_peer.h" +#include "core/io/packet_peer.h" +#include "core/io/packet_peer_udp.h" +#include "core/io/pck_packer.h" +#include "core/io/resource_format_binary.h" +#include "core/io/resource_import.h" +#include "core/io/stream_peer_ssl.h" +#include "core/io/tcp_server.h" +#include "core/io/translation_loader_po.h" #include "core/io/xml_parser.h" -#include "core_string_names.h" -#include "engine.h" -#include "func_ref.h" -#include "geometry.h" -#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" -#include "io/packet_peer.h" -#include "io/packet_peer_udp.h" -#include "io/pck_packer.h" -#include "io/resource_format_binary.h" -#include "io/resource_import.h" -#include "io/stream_peer_ssl.h" -#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" -#include "packed_data_container.h" -#include "path_remap.h" -#include "project_settings.h" -#include "translation.h" - -#include "undo_redo.h" +#include "core/math/a_star.h" +#include "core/math/expression.h" +#include "core/math/geometry.h" +#include "core/math/triangle_mesh.h" +#include "core/os/input.h" +#include "core/os/main_loop.h" +#include "core/packed_data_container.h" +#include "core/path_remap.h" +#include "core/project_settings.h" +#include "core/translation.h" +#include "core/undo_redo.h" + static ResourceFormatSaverBinary *resource_saver_binary = NULL; static ResourceFormatLoaderBinary *resource_loader_binary = NULL; static ResourceFormatImporter *resource_format_importer = NULL; @@ -146,9 +146,9 @@ void register_core_types() { ClassDB::register_class<FuncRef>(); ClassDB::register_virtual_class<StreamPeer>(); ClassDB::register_class<StreamPeerBuffer>(); - ClassDB::register_custom_instance_class<StreamPeerTCP>(); - ClassDB::register_custom_instance_class<TCP_Server>(); - ClassDB::register_custom_instance_class<PacketPeerUDP>(); + ClassDB::register_class<StreamPeerTCP>(); + ClassDB::register_class<TCP_Server>(); + ClassDB::register_class<PacketPeerUDP>(); ClassDB::register_custom_instance_class<StreamPeerSSL>(); ClassDB::register_virtual_class<IP>(); ClassDB::register_virtual_class<PacketPeer>(); @@ -156,7 +156,6 @@ void register_core_types() { ClassDB::register_virtual_class<NetworkedMultiplayerPeer>(); ClassDB::register_class<MultiplayerAPI>(); ClassDB::register_class<MainLoop>(); - //ClassDB::register_type<OptimizedSaver>(); ClassDB::register_class<Translation>(); ClassDB::register_class<PHashTranslation>(); ClassDB::register_class<UndoRedo>(); @@ -200,6 +199,7 @@ void register_core_types() { void register_core_settings() { //since in register core types, globals may not e present GLOBAL_DEF_RST("network/limits/packet_peer_stream/max_buffer_po2", (16)); + ProjectSettings::get_singleton()->set_custom_property_info("network/limits/packet_peer_stream/max_buffer_po2", PropertyInfo(Variant::INT, "network/limits/packet_peer_stream/max_buffer_po2", PROPERTY_HINT_RANGE, "0,64,1,or_greater")); } void register_core_singletons() { diff --git a/core/resource.cpp b/core/resource.cpp index 3078eb135a..4dcd338e94 100644 --- a/core/resource.cpp +++ b/core/resource.cpp @@ -30,11 +30,12 @@ #include "resource.h" -#include "core_string_names.h" -#include "io/resource_loader.h" -#include "os/file_access.h" +#include "core/core_string_names.h" +#include "core/io/resource_loader.h" +#include "core/os/file_access.h" +#include "core/script_language.h" #include "scene/main/node.h" //only so casting works -#include "script_language.h" + #include <stdio.h> void Resource::emit_changed() { @@ -230,7 +231,7 @@ Ref<Resource> Resource::duplicate(bool p_subresources) const { Variant p = get(E->get().name); if ((p.get_type() == Variant::DICTIONARY || p.get_type() == Variant::ARRAY)) { - p = p.duplicate(p_subresources); //does not make a long of sense but should work? + r->set(E->get().name, p.duplicate(p_subresources)); } else if (p.get_type() == Variant::OBJECT && (p_subresources || (E->get().usage & PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE))) { RES sr = p; diff --git a/core/resource.h b/core/resource.h index 60c63bfe7f..b2812c11e1 100644 --- a/core/resource.h +++ b/core/resource.h @@ -31,12 +31,12 @@ #ifndef RESOURCE_H #define RESOURCE_H -#include "class_db.h" -#include "object.h" -#include "ref_ptr.h" -#include "reference.h" -#include "safe_refcount.h" -#include "self_list.h" +#include "core/class_db.h" +#include "core/object.h" +#include "core/ref_ptr.h" +#include "core/reference.h" +#include "core/safe_refcount.h" +#include "core/self_list.h" /** @author Juan Linietsky <reduzio@gmail.com> diff --git a/core/rid.h b/core/rid.h index 42306aea36..81d5b45d21 100644 --- a/core/rid.h +++ b/core/rid.h @@ -31,11 +31,11 @@ #ifndef RID_H #define RID_H -#include "list.h" -#include "os/memory.h" -#include "safe_refcount.h" -#include "set.h" -#include "typedefs.h" +#include "core/list.h" +#include "core/os/memory.h" +#include "core/safe_refcount.h" +#include "core/set.h" +#include "core/typedefs.h" /** @author Juan Linietsky <reduzio@gmail.com> @@ -118,7 +118,6 @@ protected: p_rid._data->_owner = NULL; } -# #endif public: diff --git a/core/ring_buffer.h b/core/ring_buffer.h index 00628a4ab3..2516880064 100644 --- a/core/ring_buffer.h +++ b/core/ring_buffer.h @@ -31,7 +31,7 @@ #ifndef RINGBUFFER_H #define RINGBUFFER_H -#include "vector.h" +#include "core/vector.h" template <typename T> class RingBuffer { diff --git a/core/safe_refcount.h b/core/safe_refcount.h index 36bcf5e576..940d31b2cb 100644 --- a/core/safe_refcount.h +++ b/core/safe_refcount.h @@ -31,11 +31,9 @@ #ifndef SAFE_REFCOUNT_H #define SAFE_REFCOUNT_H -#include "os/mutex.h" -/* x86/x86_64 GCC */ - +#include "core/os/mutex.h" +#include "core/typedefs.h" #include "platform_config.h" -#include "typedefs.h" // Atomic functions, these are used for multithread safe reference counters! diff --git a/core/script_debugger_local.cpp b/core/script_debugger_local.cpp index 6949b5802b..7093c3ebdb 100644 --- a/core/script_debugger_local.cpp +++ b/core/script_debugger_local.cpp @@ -29,9 +29,9 @@ /*************************************************************************/ #include "script_debugger_local.h" -#include "scene/main/scene_tree.h" -#include "os/os.h" +#include "core/os/os.h" +#include "scene/main/scene_tree.h" void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue) { diff --git a/core/script_debugger_local.h b/core/script_debugger_local.h index 7eea6ef215..1f7b5c590c 100644 --- a/core/script_debugger_local.h +++ b/core/script_debugger_local.h @@ -31,8 +31,8 @@ #ifndef SCRIPT_DEBUGGER_LOCAL_H #define SCRIPT_DEBUGGER_LOCAL_H -#include "list.h" -#include "script_language.h" +#include "core/list.h" +#include "core/script_language.h" class ScriptDebuggerLocal : public ScriptDebugger { diff --git a/core/script_debugger_remote.cpp b/core/script_debugger_remote.cpp index 2b9b5d6037..621a94ab1a 100644 --- a/core/script_debugger_remote.cpp +++ b/core/script_debugger_remote.cpp @@ -30,12 +30,12 @@ #include "script_debugger_remote.h" -#include "engine.h" -#include "io/ip.h" -#include "io/marshalls.h" -#include "os/input.h" -#include "os/os.h" -#include "project_settings.h" +#include "core/engine.h" +#include "core/io/ip.h" +#include "core/io/marshalls.h" +#include "core/os/input.h" +#include "core/os/os.h" +#include "core/project_settings.h" #include "scene/main/node.h" #include "scene/resources/packed_scene.h" @@ -77,18 +77,19 @@ Error ScriptDebuggerRemote::connect_to_host(const String &p_host, uint16_t p_por for (int i = 0; i < tries; i++) { if (tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED) { + print_verbose("Remote Debugger: Connected!"); break; } else { const int ms = waits[i]; OS::get_singleton()->delay_usec(ms * 1000); - ERR_PRINTS("Remote Debugger: Connection failed with status: '" + String::num(tcp_client->get_status()) + "', retrying in " + String::num(ms) + " msec."); + print_verbose("Remote Debugger: Connection failed with status: '" + String::num(tcp_client->get_status()) + "', retrying in " + String::num(ms) + " msec."); }; }; if (tcp_client->get_status() != StreamPeerTCP::STATUS_CONNECTED) { - ERR_PRINTS("Remote Debugger: Unable to connect."); + ERR_PRINTS("Remote Debugger: Unable to connect. Status: " + String::num(tcp_client->get_status())); return FAILED; }; @@ -97,36 +98,6 @@ Error ScriptDebuggerRemote::connect_to_host(const String &p_host, uint16_t p_por return OK; } -static int _ScriptDebuggerRemote_found_id = 0; -static Object *_ScriptDebuggerRemote_find = NULL; -static void _ScriptDebuggerRemote_debug_func(Object *p_obj) { - - if (_ScriptDebuggerRemote_find == p_obj) { - _ScriptDebuggerRemote_found_id = p_obj->get_instance_id(); - } -} - -static ObjectID safe_get_instance_id(const Variant &p_v) { - - Object *o = p_v; - if (o == NULL) - return 0; - else { - - REF r = p_v; - if (r.is_valid()) { - - return r->get_instance_id(); - } else { - - _ScriptDebuggerRemote_found_id = 0; - _ScriptDebuggerRemote_find = NULL; - ObjectDB::debug_objects(_ScriptDebuggerRemote_debug_func); - return _ScriptDebuggerRemote_found_id; - } - } -} - void ScriptDebuggerRemote::_put_variable(const String &p_name, const Variant &p_variable) { packet_peer_stream->put_var(p_name); @@ -607,8 +578,14 @@ void ScriptDebuggerRemote::_send_object_id(ObjectID p_id) { for (ScriptConstantsMap::Element *sc = constants.front(); sc; sc = sc->next()) { for (Map<StringName, Variant>::Element *E = sc->get().front(); E; E = E->next()) { String script_path = sc->key() == si->get_script().ptr() ? "" : sc->key()->get_path().get_file() + "/"; - PropertyInfo pi(E->value().get_type(), "Constants/" + script_path + E->key()); - properties.push_back(PropertyDesc(pi, E->value())); + if (E->value().get_type() == Variant::OBJECT) { + Variant id = ((Object *)E->value())->get_instance_id(); + PropertyInfo pi(id.get_type(), "Constants/" + E->key(), PROPERTY_HINT_OBJECT_ID, "Object"); + properties.push_back(PropertyDesc(pi, id)); + } else { + PropertyInfo pi(E->value().get_type(), "Constants/" + script_path + E->key()); + properties.push_back(PropertyDesc(pi, E->value())); + } } } } @@ -621,8 +598,14 @@ void ScriptDebuggerRemote::_send_object_id(ObjectID p_id) { Map<StringName, Variant> constants; s->get_constants(&constants); for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) { - PropertyInfo pi(E->value().get_type(), String("Constants/") + E->key()); - properties.push_front(PropertyDesc(pi, E->value())); + if (E->value().get_type() == Variant::OBJECT) { + Variant id = ((Object *)E->value())->get_instance_id(); + PropertyInfo pi(id.get_type(), "Constants/" + E->key(), PROPERTY_HINT_OBJECT_ID, "Object"); + properties.push_front(PropertyDesc(pi, E->value())); + } else { + PropertyInfo pi(E->value().get_type(), String("Constants/") + E->key()); + properties.push_front(PropertyDesc(pi, E->value())); + } } } } @@ -661,11 +644,13 @@ void ScriptDebuggerRemote::_send_object_id(ObjectID p_id) { prop.push_back(Variant()); } else { prop.push_back(pi.hint); - if (res.is_null()) - prop.push_back(pi.hint_string); - else - prop.push_back(String("RES:") + res->get_path()); + prop.push_back(pi.hint_string); prop.push_back(pi.usage); + + if (!res.is_null()) { + var = res->get_path(); + } + prop.push_back(var); } send_props.push_back(prop); @@ -1084,18 +1069,18 @@ ScriptDebuggerRemote::ScriptDebuggerRemote() : max_frame_functions(16), skip_profile_frame(false), reload_all_scripts(false), - tcp_client(StreamPeerTCP::create_ref()), + tcp_client(Ref<StreamPeerTCP>(memnew(StreamPeerTCP))), packet_peer_stream(Ref<PacketPeerStream>(memnew(PacketPeerStream))), last_perf_time(0), performance(Engine::get_singleton()->get_singleton_object("Performance")), requested_quit(false), mutex(Mutex::create()), - max_cps(GLOBAL_GET("network/limits/debugger_stdout/max_chars_per_second")), max_messages_per_frame(GLOBAL_GET("network/limits/debugger_stdout/max_messages_per_frame")), - max_errors_per_frame(GLOBAL_GET("network/limits/debugger_stdout/max_errors_per_frame")), - char_count(0), n_messages_dropped(0), + max_errors_per_frame(GLOBAL_GET("network/limits/debugger_stdout/max_errors_per_frame")), n_errors_dropped(0), + max_cps(GLOBAL_GET("network/limits/debugger_stdout/max_chars_per_second")), + char_count(0), last_msec(0), msec_count(0), allow_focus_steal_pid(0), @@ -1115,7 +1100,7 @@ ScriptDebuggerRemote::ScriptDebuggerRemote() : eh.userdata = this; add_error_handler(&eh); - profile_info.resize(CLAMP(int(ProjectSettings::get_singleton()->get("debug/settings/profiler/max_functions")), 128, 65535)); + profile_info.resize(GLOBAL_GET("debug/settings/profiler/max_functions")); profile_info_ptrs.resize(profile_info.size()); } diff --git a/core/script_debugger_remote.h b/core/script_debugger_remote.h index b68fc4f9c9..5cd3a23a37 100644 --- a/core/script_debugger_remote.h +++ b/core/script_debugger_remote.h @@ -31,11 +31,11 @@ #ifndef SCRIPT_DEBUGGER_REMOTE_H #define SCRIPT_DEBUGGER_REMOTE_H -#include "io/packet_peer.h" -#include "io/stream_peer_tcp.h" -#include "list.h" -#include "os/os.h" -#include "script_language.h" +#include "core/io/packet_peer.h" +#include "core/io/stream_peer_tcp.h" +#include "core/list.h" +#include "core/os/os.h" +#include "core/script_language.h" class ScriptDebuggerRemote : public ScriptDebugger { diff --git a/core/script_language.cpp b/core/script_language.cpp index e146fb773c..5b65da9ef1 100644 --- a/core/script_language.cpp +++ b/core/script_language.cpp @@ -29,7 +29,8 @@ /*************************************************************************/ #include "script_language.h" -#include "project_settings.h" + +#include "core/project_settings.h" ScriptLanguage *ScriptServer::_languages[MAX_LANGUAGES]; int ScriptServer::_language_count = 0; diff --git a/core/script_language.h b/core/script_language.h index 71b705e960..bcd9c2c5ea 100644 --- a/core/script_language.h +++ b/core/script_language.h @@ -31,10 +31,11 @@ #ifndef SCRIPT_LANGUAGE_H #define SCRIPT_LANGUAGE_H -#include "io/multiplayer_api.h" -#include "map.h" -#include "pair.h" -#include "resource.h" +#include "core/io/multiplayer_api.h" +#include "core/map.h" +#include "core/pair.h" +#include "core/resource.h" + /** @author Juan Linietsky <reduzio@gmail.com> */ @@ -311,6 +312,8 @@ public: virtual void *alloc_instance_binding_data(Object *p_object) { return NULL; } //optional, not used by all languages virtual void free_instance_binding_data(void *p_data) {} //optional, not used by all languages + virtual void refcount_incremented_instance_binding(Object *p_object) {} //optional, not used by all languages + virtual bool refcount_decremented_instance_binding(Object *p_object) { return true; } //return true if it can die //optional, not used by all languages virtual void frame(); diff --git a/core/self_list.h b/core/self_list.h index 6e84e1cd5f..9a4ecdeef9 100644 --- a/core/self_list.h +++ b/core/self_list.h @@ -31,7 +31,7 @@ #ifndef SELF_LIST_H #define SELF_LIST_H -#include "typedefs.h" +#include "core/typedefs.h" template <class T> class SelfList { diff --git a/core/set.h b/core/set.h index d79dd81644..59aa54128e 100644 --- a/core/set.h +++ b/core/set.h @@ -31,8 +31,8 @@ #ifndef SET_H #define SET_H -#include "os/memory.h" -#include "typedefs.h" +#include "core/os/memory.h" +#include "core/typedefs.h" /** @author Juan Linietsky <reduzio@gmail.com> @@ -595,6 +595,7 @@ public: return e; } + inline bool empty() const { return _data.size_cache == 0; } inline int size() const { return _data.size_cache; } int calculate_depth() const { diff --git a/core/sort.h b/core/sort.h index 97983829e1..e112304d1b 100644 --- a/core/sort.h +++ b/core/sort.h @@ -31,10 +31,7 @@ #ifndef SORT_H #define SORT_H -#include "typedefs.h" -/** - @author ,,, <red@lunatea> -*/ +#include "core/typedefs.h" #define ERR_BAD_COMPARE(cond) \ if (unlikely(cond)) { \ diff --git a/core/string_buffer.h b/core/string_buffer.h index 5d3be0ecf1..38dce5e6bf 100644 --- a/core/string_buffer.h +++ b/core/string_buffer.h @@ -31,8 +31,7 @@ #ifndef STRING_BUFFER_H #define STRING_BUFFER_H -#include "ustring.h" -#include <string.h> +#include "core/ustring.h" template <int SHORT_BUFFER_SIZE = 64> class StringBuffer { diff --git a/core/string_db.cpp b/core/string_db.cpp index 067e4493a1..a798df8795 100644 --- a/core/string_db.cpp +++ b/core/string_db.cpp @@ -30,8 +30,8 @@ #include "string_db.h" -#include "os/os.h" -#include "print_string.h" +#include "core/os/os.h" +#include "core/print_string.h" StaticCString StaticCString::create(const char *p_ptr) { StaticCString scs; diff --git a/core/string_db.h b/core/string_db.h index 965385b136..46bb77df93 100644 --- a/core/string_db.h +++ b/core/string_db.h @@ -31,9 +31,9 @@ #ifndef STRING_DB_H #define STRING_DB_H -#include "os/mutex.h" -#include "safe_refcount.h" -#include "ustring.h" +#include "core/os/mutex.h" +#include "core/safe_refcount.h" +#include "core/ustring.h" /** @author Juan Linietsky <reduzio@gmail.com> */ diff --git a/core/translation.cpp b/core/translation.cpp index 82a16d0b17..25e67e9b96 100644 --- a/core/translation.cpp +++ b/core/translation.cpp @@ -30,9 +30,9 @@ #include "translation.h" -#include "io/resource_loader.h" -#include "os/os.h" -#include "project_settings.h" +#include "core/io/resource_loader.h" +#include "core/os/os.h" +#include "core/project_settings.h" // ISO 639-1 language codes, with the addition of glibc locales with their // regional identifiers. This list must match the language names (in English) @@ -938,11 +938,14 @@ void TranslationServer::set_locale(const String &p_locale) { if (!is_locale_valid(univ_locale)) { String trimmed_locale = get_trimmed_locale(univ_locale); + print_verbose(vformat("Unsupported locale '%s', falling back to '%s'.", p_locale, trimmed_locale)); - ERR_EXPLAIN("Invalid locale: " + trimmed_locale); - ERR_FAIL_COND(!is_locale_valid(trimmed_locale)); - - locale = trimmed_locale; + if (!is_locale_valid(trimmed_locale)) { + ERR_PRINTS(vformat("Unsupported locale '%s', falling back to 'en'.", trimmed_locale)); + locale = "en"; + } else { + locale = trimmed_locale; + } } else { locale = univ_locale; } diff --git a/core/translation.h b/core/translation.h index e7c0dcbc07..be626f4eb9 100644 --- a/core/translation.h +++ b/core/translation.h @@ -31,7 +31,7 @@ #ifndef TRANSLATION_H #define TRANSLATION_H -#include "resource.h" +#include "core/resource.h" class Translation : public Resource { diff --git a/core/typedefs.h b/core/typedefs.h index 094f1bbfd5..5c15da9c2d 100644 --- a/core/typedefs.h +++ b/core/typedefs.h @@ -32,6 +32,7 @@ #define TYPEDEFS_H #include <stddef.h> + /** * Basic definitions and simple functions to be used everywhere. */ @@ -43,6 +44,7 @@ #define _MKSTR(m_x) _STR(m_x) #endif +//should always inline no matter what #ifndef _ALWAYS_INLINE_ #if defined(__GNUC__) && (__GNUC__ >= 4) @@ -57,10 +59,17 @@ #endif +//should always inline, except in some cases because it makes debugging harder #ifndef _FORCE_INLINE_ + +#ifdef DISABLE_FORCED_INLINE +#define _FORCE_INLINE_ inline +#else #define _FORCE_INLINE_ _ALWAYS_INLINE_ #endif +#endif + //custom, gcc-safe offsetof, because gcc complains a lot. template <class T> T *_nullptr() { @@ -96,19 +105,19 @@ T *_nullptr() { #undef CONNECT_DEFERRED // override from Windows SDK, clashes with Object enum #endif -#include "int_types.h" +#include "core/int_types.h" -#include "error_list.h" -#include "error_macros.h" +#include "core/error_list.h" +#include "core/error_macros.h" /** Generic ABS function, for math uses please use Math::abs */ #ifndef ABS -#define ABS(m_v) ((m_v < 0) ? (-(m_v)) : (m_v)) +#define ABS(m_v) (((m_v) < 0) ? (-(m_v)) : (m_v)) #endif #ifndef SGN -#define SGN(m_v) ((m_v < 0) ? (-1.0) : (+1.0)) +#define SGN(m_v) (((m_v) < 0) ? (-1.0) : (+1.0)) #endif #ifndef MIN diff --git a/core/undo_redo.cpp b/core/undo_redo.cpp index 3d90608dd7..7d67076df5 100644 --- a/core/undo_redo.cpp +++ b/core/undo_redo.cpp @@ -30,7 +30,7 @@ #include "undo_redo.h" -#include "os/os.h" +#include "core/os/os.h" void UndoRedo::_discard_redo() { diff --git a/core/undo_redo.h b/core/undo_redo.h index 3a17c78851..22dcd60472 100644 --- a/core/undo_redo.h +++ b/core/undo_redo.h @@ -31,8 +31,8 @@ #ifndef UNDO_REDO_H #define UNDO_REDO_H -#include "object.h" -#include "resource.h" +#include "core/object.h" +#include "core/resource.h" class UndoRedo : public Object { diff --git a/core/ustring.cpp b/core/ustring.cpp index 96e3a3d784..2191bb5e23 100644 --- a/core/ustring.cpp +++ b/core/ustring.cpp @@ -30,12 +30,13 @@ #include "ustring.h" -#include "color.h" -#include "math_funcs.h" -#include "os/memory.h" -#include "print_string.h" -#include "ucaps.h" -#include "variant.h" +#include "core/color.h" +#include "core/math/math_funcs.h" +#include "core/os/memory.h" +#include "core/print_string.h" +#include "core/translation.h" +#include "core/ucaps.h" +#include "core/variant.h" #include "thirdparty/misc/md5.h" #include "thirdparty/misc/sha256.h" @@ -48,7 +49,7 @@ #endif #if defined(MINGW_ENABLED) || defined(_MSC_VER) -#define snprintf _snprintf +#define snprintf _snprintf_s #endif #define MAX_DIGITS 6 @@ -585,6 +586,8 @@ String String::camelcase_to_underscore(bool lowercase) const { bool is_upper = cstr[i] >= A && cstr[i] <= Z; bool is_number = cstr[i] >= '0' && cstr[i] <= '9'; bool are_next_2_lower = false; + bool is_next_lower = false; + bool is_next_number = false; bool was_precedent_upper = cstr[i - 1] >= A && cstr[i - 1] <= Z; bool was_precedent_number = cstr[i - 1] >= '0' && cstr[i - 1] <= '9'; @@ -592,7 +595,18 @@ String String::camelcase_to_underscore(bool lowercase) const { are_next_2_lower = cstr[i + 1] >= a && cstr[i + 1] <= z && cstr[i + 2] >= a && cstr[i + 2] <= z; } - bool should_split = ((is_upper && !was_precedent_upper && !was_precedent_number) || (was_precedent_upper && is_upper && are_next_2_lower) || (is_number && !was_precedent_number)); + if (i + 1 < this->size()) { + is_next_lower = cstr[i + 1] >= a && cstr[i + 1] <= z; + is_next_number = cstr[i + 1] >= '0' && cstr[i + 1] <= '9'; + } + + const bool a = is_upper && !was_precedent_upper && !was_precedent_number; + const bool b = was_precedent_upper && is_upper && are_next_2_lower; + const bool c = is_number && !was_precedent_number; + const bool can_break_number_letter = is_number && !was_precedent_number && is_next_lower; + const bool can_break_letter_number = !is_number && was_precedent_number && (is_next_lower || is_next_number); + + bool should_split = a || b || c || can_break_number_letter || can_break_letter_number; if (should_split) { new_string += this->substr(start_index, i - start_index) + "_"; start_index = i; @@ -1827,8 +1841,8 @@ static double built_in_strtod(const C *string, /* A decimal ASCII floating-point int sign, expSign = false; double fraction, dblExp; const double *d; - register const C *p; - register int c; + const C *p; + int c; int exp = 0; /* Exponent read from "EX" field. */ int fracExp = 0; /* Exponent that derives from the fractional * part. Under normal circumstances, it is @@ -2760,16 +2774,13 @@ String String::format(const Variant &values, String placeholder) const { if (value_arr.size() == 2) { Variant v_key = value_arr[0]; - String key; - - key = v_key.get_construct_string(); + String key = v_key; if (key.left(1) == "\"" && key.right(key.length() - 1) == "\"") { key = key.substr(1, key.length() - 2); } Variant v_val = value_arr[1]; - String val; - val = v_val.get_construct_string(); + String val = v_val; if (val.left(1) == "\"" && val.right(val.length() - 1) == "\"") { val = val.substr(1, val.length() - 2); @@ -2781,8 +2792,7 @@ String String::format(const Variant &values, String placeholder) const { } } else { //Array structure ["RobotGuy","Logis","rookie"] Variant v_val = values_arr[i]; - String val; - val = v_val.get_construct_string(); + String val = v_val; if (val.left(1) == "\"" && val.right(val.length() - 1) == "\"") { val = val.substr(1, val.length() - 2); @@ -2801,8 +2811,8 @@ String String::format(const Variant &values, String placeholder) const { d.get_key_list(&keys); for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - String key = E->get().get_construct_string(); - String val = d[E->get()].get_construct_string(); + String key = E->get(); + String val = d[E->get()]; if (key.left(1) == "\"" && key.right(key.length() - 1) == "\"") { key = key.substr(1, key.length() - 2); @@ -3091,7 +3101,7 @@ String String::simplify_path() const { } else if (s.begins_with("user://")) { drive = "user://"; - s = s.substr(6, s.length()); + s = s.substr(7, s.length()); } else if (s.begins_with("/") || s.begins_with("\\")) { drive = s.substr(0, 1); @@ -4204,8 +4214,6 @@ String String::unquote() const { return substr(1, length() - 2); } -#include "translation.h" - #ifdef TOOLS_ENABLED String TTR(const String &p_text) { diff --git a/core/ustring.h b/core/ustring.h index 01397f6912..d2766ec7a3 100644 --- a/core/ustring.h +++ b/core/ustring.h @@ -28,16 +28,16 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef RSTRING_H -#define RSTRING_H +#ifndef USTRING_H +#define USTRING_H -#include "array.h" -#include "cowdata.h" -#include "typedefs.h" -#include "vector.h" +#include "core/array.h" +#include "core/cowdata.h" +#include "core/typedefs.h" +#include "core/vector.h" /** - @author red <red@killy> + @author Juan Linietsky <reduzio@gmail.com> */ class CharString { @@ -366,4 +366,4 @@ String RTR(const String &); bool is_symbol(CharType c); bool select_word(const String &p_s, int p_col, int &r_beg, int &r_end); -#endif +#endif // USTRING_H diff --git a/core/variant.cpp b/core/variant.cpp index b0e97900a2..edbe66ba31 100644 --- a/core/variant.cpp +++ b/core/variant.cpp @@ -30,14 +30,14 @@ #include "variant.h" -#include "core_string_names.h" -#include "io/marshalls.h" -#include "math_funcs.h" -#include "print_string.h" -#include "resource.h" +#include "core/core_string_names.h" +#include "core/io/marshalls.h" +#include "core/math/math_funcs.h" +#include "core/print_string.h" +#include "core/resource.h" +#include "core/variant_parser.h" #include "scene/gui/control.h" #include "scene/main/node.h" -#include "variant_parser.h" String Variant::get_type_name(Variant::Type p_type) { @@ -858,7 +858,7 @@ bool Variant::is_one() const { // atomic types case BOOL: { - return _data._bool == true; + return _data._bool; } break; case INT: { diff --git a/core/variant.h b/core/variant.h index 577a33aa4d..8953217760 100644 --- a/core/variant.h +++ b/core/variant.h @@ -35,23 +35,23 @@ @author Juan Linietsky <reduzio@gmail.com> */ -#include "aabb.h" -#include "array.h" -#include "color.h" -#include "dictionary.h" -#include "dvector.h" -#include "face3.h" -#include "io/ip_address.h" -#include "matrix3.h" -#include "node_path.h" -#include "plane.h" -#include "quat.h" -#include "ref_ptr.h" -#include "rid.h" -#include "transform.h" -#include "transform_2d.h" -#include "ustring.h" -#include "vector3.h" +#include "core/array.h" +#include "core/color.h" +#include "core/dictionary.h" +#include "core/dvector.h" +#include "core/io/ip_address.h" +#include "core/math/aabb.h" +#include "core/math/face3.h" +#include "core/math/matrix3.h" +#include "core/math/plane.h" +#include "core/math/quat.h" +#include "core/math/transform.h" +#include "core/math/transform_2d.h" +#include "core/math/vector3.h" +#include "core/node_path.h" +#include "core/ref_ptr.h" +#include "core/rid.h" +#include "core/ustring.h" class RefPtr; class Object; @@ -116,7 +116,7 @@ public: }; private: - friend class _VariantCall; + friend struct _VariantCall; // Variant takes 20 bytes when real_t is float, and 36 if double // it only allocates extra memory for aabb/matrix. diff --git a/core/variant_call.cpp b/core/variant_call.cpp index 8b18b274b6..8693a584f2 100644 --- a/core/variant_call.cpp +++ b/core/variant_call.cpp @@ -31,11 +31,11 @@ #include "variant.h" #include "core/color_names.inc" -#include "core_string_names.h" -#include "io/compression.h" -#include "object.h" -#include "os/os.h" -#include "script_language.h" +#include "core/core_string_names.h" +#include "core/io/compression.h" +#include "core/object.h" +#include "core/os/os.h" +#include "core/script_language.h" typedef void (*VariantFunc)(Variant &r_ret, Variant &p_self, const Variant **p_args); typedef void (*VariantConstructFunc)(Variant &r_ret, const Variant **p_args); @@ -1895,6 +1895,7 @@ void register_variant_methods() { _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, "ONE", Vector3(1, 1, 1)); _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)); @@ -1904,6 +1905,7 @@ void register_variant_methods() { _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, "ONE", Vector2(1, 1)); _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)); diff --git a/core/variant_op.cpp b/core/variant_op.cpp index bfa69b1fde..d193858966 100644 --- a/core/variant_op.cpp +++ b/core/variant_op.cpp @@ -30,9 +30,9 @@ #include "variant.h" -#include "core_string_names.h" -#include "object.h" -#include "script_language.h" +#include "core/core_string_names.h" +#include "core/object.h" +#include "core/script_language.h" #define CASE_TYPE_ALL(PREFIX, OP) \ CASE_TYPE(PREFIX, OP, INT) \ @@ -521,7 +521,7 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a, const Dictionary *arr_a = reinterpret_cast<const Dictionary *>(p_a._data._mem); const Dictionary *arr_b = reinterpret_cast<const Dictionary *>(p_b._data._mem); - _RETURN((*arr_a == *arr_b) == false); + _RETURN(*arr_a != *arr_b); } CASE_TYPE(math, OP_NOT_EQUAL, ARRAY) { @@ -1656,13 +1656,13 @@ Variant Variant::get_named(const StringName &p_index, bool *r_valid) const { } else if (p_index == CoreStringNames::singleton->a) { return v->a; } else if (p_index == CoreStringNames::singleton->r8) { - return int(v->r * 255.0); + return int(Math::round(v->r * 255.0)); } else if (p_index == CoreStringNames::singleton->g8) { - return int(v->g * 255.0); + return int(Math::round(v->g * 255.0)); } else if (p_index == CoreStringNames::singleton->b8) { - return int(v->b * 255.0); + return int(Math::round(v->b * 255.0)); } else if (p_index == CoreStringNames::singleton->a8) { - return int(v->a * 255.0); + return int(Math::round(v->a * 255.0)); } else if (p_index == CoreStringNames::singleton->h) { return v->get_h(); } else if (p_index == CoreStringNames::singleton->s) { @@ -3521,7 +3521,7 @@ void Variant::interpolate(const Variant &a, const Variant &b, float c, Variant & //not as efficient but.. real_t va = a; real_t vb = b; - r_dst = (1.0 - c) * va + vb * c; + r_dst = va + (vb - va) * c; } else { r_dst = a; @@ -3542,13 +3542,13 @@ void Variant::interpolate(const Variant &a, const Variant &b, float c, Variant & case INT: { int64_t va = a._data._int; int64_t vb = b._data._int; - r_dst = int((1.0 - c) * va + vb * c); + r_dst = int(va + (vb - va) * c); } return; case REAL: { real_t va = a._data._real; real_t vb = b._data._real; - r_dst = (1.0 - c) * va + vb * c; + r_dst = va + (vb - va) * c; } return; case STRING: { @@ -3556,7 +3556,9 @@ void Variant::interpolate(const Variant &a, const Variant &b, float c, Variant & String sa = *reinterpret_cast<const String *>(a._data._mem); String sb = *reinterpret_cast<const String *>(b._data._mem); String dst; - int csize = sb.length() * c + sa.length() * (1.0 - c); + int sa_len = sa.length(); + int sb_len = sb.length(); + int csize = sa_len + (sb_len - sa_len) * c; if (csize == 0) { r_dst = ""; return; diff --git a/core/variant_parser.cpp b/core/variant_parser.cpp index 446aee286d..3d5ae74e92 100644 --- a/core/variant_parser.cpp +++ b/core/variant_parser.cpp @@ -30,10 +30,10 @@ #include "variant_parser.h" +#include "core/io/resource_loader.h" +#include "core/os/input_event.h" +#include "core/os/keyboard.h" #include "core/string_buffer.h" -#include "io/resource_loader.h" -#include "os/input_event.h" -#include "os/keyboard.h" CharType VariantParser::StreamFile::get_char() { @@ -1429,10 +1429,10 @@ Error VariantParser::_parse_tag(Token &token, Stream *p_stream, int &line, Strin break; if (parsing_tag && token.type == TK_PERIOD) { - r_tag.name += "."; //support tags such as [someprop.Anroid] for specific platforms + r_tag.name += "."; //support tags such as [someprop.Android] for specific platforms get_token(p_stream, token, line, r_err_str); } else if (parsing_tag && token.type == TK_COLON) { - r_tag.name += ":"; //support tags such as [someprop.Anroid] for specific platforms + r_tag.name += ":"; //support tags such as [someprop.Android] for specific platforms get_token(p_stream, token, line, r_err_str); } else { parsing_tag = false; diff --git a/core/variant_parser.h b/core/variant_parser.h index 8d95595234..531c1d59cd 100644 --- a/core/variant_parser.h +++ b/core/variant_parser.h @@ -31,9 +31,9 @@ #ifndef VARIANT_PARSER_H #define VARIANT_PARSER_H -#include "os/file_access.h" -#include "resource.h" -#include "variant.h" +#include "core/os/file_access.h" +#include "core/resource.h" +#include "core/variant.h" class VariantParser { public: diff --git a/core/vector.h b/core/vector.h index 52e8758f9b..38a1082407 100644 --- a/core/vector.h +++ b/core/vector.h @@ -36,10 +36,11 @@ * @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 "sort.h" + +#include "core/cowdata.h" +#include "core/error_macros.h" +#include "core/os/memory.h" +#include "core/sort.h" template <class T> class VectorWriteProxy { diff --git a/core/version.h b/core/version.h index d39172865a..2a3a282d82 100644 --- a/core/version.h +++ b/core/version.h @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "version_generated.gen.h" +#include "core/version_generated.gen.h" // Godot versions are of the form <major>.<minor> for the initial release, // and then <major>.<minor>.<patch> for subsequent bugfix releases where <patch> != 0 diff --git a/core/vmap.h b/core/vmap.h index ce0ddc4ec6..9fc99e636d 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 "core/cowdata.h" +#include "core/typedefs.h" template <class T, class V> class VMap { diff --git a/core/vset.h b/core/vset.h index 7f4d8e7f62..9ec329b45f 100644 --- a/core/vset.h +++ b/core/vset.h @@ -31,8 +31,8 @@ #ifndef VSET_H #define VSET_H -#include "typedefs.h" -#include "vector.h" +#include "core/typedefs.h" +#include "core/vector.h" template <class T> class VSet { diff --git a/doc/classes/@GDScript.xml b/doc/classes/@GDScript.xml index 2cfdfafea1..493f55e89b 100644 --- a/doc/classes/@GDScript.xml +++ b/doc/classes/@GDScript.xml @@ -55,7 +55,7 @@ <argument index="0" name="s" type="float"> </argument> <description> - Returns the absolute value of parameter [code]s[/code] (i.e. unsigned value, works for integer and float). + Returns the absolute value of parameter [code]s[/code] (i.e. unsigned value, works for integer and float). [codeblock] # a is 1 a = abs(-1) @@ -120,9 +120,9 @@ <method name="atan2"> <return type="float"> </return> - <argument index="0" name="x" type="float"> + <argument index="0" name="y" type="float"> </argument> - <argument index="1" name="y" type="float"> + <argument index="1" name="x" type="float"> </argument> <description> Returns the arc tangent of [code]y/x[/code] in radians. Use to get the angle of tangent [code]y/x[/code]. To compute the value, the method takes into account the sign of both arguments in order to determine the quadrant. @@ -373,7 +373,7 @@ <description> Returns the floating-point remainder of [code]x/y[/code] that wraps equally in positive and negative. [codeblock] - var i = -10; + var i = -10 while i < 0: prints(i, fposmod(i, 10)) i += 1 @@ -412,7 +412,7 @@ </description> </method> <method name="get_stack"> - <return type="void"> + <return type="Array"> </return> <description> </description> diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index d2c6a853ad..65d339c0d5 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -29,6 +29,8 @@ <member name="Geometry" type="Geometry" setter="" getter=""> [Geometry] singleton </member> + <member name="GodotSharp" type="GodotSharp" setter="" getter=""> + </member> <member name="IP" type="IP" setter="" getter=""> [IP] singleton </member> @@ -884,8 +886,10 @@ Middle Mouse Button </constant> <constant name="BUTTON_XBUTTON1" value="8" enum="ButtonList"> + Extra Mouse Button 1 </constant> <constant name="BUTTON_XBUTTON2" value="9" enum="ButtonList"> + Extra Mouse Button 2 </constant> <constant name="BUTTON_WHEEL_UP" value="4" enum="ButtonList"> Mouse wheel up @@ -909,8 +913,10 @@ Middle Mouse Button Mask </constant> <constant name="BUTTON_MASK_XBUTTON1" value="128" enum="ButtonList"> + Extra Mouse Button 1 Mask </constant> <constant name="BUTTON_MASK_XBUTTON2" value="256" enum="ButtonList"> + Extra Mouse Button 2 Mask </constant> <constant name="JOY_BUTTON_0" value="0" enum="JoystickList"> Joypad Button 0 diff --git a/doc/classes/AABB.xml b/doc/classes/AABB.xml index b9061e0b87..2e0d0c15b2 100644 --- a/doc/classes/AABB.xml +++ b/doc/classes/AABB.xml @@ -7,7 +7,7 @@ AABB consists of a position, a size, and several utility functions. It is typically used for fast overlap tests. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/math/index.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/math/index.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/ARVRController.xml b/doc/classes/ARVRController.xml index 9c306c3ea4..d3d6fce537 100644 --- a/doc/classes/ARVRController.xml +++ b/doc/classes/ARVRController.xml @@ -6,7 +6,7 @@ <description> This is a helper spatial node that is linked to the tracking of controllers. It also offers several handy pass throughs to the state of buttons and such on the controllers. Controllers are linked by their id. You can create controller nodes before the controllers are available. Say your game always uses two controllers (one for each hand) you can predefine the controllers with id 1 and 2 and they will become active as soon as the controllers are identified. If you expect additional controllers to be used you should react to the signals and add ARVRController nodes to your scene. - The position of the controller node is automatically updated by the ARVR Server. This makes this node ideal to add child nodes to visualise the controller. + The position of the controller node is automatically updated by the ARVR Server. This makes this node ideal to add child nodes to visualise the controller. </description> <tutorials> </tutorials> diff --git a/doc/classes/AnimatedTexture.xml b/doc/classes/AnimatedTexture.xml index f874c43ef1..9dc58ed195 100644 --- a/doc/classes/AnimatedTexture.xml +++ b/doc/classes/AnimatedTexture.xml @@ -9,1034 +9,46 @@ <demos> </demos> <methods> + <method name="get_frame_delay" qualifiers="const"> + <return type="float"> + </return> + <argument index="0" name="frame" type="int"> + </argument> + <description> + </description> + </method> + <method name="get_frame_texture" qualifiers="const"> + <return type="Texture"> + </return> + <argument index="0" name="frame" type="int"> + </argument> + <description> + </description> + </method> + <method name="set_frame_delay"> + <return type="void"> + </return> + <argument index="0" name="frame" type="int"> + </argument> + <argument index="1" name="delay" type="float"> + </argument> + <description> + </description> + </method> + <method name="set_frame_texture"> + <return type="void"> + </return> + <argument index="0" name="frame" type="int"> + </argument> + <argument index="1" name="texture" type="Texture"> + </argument> + <description> + </description> + </method> </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> diff --git a/doc/classes/Animation.xml b/doc/classes/Animation.xml index 74c6796b06..f189e6451d 100644 --- a/doc/classes/Animation.xml +++ b/doc/classes/Animation.xml @@ -5,10 +5,19 @@ </brief_description> <description> An Animation resource contains data used to animate everything in the engine. Animations are divided into tracks, and each track must be linked to a node. The state of that node can be changed through time, by adding timed keys (events) to the track. - Animations are just data containers, and must be added to odes such as an [AnimationPlayer] or [AnimationTreePlayer] to be played back. + [codeblock] + # This creates an animation that makes the node "Enemy" move to the right by + # 100 pixels in 1 second. + var animation = Animation.new() + var track_index = animation.add_track(Animation.TYPE_VALUE) + animation.track_set_path(track_index, "Enemy:position.x") + animation.track_insert_key(track_index, 0.0, 0) + animation.track_insert_key(track_index, 0.5, 100) + [/codeblock] + Animations are just data containers, and must be added to nodes such as an [AnimationPlayer] or [AnimationTreePlayer] to be played back. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/animation/index.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/animation/index.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/AnimationPlayer.xml b/doc/classes/AnimationPlayer.xml index 6dc91a234a..3b3638a4f3 100644 --- a/doc/classes/AnimationPlayer.xml +++ b/doc/classes/AnimationPlayer.xml @@ -7,8 +7,8 @@ An animation player is used for general purpose playback of [Animation] resources. It contains a dictionary of animations (referenced by name) and custom blend times between their transitions. Additionally, animations can be played and blended in different channels. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/getting_started/step_by_step/animations.html</link> - <link>http://docs.godotengine.org/en/3.0/tutorials/animation/index.html</link> + <link>https://docs.godotengine.org/en/latest/getting_started/step_by_step/animations.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/animation/index.html</link> </tutorials> <demos> </demos> @@ -238,7 +238,7 @@ The default time in which to blend animations. Ranges from 0 to 4096 with 0.01 precision. Default value: [code]0[/code]. </member> <member name="playback_process_mode" type="int" setter="set_animation_process_mode" getter="get_animation_process_mode" enum="AnimationPlayer.AnimationProcessMode"> - The process notification in which to update animations. Default value: [enum ANIMATION_PROCESS_IDLE]. + The process notification in which to update animations. Default value: [code]ANIMATION_PROCESS_IDLE[/code]. </member> <member name="playback_speed" type="float" setter="set_speed_scale" getter="get_speed_scale"> The speed scaling ratio. For instance, if this value is 1 then the animation plays at normal speed. If it's 0.5 then it plays at half speed. If it's 2 then it plays at double speed. Default value: [code]1[/code]. diff --git a/doc/classes/AnimationTreePlayer.xml b/doc/classes/AnimationTreePlayer.xml index 8c32d5f6a3..a081c64f6d 100644 --- a/doc/classes/AnimationTreePlayer.xml +++ b/doc/classes/AnimationTreePlayer.xml @@ -618,7 +618,7 @@ Once set, Animation nodes can be added to the AnimationTreePlayer. </member> <member name="playback_process_mode" type="int" setter="set_animation_process_mode" getter="get_animation_process_mode" enum="AnimationTreePlayer.AnimationProcessMode"> - The thread in which to update animations. Default value: [enum ANIMATION_PROCESS_IDLE]. + The thread in which to update animations. Default value: [code]ANIMATION_PROCESS_IDLE[/code]. </member> </members> <constants> diff --git a/doc/classes/Area.xml b/doc/classes/Area.xml index 5c56b5e21b..970d09a2ac 100644 --- a/doc/classes/Area.xml +++ b/doc/classes/Area.xml @@ -142,14 +142,14 @@ </members> <signals> <signal name="area_entered"> - <argument index="0" name="area" type="Object"> + <argument index="0" name="area" type="Area"> </argument> <description> Emitted when another area enters. </description> </signal> <signal name="area_exited"> - <argument index="0" name="area" type="Object"> + <argument index="0" name="area" type="Area"> </argument> <description> Emitted when another area exits. @@ -158,7 +158,7 @@ <signal name="area_shape_entered"> <argument index="0" name="area_id" type="int"> </argument> - <argument index="1" name="area" type="Object"> + <argument index="1" name="area" type="Area"> </argument> <argument index="2" name="area_shape" type="int"> </argument> @@ -171,7 +171,7 @@ <signal name="area_shape_exited"> <argument index="0" name="area_id" type="int"> </argument> - <argument index="1" name="area" type="Object"> + <argument index="1" name="area" type="Area"> </argument> <argument index="2" name="area_shape" type="int"> </argument> @@ -182,14 +182,14 @@ </description> </signal> <signal name="body_entered"> - <argument index="0" name="body" type="Object"> + <argument index="0" name="body" type="Node"> </argument> <description> Emitted when a [PhysicsBody] object enters. </description> </signal> <signal name="body_exited"> - <argument index="0" name="body" type="Object"> + <argument index="0" name="body" type="Node"> </argument> <description> Emitted when a [PhysicsBody] object exits. @@ -198,7 +198,7 @@ <signal name="body_shape_entered"> <argument index="0" name="body_id" type="int"> </argument> - <argument index="1" name="body" type="Object"> + <argument index="1" name="body" type="Node"> </argument> <argument index="2" name="body_shape" type="int"> </argument> @@ -211,7 +211,7 @@ <signal name="body_shape_exited"> <argument index="0" name="body_id" type="int"> </argument> - <argument index="1" name="body" type="Object"> + <argument index="1" name="body" type="Node"> </argument> <argument index="2" name="body_shape" type="int"> </argument> diff --git a/doc/classes/Area2D.xml b/doc/classes/Area2D.xml index c50ccefc4c..b77a931201 100644 --- a/doc/classes/Area2D.xml +++ b/doc/classes/Area2D.xml @@ -130,14 +130,14 @@ </members> <signals> <signal name="area_entered"> - <argument index="0" name="area" type="Object"> + <argument index="0" name="area" type="Area2D"> </argument> <description> Emitted when another area enters. </description> </signal> <signal name="area_exited"> - <argument index="0" name="area" type="Object"> + <argument index="0" name="area" type="Area2D"> </argument> <description> Emitted when another area exits. @@ -146,7 +146,7 @@ <signal name="area_shape_entered"> <argument index="0" name="area_id" type="int"> </argument> - <argument index="1" name="area" type="Object"> + <argument index="1" name="area" type="Area2D"> </argument> <argument index="2" name="area_shape" type="int"> </argument> @@ -159,7 +159,7 @@ <signal name="area_shape_exited"> <argument index="0" name="area_id" type="int"> </argument> - <argument index="1" name="area" type="Object"> + <argument index="1" name="area" type="Area2D"> </argument> <argument index="2" name="area_shape" type="int"> </argument> @@ -170,14 +170,14 @@ </description> </signal> <signal name="body_entered"> - <argument index="0" name="body" type="Object"> + <argument index="0" name="body" type="PhysicsBody2D"> </argument> <description> Emitted when a [PhysicsBody2D] object enters. </description> </signal> <signal name="body_exited"> - <argument index="0" name="body" type="Object"> + <argument index="0" name="body" type="PhysicsBody2D"> </argument> <description> Emitted when a [PhysicsBody2D] object exits. @@ -186,7 +186,7 @@ <signal name="body_shape_entered"> <argument index="0" name="body_id" type="int"> </argument> - <argument index="1" name="body" type="Object"> + <argument index="1" name="body" type="PhysicsBody2D"> </argument> <argument index="2" name="body_shape" type="int"> </argument> @@ -199,7 +199,7 @@ <signal name="body_shape_exited"> <argument index="0" name="body_id" type="int"> </argument> - <argument index="1" name="body" type="Object"> + <argument index="1" name="body" type="PhysicsBody2D"> </argument> <argument index="2" name="body_shape" type="int"> </argument> diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml index 9c5ae8ebd0..b013b3c4ae 100644 --- a/doc/classes/Array.xml +++ b/doc/classes/Array.xml @@ -84,14 +84,14 @@ </description> </method> <method name="append"> - <argument index="0" name="value" type="var"> + <argument index="0" name="value" type="Variant"> </argument> <description> Append an element at the end of the array (alias of [method push_back]). </description> </method> <method name="back"> - <return type="var"> + <return type="Variant"> </return> <description> Returns the last element of the array if the array is not empty (size>0). @@ -100,7 +100,7 @@ <method name="bsearch"> <return type="int"> </return> - <argument index="0" name="value" type="var"> + <argument index="0" name="value" type="Variant"> </argument> <argument index="1" name="before" type="bool" default="True"> </argument> @@ -111,7 +111,7 @@ <method name="bsearch_custom"> <return type="int"> </return> - <argument index="0" name="value" type="var"> + <argument index="0" name="value" type="Variant"> </argument> <argument index="1" name="obj" type="Object"> </argument> @@ -120,7 +120,7 @@ <argument index="3" name="before" type="bool" default="True"> </argument> <description> - Finds the index of an existing value (or the insertion index that maintains sorting order, if the value is not yet present in the array) using binary search and a custom comparison method. Optionally, a before specifier can be passed. If false, the returned index comes after all existing entries of the value in the array. The custom method receives two arguments (an element from the array and the value searched for) and must return true if the first argument is less than the second, and return false otherwise. Note that calling bsearch on an unsorted array results in unexpected behavior. + Finds the index of an existing value (or the insertion index that maintains sorting order, if the value is not yet present in the array) using binary search and a custom comparison method. Optionally, a before specifier can be passed. If false, the returned index comes after all existing entries of the value in the array. The custom method receives two arguments (an element from the array and the value searched for) and must return true if the first argument is less than the second, and return false otherwise. Note that calling bsearch on an unsorted array results in unexpected behavior. </description> </method> <method name="clear"> @@ -131,7 +131,7 @@ <method name="count"> <return type="int"> </return> - <argument index="0" name="value" type="var"> + <argument index="0" name="value" type="Variant"> </argument> <description> Return the amount of times an element is in the array. @@ -155,7 +155,7 @@ </description> </method> <method name="erase"> - <argument index="0" name="value" type="var"> + <argument index="0" name="value" type="Variant"> </argument> <description> Remove the first occurrence of a value from the array. @@ -164,7 +164,7 @@ <method name="find"> <return type="int"> </return> - <argument index="0" name="what" type="var"> + <argument index="0" name="what" type="Variant"> </argument> <argument index="1" name="from" type="int" default="0"> </argument> @@ -175,14 +175,14 @@ <method name="find_last"> <return type="int"> </return> - <argument index="0" name="value" type="var"> + <argument index="0" name="value" type="Variant"> </argument> <description> Searches the array in reverse order for a value and returns its index or -1 if not found. </description> </method> <method name="front"> - <return type="var"> + <return type="Variant"> </return> <description> Returns the first element of the array if the array is not empty (size>0). @@ -191,7 +191,7 @@ <method name="has"> <return type="bool"> </return> - <argument index="0" name="value" type="var"> + <argument index="0" name="value" type="Variant"> </argument> <description> Return true if the array contains given value. @@ -213,40 +213,54 @@ <method name="insert"> <argument index="0" name="position" type="int"> </argument> - <argument index="1" name="value" type="var"> + <argument index="1" name="value" type="Variant"> </argument> <description> - Insert a new element at a given position in the array. The position must be valid, or at the end of the array (pos==size()). + Insert a new element at a given position in the array. The position must be valid, or at the end of the array ([code]pos == size()[/code]). </description> </method> <method name="invert"> <description> - Reverse the order of the elements in the array (so first element will now be the last) and return reference to the array. + Reverse the order of the elements in the array. + </description> + </method> + <method name="max"> + <return type="Variant"> + </return> + <description> + Return maximum value contained in the array if all elements are of comparable types. If the elements can't be compared, [code]null[/code] is returned. + </description> + </method> + <method name="min"> + <return type="Variant"> + </return> + <description> + Return minimum value contained in the array if all elements are of comparable types. If the elements can't be compared, [code]null[/code] is returned. </description> </method> <method name="pop_back"> - <return type="var"> + <return type="Variant"> </return> <description> Remove the last element of the array. </description> </method> <method name="pop_front"> - <return type="var"> + <return type="Variant"> </return> <description> Remove the first element of the array. </description> </method> <method name="push_back"> - <argument index="0" name="value" type="var"> + <argument index="0" name="value" type="Variant"> </argument> <description> Append an element at the end of the array. </description> </method> <method name="push_front"> - <argument index="0" name="value" type="var"> + <argument index="0" name="value" type="Variant"> </argument> <description> Add an element at the beginning of the array. @@ -269,7 +283,7 @@ <method name="rfind"> <return type="int"> </return> - <argument index="0" name="what" type="var"> + <argument index="0" name="what" type="Variant"> </argument> <argument index="1" name="from" type="int" default="-1"> </argument> @@ -307,7 +321,7 @@ static func sort(a, b): if a[0] < b[0]: return true - return false + return false var my_items = [[5, "Potato"], [9, "Rice"], [4, "Tomato"]] my_items.sort_custom(MyCustomSorter, "sort") diff --git a/doc/classes/ArrayMesh.xml b/doc/classes/ArrayMesh.xml index 1e2478dd14..7806cf4ce4 100644 --- a/doc/classes/ArrayMesh.xml +++ b/doc/classes/ArrayMesh.xml @@ -3,6 +3,23 @@ <brief_description> </brief_description> <description> + The [code]ArrayMesh[/code] is used to construct a [Mesh] by specifying the attributes as arrays. The most basic example is the creation of a single triangle + [codeblock] + var vertices = PoolVector3Array() + vertices.push_back(Vector3(0,1,0)) + vertices.push_back(Vector3(1,0,0)) + vertices.push_back(Vector3(0,0,1)) + # Initialize the ArrayMesh. + var arr_mesh = ArrayMesh.new() + var arrays = [] + arrays.resize(ArrayMesh.ARRAY_MAX) + arrays[ArrayMesh.ARRAY_VERTEX] = vertices + # Create the Mesh. + arr_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays) + var m = MeshInstance.new() + m.mesh = arr_mesh + [/codeblock] + The [code]MeshInstance[/code] is ready to be added to the SceneTree to be shown. </description> <tutorials> </tutorials> @@ -30,7 +47,7 @@ </argument> <description> Creates a new surface. - Surfaces are created to be rendered using a "primitive", which may be PRIMITIVE_POINTS, PRIMITIVE_LINES, PRIMITIVE_LINE_STRIP, PRIMITIVE_LINE_LOOP, PRIMITIVE_TRIANGLES, PRIMITIVE_TRIANGLE_STRIP, PRIMITIVE_TRIANGLE_FAN. See [Mesh] for details. (As a note, when using indices, it is recommended to only use points, lines or triangles). [method get_surface_count] will become the surf_idx for this new surface. + Surfaces are created to be rendered using a "primitive", which may be PRIMITIVE_POINTS, PRIMITIVE_LINES, PRIMITIVE_LINE_STRIP, PRIMITIVE_LINE_LOOP, PRIMITIVE_TRIANGLES, PRIMITIVE_TRIANGLE_STRIP, PRIMITIVE_TRIANGLE_FAN. See [Mesh] for details. (As a note, when using indices, it is recommended to only use points, lines or triangles). [method Mesh.get_surface_count] will become the [code]surf_idx[/code] for this new surface. The [code]arrays[/code] argument is an array of arrays. See [enum ArrayType] for the values used in this array. For example, [code]arrays[0][/code] is the array of vertices. That first vertex sub-array is always required; the others are optional. Adding an index array puts this function into "index mode" where the vertex and other arrays become the sources of data and the index array defines the vertex order. All sub-arrays must have the same length as the vertex array or be empty, except for [code]ARRAY_INDEX[/code] if it is used. Adding an index array puts this function into "index mode" where the vertex and other arrays become the sources of data, and the index array defines the order of the vertices. Godot uses clockwise winding order for front faces of triangle primitive modes. @@ -66,13 +83,6 @@ Returns the name of the blend shape at this index. </description> </method> - <method name="get_surface_count" qualifiers="const"> - <return type="int"> - </return> - <description> - Return the amount of surfaces that the [code]ArrayMesh[/code] holds. - </description> - </method> <method name="lightmap_unwrap"> <return type="int" enum="Error"> </return> @@ -118,24 +128,6 @@ Return the length in vertices of the vertex array in the requested surface (see [method add_surface_from_arrays]). </description> </method> - <method name="surface_get_arrays" qualifiers="const"> - <return type="Array"> - </return> - <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"> - <return type="Array"> - </return> - <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"> <return type="int"> </return> @@ -145,15 +137,6 @@ Return the format mask of the requested surface (see [method add_surface_from_arrays]). </description> </method> - <method name="surface_get_material" qualifiers="const"> - <return type="Material"> - </return> - <argument index="0" name="surf_idx" type="int"> - </argument> - <description> - Return a [Material] in a given surface. Surface is rendered using this material. - </description> - </method> <method name="surface_get_name" qualifiers="const"> <return type="String"> </return> diff --git a/doc/classes/AudioEffectBandLimitFilter.xml b/doc/classes/AudioEffectBandLimitFilter.xml index 9eba806ad5..c9ddbd5b9a 100644 --- a/doc/classes/AudioEffectBandLimitFilter.xml +++ b/doc/classes/AudioEffectBandLimitFilter.xml @@ -4,7 +4,7 @@ Adds a band limit filter to the Audio Bus. </brief_description> <description> - Limits the frequencies in a range around the [member cutoff_hz] and allows frequencies outside of this range to pass. + Limits the frequencies in a range around the [member AudioEffectFilter.cutoff_hz] and allows frequencies outside of this range to pass. </description> <tutorials> </tutorials> diff --git a/doc/classes/AudioEffectBandPassFilter.xml b/doc/classes/AudioEffectBandPassFilter.xml index 11aab3e86d..7f4c9f4632 100644 --- a/doc/classes/AudioEffectBandPassFilter.xml +++ b/doc/classes/AudioEffectBandPassFilter.xml @@ -4,7 +4,7 @@ Adds a band pass filter to the Audio Bus. </brief_description> <description> - Attenuates the frequencies inside of a range around the [member cutoff_hz] and cuts frequencies outside of this band. + Attenuates the frequencies inside of a range around the [member AudioEffectFilter.cutoff_hz] and cuts frequencies outside of this band. </description> <tutorials> </tutorials> diff --git a/doc/classes/AudioEffectHighPassFilter.xml b/doc/classes/AudioEffectHighPassFilter.xml index 3d487fc783..6c97199cb9 100644 --- a/doc/classes/AudioEffectHighPassFilter.xml +++ b/doc/classes/AudioEffectHighPassFilter.xml @@ -4,7 +4,7 @@ Adds a high pass filter to the Audio Bus. </brief_description> <description> - Cuts frequencies lower than the [member cutoff_hz] and allows higher frequencies to pass. + Cuts frequencies lower than the [member AudioEffectFilter.cutoff_hz] and allows higher frequencies to pass. </description> <tutorials> </tutorials> diff --git a/doc/classes/AudioEffectLowPassFilter.xml b/doc/classes/AudioEffectLowPassFilter.xml index 3facd8b665..7048a56e6c 100644 --- a/doc/classes/AudioEffectLowPassFilter.xml +++ b/doc/classes/AudioEffectLowPassFilter.xml @@ -4,7 +4,7 @@ Adds a low pass filter to the Audio Bus. </brief_description> <description> - Cuts frequencies higher than the [member cutoff_hz] and allows lower frequencies to pass. + Cuts frequencies higher than the [member AudioEffectFilter.cutoff_hz] and allows lower frequencies to pass. </description> <tutorials> </tutorials> diff --git a/doc/classes/AudioEffectNotchFilter.xml b/doc/classes/AudioEffectNotchFilter.xml index 741931f262..0378934890 100644 --- a/doc/classes/AudioEffectNotchFilter.xml +++ b/doc/classes/AudioEffectNotchFilter.xml @@ -4,7 +4,7 @@ Adds a notch filter to the Audio Bus. </brief_description> <description> - Attenuates frequencies in a narrow band around the [member cutoff_hz] and cuts frequencies outside of this range. + Attenuates frequencies in a narrow band around the [member AudioEffectFilter.cutoff_hz] and cuts frequencies outside of this range. </description> <tutorials> </tutorials> diff --git a/doc/classes/AudioServer.xml b/doc/classes/AudioServer.xml index 37756bcfd8..3ae5454e65 100644 --- a/doc/classes/AudioServer.xml +++ b/doc/classes/AudioServer.xml @@ -7,7 +7,7 @@ AudioServer is a low level server interface for audio access. It is in charge of creating sample data (playable audio) as well as its playback via a voice interface. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/audio/audio_buses.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/audio/audio_buses.html</link> </tutorials> <demos> </demos> @@ -385,6 +385,8 @@ <constant name="SPEAKER_MODE_STEREO" value="0" enum="SpeakerMode"> Two or fewer speakers are detected. </constant> + <constant name="SPEAKER_SURROUND_31" value="1" enum="SpeakerMode"> + </constant> <constant name="SPEAKER_SURROUND_51" value="2" enum="SpeakerMode"> A 5.1 channel surround setup detected. </constant> diff --git a/doc/classes/AudioStream.xml b/doc/classes/AudioStream.xml index 753a506058..96ce53b14f 100644 --- a/doc/classes/AudioStream.xml +++ b/doc/classes/AudioStream.xml @@ -7,7 +7,7 @@ Base class for audio streams. Audio streams are used for music playback, or other types of streamed sounds that don't fit or require more flexibility than a [Sample]. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/audio/audio_streams.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/audio/audio_streams.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/AudioStreamPlayer.xml b/doc/classes/AudioStreamPlayer.xml index 26d0b1a83d..be4e4f137a 100644 --- a/doc/classes/AudioStreamPlayer.xml +++ b/doc/classes/AudioStreamPlayer.xml @@ -7,8 +7,8 @@ Plays background audio. </description> <tutorials> - <link>http://docs.godotengine.org/en/latest/learning/features/audio/index.html</link> - <link>http://docs.godotengine.org/en/3.0/tutorials/audio/audio_streams.html</link> + <link>https://docs.godotengine.org/en/latest/learning/features/audio/index.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/audio/audio_streams.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/AudioStreamPlayer2D.xml b/doc/classes/AudioStreamPlayer2D.xml index 5cc87e0e7a..3b81894c14 100644 --- a/doc/classes/AudioStreamPlayer2D.xml +++ b/doc/classes/AudioStreamPlayer2D.xml @@ -7,8 +7,8 @@ Plays audio that dampens with distance from screen center. </description> <tutorials> - <link>http://docs.godotengine.org/en/latest/learning/features/audio/index.html</link> - <link>http://docs.godotengine.org/en/3.0/tutorials/audio/audio_streams.html</link> + <link>https://docs.godotengine.org/en/latest/learning/features/audio/index.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/audio/audio_streams.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/AudioStreamPlayer3D.xml b/doc/classes/AudioStreamPlayer3D.xml index e61515ffc0..5841f1f6dc 100644 --- a/doc/classes/AudioStreamPlayer3D.xml +++ b/doc/classes/AudioStreamPlayer3D.xml @@ -7,8 +7,8 @@ Plays a sound effect with directed sound effects, dampens with distance if needed, generates effect of hearable position in space. </description> <tutorials> - <link>http://docs.godotengine.org/en/latest/learning/features/audio/index.html</link> - <link>http://docs.godotengine.org/en/3.0/tutorials/audio/audio_streams.html</link> + <link>https://docs.godotengine.org/en/latest/learning/features/audio/index.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/audio/audio_streams.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/BakedLightmap.xml b/doc/classes/BakedLightmap.xml index 966b6dd7c4..8e27dc8d5d 100644 --- a/doc/classes/BakedLightmap.xml +++ b/doc/classes/BakedLightmap.xml @@ -7,7 +7,7 @@ Baked lightmaps are an alternative workflow for adding indirect (or baked) lighting to a scene. Unlike the [GIProbe] approach, baked lightmaps work fine on low-end PCs and mobile devices as they consume almost no resources in run-time. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/3d/baked_lightmaps.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/3d/baked_lightmaps.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/BaseButton.xml b/doc/classes/BaseButton.xml index 3ff8634010..3364770280 100644 --- a/doc/classes/BaseButton.xml +++ b/doc/classes/BaseButton.xml @@ -106,6 +106,8 @@ <constant name="DRAW_DISABLED" value="3" enum="DrawMode"> The state of buttons are disabled. </constant> + <constant name="DRAW_HOVER_PRESSED" value="4" enum="DrawMode"> + </constant> <constant name="ACTION_MODE_BUTTON_PRESS" value="0" enum="ActionMode"> Require just a press to consider the button clicked. </constant> diff --git a/doc/classes/Basis.xml b/doc/classes/Basis.xml index fe8debe1a9..95f6346321 100644 --- a/doc/classes/Basis.xml +++ b/doc/classes/Basis.xml @@ -8,8 +8,8 @@ For such use, it is composed of a scaling and a rotation matrix, in that order (M = R.S). </description> <tutorials> - <link>http://docs.godotengine.org/en/latest/tutorials/3d/using_transforms.html</link> - <link>http://docs.godotengine.org/en/latest/tutorials/math/rotations.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/3d/using_transforms.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/math/rotations.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/CPUParticles2D.xml b/doc/classes/CPUParticles2D.xml new file mode 100644 index 0000000000..6d115e2650 --- /dev/null +++ b/doc/classes/CPUParticles2D.xml @@ -0,0 +1,193 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="CPUParticles2D" inherits="Node2D" 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="CPUParticles2D.DrawOrder"> + </member> + <member name="emission_colors" type="PoolColorArray" setter="set_emission_colors" getter="get_emission_colors"> + </member> + <member name="emission_normals" type="PoolVector2Array" setter="set_emission_normals" getter="get_emission_normals"> + </member> + <member name="emission_points" type="PoolVector2Array" setter="set_emission_points" getter="get_emission_points"> + </member> + <member name="emission_rect_extents" type="Vector2" setter="set_emission_rect_extents" getter="get_emission_rect_extents"> + </member> + <member name="emission_shape" type="int" setter="set_emission_shape" getter="get_emission_shape" enum="CPUParticles2D.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="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="Vector2" 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="normalmap" type="Texture" setter="set_normalmap" getter="get_normalmap"> + </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> + <member name="texture" type="Texture" setter="set_texture" getter="get_texture"> + </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="PARAM_INITIAL_LINEAR_VELOCITY" value="0" enum="Parameter"> + </constant> + <constant name="PARAM_ANGULAR_VELOCITY" value="1" enum="Parameter"> + </constant> + <constant name="PARAM_ORBIT_VELOCITY" value="2" enum="Parameter"> + </constant> + <constant name="PARAM_LINEAR_ACCEL" value="3" enum="Parameter"> + </constant> + <constant name="PARAM_RADIAL_ACCEL" value="4" enum="Parameter"> + </constant> + <constant name="PARAM_TANGENTIAL_ACCEL" value="5" enum="Parameter"> + </constant> + <constant name="PARAM_DAMPING" value="6" enum="Parameter"> + </constant> + <constant name="PARAM_ANGLE" value="7" enum="Parameter"> + </constant> + <constant name="PARAM_SCALE" value="8" enum="Parameter"> + </constant> + <constant name="PARAM_HUE_VARIATION" value="9" enum="Parameter"> + </constant> + <constant name="PARAM_ANIM_SPEED" value="10" enum="Parameter"> + </constant> + <constant name="PARAM_ANIM_OFFSET" value="11" enum="Parameter"> + </constant> + <constant name="PARAM_MAX" value="12" enum="Parameter"> + </constant> + <constant name="FLAG_ALIGN_Y_TO_VELOCITY" value="0" enum="Flags"> + </constant> + <constant name="FLAG_MAX" value="2" enum="Flags"> + </constant> + <constant name="EMISSION_SHAPE_POINT" value="0" enum="EmissionShape"> + </constant> + <constant name="EMISSION_SHAPE_CIRCLE" value="1" enum="EmissionShape"> + </constant> + <constant name="EMISSION_SHAPE_RECTANGLE" 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/CanvasItem.xml b/doc/classes/CanvasItem.xml index a04e38af5c..4bae656740 100644 --- a/doc/classes/CanvasItem.xml +++ b/doc/classes/CanvasItem.xml @@ -11,8 +11,8 @@ Ultimately, a transform notification can be requested, which will notify the node that its global position changed in case the parent tree changed. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/2d/2d_transforms.html</link> - <link>http://docs.godotengine.org/en/3.0/tutorials/2d/custom_drawing_in_2d.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/2d/2d_transforms.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/2d/custom_drawing_in_2d.html</link> </tutorials> <demos> </demos> @@ -330,6 +330,12 @@ Draws a textured rectangle region at a given position, optionally modulated by a color. Transpose swaps the x and y coordinates when reading the texture. </description> </method> + <method name="force_update_transform"> + <return type="void"> + </return> + <description> + </description> + </method> <method name="get_canvas" qualifiers="const"> <return type="RID"> </return> @@ -570,7 +576,7 @@ Disable blending mode. Colors including alpha are written as is. Only applicable for render targets with a transparent background. No lighting will be applied. </constant> <constant name="NOTIFICATION_TRANSFORM_CHANGED" value="29"> - Canvas item transform has changed. Only received if requested. + Canvas item transform has changed. Notification is only received if enabled by [method set_notify_transform] or [method set_notify_local_transform]. </constant> <constant name="NOTIFICATION_DRAW" value="30"> CanvasItem is requested to draw. diff --git a/doc/classes/CanvasLayer.xml b/doc/classes/CanvasLayer.xml index d4412e15c9..c39b47ab07 100644 --- a/doc/classes/CanvasLayer.xml +++ b/doc/classes/CanvasLayer.xml @@ -7,8 +7,8 @@ Canvas drawing layer. [CanvasItem] nodes that are direct or indirect children of a [code]CanvasLayer[/code] will be drawn in that layer. The layer is a numeric index that defines the draw order. The default 2D scene renders with index 0, so a [code]CanvasLayer[/code] with index -1 will be drawn below, and one with index 1 will be drawn above. This is very useful for HUDs (in layer 1+ or above), or backgrounds (in layer -1 or below). </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/2d/2d_transforms.html</link> - <link>http://docs.godotengine.org/en/3.0/tutorials/2d/canvas_layers.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/2d/2d_transforms.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/2d/canvas_layers.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/CheckBox.xml b/doc/classes/CheckBox.xml index fb2cf64d98..a2a7cf85e8 100644 --- a/doc/classes/CheckBox.xml +++ b/doc/classes/CheckBox.xml @@ -31,10 +31,14 @@ </theme_item> <theme_item name="font_color_hover" type="Color"> </theme_item> + <theme_item name="font_color_hover_pressed" type="Color"> + </theme_item> <theme_item name="font_color_pressed" type="Color"> </theme_item> <theme_item name="hover" type="StyleBox"> </theme_item> + <theme_item name="hover_pressed" type="StyleBox"> + </theme_item> <theme_item name="hseparation" type="int"> </theme_item> <theme_item name="normal" type="StyleBox"> diff --git a/doc/classes/CheckButton.xml b/doc/classes/CheckButton.xml index deba9a17b6..24875017fe 100644 --- a/doc/classes/CheckButton.xml +++ b/doc/classes/CheckButton.xml @@ -29,10 +29,14 @@ </theme_item> <theme_item name="font_color_hover" type="Color"> </theme_item> + <theme_item name="font_color_hover_pressed" type="Color"> + </theme_item> <theme_item name="font_color_pressed" type="Color"> </theme_item> <theme_item name="hover" type="StyleBox"> </theme_item> + <theme_item name="hover_pressed" type="StyleBox"> + </theme_item> <theme_item name="hseparation" type="int"> </theme_item> <theme_item name="normal" type="StyleBox"> diff --git a/doc/classes/ClippedCamera.xml b/doc/classes/ClippedCamera.xml new file mode 100644 index 0000000000..509ddb01fc --- /dev/null +++ b/doc/classes/ClippedCamera.xml @@ -0,0 +1,87 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="ClippedCamera" inherits="Camera" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + <method name="add_exception"> + <return type="void"> + </return> + <argument index="0" name="node" type="Object"> + </argument> + <description> + </description> + </method> + <method name="add_exception_rid"> + <return type="void"> + </return> + <argument index="0" name="rid" type="RID"> + </argument> + <description> + </description> + </method> + <method name="clear_exceptions"> + <return type="void"> + </return> + <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="remove_exception"> + <return type="void"> + </return> + <argument index="0" name="node" type="Object"> + </argument> + <description> + </description> + </method> + <method name="remove_exception_rid"> + <return type="void"> + </return> + <argument index="0" name="rid" type="RID"> + </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> + </methods> + <members> + <member name="clip_to_areas" type="bool" setter="set_clip_to_areas" getter="is_clip_to_areas_enabled"> + </member> + <member name="clip_to_bodies" type="bool" setter="set_clip_to_bodies" getter="is_clip_to_bodies_enabled"> + </member> + <member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask"> + </member> + <member name="margin" type="float" setter="set_margin" getter="get_margin"> + </member> + <member name="process_mode" type="int" setter="set_process_mode" getter="get_process_mode" enum="ClippedCamera.ProcessMode"> + </member> + </members> + <constants> + <constant name="CLIP_PROCESS_PHYSICS" value="0" enum="ProcessMode"> + </constant> + <constant name="CLIP_PROCESS_IDLE" value="1" enum="ProcessMode"> + </constant> + </constants> +</class> diff --git a/doc/classes/CollisionObject.xml b/doc/classes/CollisionObject.xml index 22b9725121..5e4e740498 100644 --- a/doc/classes/CollisionObject.xml +++ b/doc/classes/CollisionObject.xml @@ -191,9 +191,9 @@ </members> <signals> <signal name="input_event"> - <argument index="0" name="camera" type="Object"> + <argument index="0" name="camera" type="Node"> </argument> - <argument index="1" name="event" type="Object"> + <argument index="1" name="event" type="InputEvent"> </argument> <argument index="2" name="click_position" type="Vector3"> </argument> diff --git a/doc/classes/CollisionObject2D.xml b/doc/classes/CollisionObject2D.xml index 1ef72c0ca2..b507204f0d 100644 --- a/doc/classes/CollisionObject2D.xml +++ b/doc/classes/CollisionObject2D.xml @@ -204,9 +204,9 @@ </members> <signals> <signal name="input_event"> - <argument index="0" name="viewport" type="Object"> + <argument index="0" name="viewport" type="Node"> </argument> - <argument index="1" name="event" type="Object"> + <argument index="1" name="event" type="InputEvent"> </argument> <argument index="2" name="shape_idx" type="int"> </argument> diff --git a/doc/classes/CollisionShape.xml b/doc/classes/CollisionShape.xml index 682c9340df..740a6b7ab1 100644 --- a/doc/classes/CollisionShape.xml +++ b/doc/classes/CollisionShape.xml @@ -4,10 +4,10 @@ Node that represents collision shape data in 3D space. </brief_description> <description> - Editor facility for creating and editing collision shapes in 3D space. You can use this node to represent all sorts of collision shapes, for example, add this to an [Area] to give it a detection shape, or add it to a [PhysicsBody] to create a solid object. [b]IMPORTANT[/b]: this is an Editor-only helper to create shapes, use [method get_shape] to get the actual shape. + Editor facility for creating and editing collision shapes in 3D space. You can use this node to represent all sorts of collision shapes, for example, add this to an [Area] to give it a detection shape, or add it to a [PhysicsBody] to create a solid object. [b]IMPORTANT[/b]: this is an Editor-only helper to create shapes, use [method CollisionObject.shape_owner_get_shape] to get the actual shape. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/CollisionShape2D.xml b/doc/classes/CollisionShape2D.xml index 3312fad99c..d3c616ee3d 100644 --- a/doc/classes/CollisionShape2D.xml +++ b/doc/classes/CollisionShape2D.xml @@ -4,10 +4,10 @@ Node that represents collision shape data in 2D space. </brief_description> <description> - Editor facility for creating and editing collision shapes in 2D space. You can use this node to represent all sorts of collision shapes, for example, add this to an [Area2D] to give it a detection shape, or add it to a [PhysicsBody2D] to create a solid object. [b]IMPORTANT[/b]: this is an Editor-only helper to create shapes, use [method get_shape] to get the actual shape. + Editor facility for creating and editing collision shapes in 2D space. You can use this node to represent all sorts of collision shapes, for example, add this to an [Area2D] to give it a detection shape, or add it to a [PhysicsBody2D] to create a solid object. [b]IMPORTANT[/b]: this is an Editor-only helper to create shapes, use [method CollisionObject2D.shape_owner_get_shape] to get the actual shape. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/Color.xml b/doc/classes/Color.xml index b66239181a..82a10fbaa4 100644 --- a/doc/classes/Color.xml +++ b/doc/classes/Color.xml @@ -4,8 +4,8 @@ Color in RGBA format with some support for ARGB format. </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 Color.ColorN (e.g. Color.green) or [method @GDScript.ColorN]. + A color is represented by red, green, and blue [code](r, g, b)[/code] components. Additionally, [code]a[/code] represents the alpha component, often used for transparency. Values are in floating point and usually range from 0 to 1. Some properties (such as [member CanvasItem.modulate]) may accept values > 1. + You can also create a color from standardized color names by using [method @GDScript.ColorN]. </description> <tutorials> </tutorials> @@ -25,7 +25,7 @@ [code]"#ff00ff"[/code] - RGB format with '#' [code]"ff00ff"[/code] - RGB format [codeblock] - # The following code creates the same color of an RGBA(178, 217, 10, 255) + # Each of the following creates the same color 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 '#' @@ -41,7 +41,7 @@ <description> Constructs a color from a 32-bit integer (each byte represents a component of the RGBA profile). [codeblock] - var c = Color(274) # a color of an RGBA(0, 0, 1, 18) + var c = Color(274) # Equivalent to RGBA(0, 0, 1, 18) [/codeblock] </description> </method> @@ -55,9 +55,9 @@ <argument index="2" name="b" type="float"> </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 an RGB profile using values between 0 and 1. Alpha will always be 1. [codeblock] - var c = Color(0.2, 1.0, .7) # a color of an RGBA(51, 255, 178, 255) + var c = Color(0.2, 1.0, .7) # Equivalent to RGBA(51, 255, 178, 255) [/codeblock] </description> </method> @@ -73,9 +73,9 @@ <argument index="3" name="a" type="float"> </argument> <description> - Constructs a color from an RGBA profile using values between 0 and 1 (float). + Constructs a color from an RGBA profile using values between 0 and 1. [codeblock] - var c = Color(0.2, 1.0, .7, .8) # a color of an RGBA(51, 255, 178, 204) + var c = Color(0.2, 1.0, .7, .8) # Equivalent to RGBA(51, 255, 178, 204) [/codeblock] </description> </method> @@ -85,7 +85,7 @@ <argument index="0" name="over" type="Color"> </argument> <description> - Returns a new color resulting from blending this color over another color. If the color is opaque, the result would also be opaque. The other color could then take a range of values with different alpha values. + Returns a new color resulting from blending this color over another. If the color is opaque, the result is also opaque. The second color may have a range of alpha values. [codeblock] var bg = Color(0.0, 1.0, 0.0, 0.5) # Green with alpha of 50% var fg = Color(1.0, 0.0, 0.0, .5) # Red with alpha of 50% @@ -100,7 +100,7 @@ Returns the most contrasting color. [codeblock] var c = Color(.3, .4, .9) - var contrastedColor = c.contrasted() # a color of an RGBA(204, 229, 102, 255) + var contrastedColor = c.contrasted() # Equivalent to RGBA(204, 229, 102, 255) [/codeblock] </description> </method> @@ -131,7 +131,7 @@ <description> Constructs a color from an HSV profile. [code]h[/code], [code]s[/code], and [code]v[/code] are values between 0 and 1. [codeblock] - var c = Color.from_hsv(0.58, 0.5, 0.79, 0.8) # equivalent to HSV(210, 50, 79, 0.8) or Color8(100, 151, 201, 0.8) + var c = Color.from_hsv(0.58, 0.5, 0.79, 0.8) # Equivalent to HSV(210, 50, 79, 0.8) or Color8(100, 151, 201, 0.8) [/codeblock] </description> </method> @@ -139,8 +139,8 @@ <return type="float"> </return> <description> - Returns the color's grayscale. - The gray is calculated by (r + g + b) / 3. + Returns the color's grayscale representation. + The gray is calculated by [code](r + g + b) / 3[/code]. [codeblock] var c = Color(0.2, 0.45, 0.82) var gray = c.gray() # a value of 0.466667 @@ -151,7 +151,7 @@ <return type="Color"> </return> <description> - Returns the inverted color (1-r, 1-g, 1-b, 1-a). + Returns the inverted color [code](1 - r, 1 - g, 1 - b, 1 - a)[/code]. [codeblock] var c = Color(.3, .4, .9) var invertedColor = c.inverted() # a color of an RGBA(178, 153, 26, 255) @@ -179,7 +179,7 @@ <argument index="1" name="t" type="float"> </argument> <description> - Returns the color of the linear interpolation with another color. The value t is between 0 and 1 (float). + Returns the linear interpolation with another color. The value t is between 0 and 1. [codeblock] var c1 = Color(1.0, 0.0, 0.0) var c2 = Color(0.0, 1.0, 0.0) @@ -238,7 +238,7 @@ </argument> <description> Returns the color's HTML hexadecimal color string in ARGB format (ex: [code]ff34f822[/code]). - Optionally flag 'false' to not include alpha in hexadecimal string. + Setting [code]with_alpha[/code] to [code]false[/code] excludes alpha from the hexadecimal string. [codeblock] var c = Color(1, 1, 1, .5) var s1 = c.to_html() # Results "7fffffff" @@ -250,7 +250,7 @@ <return type="int"> </return> <description> - 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. + Returns the color's 32-bit integer in RGBA format (each byte represents a component of the RGBA profile). RGBA is Godot's default format. [codeblock] var c = Color(1, .5, .2) print(c.to_rgba32()) # Prints 4286526463 @@ -261,7 +261,7 @@ <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. + Returns the color's 64-bit integer in RGBA format (each word represents a component of the RGBA profile). RGBA is Godot's default format. [codeblock] var c = Color(1, .5, .2) print(c.to_rgba64()) # Prints -140736629309441 @@ -271,37 +271,37 @@ </methods> <members> <member name="a" type="float" setter="" getter=""> - Alpha (0 to 1) + Alpha value (range 0 to 1). </member> <member name="a8" type="int" setter="" getter=""> - Alpha (0 to 255) + Alpha value (range 0 to 255). </member> <member name="b" type="float" setter="" getter=""> - Blue (0 to 1) + Blue value (range 0 to 1). </member> <member name="b8" type="int" setter="" getter=""> - Blue (0 to 255) + Blue value (range 0 to 255). </member> <member name="g" type="float" setter="" getter=""> - Green (0 to 1) + Green value (range 0 to 1). </member> <member name="g8" type="int" setter="" getter=""> - Green (0 to 255) + Green value (range 0 to 255). </member> <member name="h" type="float" setter="" getter=""> - Hue (0 to 1) + HSV hue value (range 0 to 1). </member> <member name="r" type="float" setter="" getter=""> - Red (0 to 1) + Red value (range 0 to 1). </member> <member name="r8" type="int" setter="" getter=""> - Red (0 to 255) + Red value (range 0 to 255). </member> <member name="s" type="float" setter="" getter=""> - Saturation (0 to 1) + HSV saturation value (range 0 to 1). </member> <member name="v" type="float" setter="" getter=""> - Value (0 to 1) + HSV value (range 0 to 1). </member> </members> <constants> diff --git a/doc/classes/ColorPicker.xml b/doc/classes/ColorPicker.xml index 4d52eacba8..554e6b5632 100644 --- a/doc/classes/ColorPicker.xml +++ b/doc/classes/ColorPicker.xml @@ -26,7 +26,7 @@ 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 the user releases the mouse button, otherwise it will apply immediatly even in mouse motion event (which can cause performance issues). + If [code]true[/code] the color will apply only after the user releases the mouse button, otherwise it will apply immediately 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). diff --git a/doc/classes/ColorPickerButton.xml b/doc/classes/ColorPickerButton.xml index 6ac2911c11..8bc6d05bd4 100644 --- a/doc/classes/ColorPickerButton.xml +++ b/doc/classes/ColorPickerButton.xml @@ -4,7 +4,7 @@ Button that pops out a [ColorPicker]. </brief_description> <description> - Encapsulates a [ColorPicker] making it accesible by pressing a button. Pressing the button will toggle the [ColorPicker] visibility. + Encapsulates a [ColorPicker] making it accessible by pressing a button. Pressing the button will toggle the [ColorPicker] visibility. </description> <tutorials> </tutorials> diff --git a/doc/classes/ConfigFile.xml b/doc/classes/ConfigFile.xml index ec0381bda5..a4709c1c86 100644 --- a/doc/classes/ConfigFile.xml +++ b/doc/classes/ConfigFile.xml @@ -18,7 +18,7 @@ var err = config.load("user://settings.cfg") if err == OK: # if not, something went wrong with the file loading # Look for the display/width pair, and default to 1024 if missing - var screen_width = get_value("display", "width", 1024) + var screen_width = config.get_value("display", "width", 1024) # Store a variable if and only if it hasn't been defined yet if not config.has_section_key("audio", "mute"): config.set_value("audio", "mute", false) diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml index 4301102e4a..76a475e49d 100644 --- a/doc/classes/Control.xml +++ b/doc/classes/Control.xml @@ -13,8 +13,8 @@ [Theme] resources change the Control's appearance. If you change the [Theme] on a [code]Control[/code] node, it affects all of its children. To override some of the theme's parameters, call one of the [code]add_*_override[/code] methods, like [method add_font_override]. You can override the theme with the inspector. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/gui/index.html</link> - <link>http://docs.godotengine.org/en/3.0/tutorials/2d/custom_drawing_in_2d.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/gui/index.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/2d/custom_drawing_in_2d.html</link> </tutorials> <demos> </demos> @@ -38,7 +38,7 @@ <argument index="0" name="event" type="InputEvent"> </argument> <description> - The node's parent forwards input events to this method. Use it to process and accept inputs on UI elements. See [method accept_event]. + Use this method to process and accept inputs on UI elements. See [method accept_event]. Replaces Godot 2's [code]_input_event[/code]. </description> </method> @@ -76,7 +76,7 @@ <argument index="1" name="constant" type="int"> </argument> <description> - Overrides an integer constant in the [member theme] resource the node uses. If the [code]constant[/code] is invalid, Godot clears the override. See [member Theme.INVALID_CONSTANT] for more information. + Overrides an integer constant in the [member theme] resource the node uses. If the [code]constant[/code] is invalid, Godot clears the override. </description> </method> <method name="add_font_override"> @@ -229,9 +229,9 @@ extends Control func get_drag_data(position): - var mydata = make_data() - set_drag_preview(make_preview(mydata)) - return mydata + var mydata = make_data() + set_drag_preview(make_preview(mydata)) + return mydata [/codeblock] </description> </method> @@ -723,7 +723,7 @@ </description> </signal> <signal name="gui_input"> - <argument index="0" name="ev" type="Object"> + <argument index="0" name="event" type="InputEvent"> </argument> <description> Emitted when the node receives an [InputEvent]. diff --git a/doc/classes/Curve.xml b/doc/classes/Curve.xml index c7f2f7bb8d..490772e400 100644 --- a/doc/classes/Curve.xml +++ b/doc/classes/Curve.xml @@ -49,6 +49,13 @@ Removes all points from the curve. </description> </method> + <method name="get_point_count" qualifiers="const"> + <return type="int"> + </return> + <description> + Returns the number of points describing the curve. + </description> + </method> <method name="get_point_left_mode" qualifiers="const"> <return type="int" enum="Curve.TangentMode"> </return> diff --git a/doc/classes/Curve2D.xml b/doc/classes/Curve2D.xml index 26de8be42c..ab9b27542c 100644 --- a/doc/classes/Curve2D.xml +++ b/doc/classes/Curve2D.xml @@ -111,7 +111,7 @@ <argument index="1" name="t" type="float"> </argument> <description> - Returns the position between the vertex "idx" and the vertex "idx"+1, where "t" controls if the point is the first vertex (t = 0.0), the last vertex (t = 1.0), or in between. Values of "t" outside the range (0.0 >= t <=1) give strange, but predictable results. + Returns the position between the vertex "idx" and the vertex "idx"+1, where "t" controls if the point is the first vertex (t = 0.0), the last vertex (t = 1.0), or in between. Values of "t" outside the range (0.0 >= t <=1) give strange, but predictable results. If "idx" is out of bounds it is truncated to the first or last vertex, and "t" is ignored. If the curve has no points, the function sends an error to the console, and returns (0, 0). </description> </method> diff --git a/doc/classes/Curve3D.xml b/doc/classes/Curve3D.xml index 1355c74faf..c3ee309f0b 100644 --- a/doc/classes/Curve3D.xml +++ b/doc/classes/Curve3D.xml @@ -135,7 +135,7 @@ <argument index="1" name="t" type="float"> </argument> <description> - Returns the position between the vertex "idx" and the vertex "idx"+1, where "t" controls if the point is the first vertex (t = 0.0), the last vertex (t = 1.0), or in between. Values of "t" outside the range (0.0 >= t <=1) give strange, but predictable results. + Returns the position between the vertex "idx" and the vertex "idx"+1, where "t" controls if the point is the first vertex (t = 0.0), the last vertex (t = 1.0), or in between. Values of "t" outside the range (0.0 >= t <=1) give strange, but predictable results. If "idx" is out of bounds it is truncated to the first or last vertex, and "t" is ignored. If the curve has no points, the function sends an error to the console, and returns (0, 0, 0). </description> </method> diff --git a/doc/classes/Dictionary.xml b/doc/classes/Dictionary.xml index 800a76ccf1..06c996e13e 100644 --- a/doc/classes/Dictionary.xml +++ b/doc/classes/Dictionary.xml @@ -35,7 +35,7 @@ <method name="erase"> <return type="bool"> </return> - <argument index="0" name="key" type="var"> + <argument index="0" name="key" type="Variant"> </argument> <description> Erase a dictionary key/value pair by key. @@ -44,7 +44,7 @@ <method name="has"> <return type="bool"> </return> - <argument index="0" name="key" type="var"> + <argument index="0" name="key" type="Variant"> </argument> <description> Return true if the dictionary has a given key. diff --git a/doc/classes/DirectionalLight.xml b/doc/classes/DirectionalLight.xml index 2dc522083d..86c8f2f03a 100644 --- a/doc/classes/DirectionalLight.xml +++ b/doc/classes/DirectionalLight.xml @@ -7,7 +7,7 @@ A directional light is a type of [Light] node that models an infinite number of parallel rays covering the entire scene. It is used for lights with strong intensity that are located far away from the scene to model sunlight or moonlight. The worldspace location of the DirectionalLight transform (origin) is ignored. Only the basis is used do determine light direction. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/3d/lights_and_shadows.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/3d/lights_and_shadows.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/Directory.xml b/doc/classes/Directory.xml index d8ad208fa7..57301f954f 100644 --- a/doc/classes/Directory.xml +++ b/doc/classes/Directory.xml @@ -23,7 +23,7 @@ [/codeblock] </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/getting_started/step_by_step/filesystem.html</link> + <link>https://docs.godotengine.org/en/latest/getting_started/step_by_step/filesystem.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/EditorFileSystem.xml b/doc/classes/EditorFileSystem.xml index ade6d2034d..5a8b506f9e 100644 --- a/doc/classes/EditorFileSystem.xml +++ b/doc/classes/EditorFileSystem.xml @@ -93,10 +93,6 @@ Remitted if a resource is reimported. </description> </signal> - <signal name="script_classes_updated"> - <description> - </description> - </signal> <signal name="sources_changed"> <argument index="0" name="exist" type="bool"> </argument> diff --git a/doc/classes/EditorFileSystemDirectory.xml b/doc/classes/EditorFileSystemDirectory.xml index bb3ff91639..9dd28d2400 100644 --- a/doc/classes/EditorFileSystemDirectory.xml +++ b/doc/classes/EditorFileSystemDirectory.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="EditorFileSystemDirectory" inherits="Object" category="Core" version="3.1"> <brief_description> - A diretory for the resource filesystem. + A directory for the resource filesystem. </brief_description> <description> A more generalized, low-level variation of the directory concept. diff --git a/doc/classes/EditorImportPlugin.xml b/doc/classes/EditorImportPlugin.xml index b21d402468..24201c39b9 100644 --- a/doc/classes/EditorImportPlugin.xml +++ b/doc/classes/EditorImportPlugin.xml @@ -41,6 +41,7 @@ return FAILED var mesh = Mesh.new() + # Fill the Mesh with data read in 'file', left as exercise to the reader var filename = save_path + "." + get_save_extension() ResourceSaver.save(filename, mesh) @@ -48,7 +49,7 @@ [/codeblock] </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/plugins/editor/import_plugins.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/plugins/editor/import_plugins.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/EditorInterface.xml b/doc/classes/EditorInterface.xml index 19bd7e6d52..f073c5e40b 100644 --- a/doc/classes/EditorInterface.xml +++ b/doc/classes/EditorInterface.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="EditorInterface" inherits="Node" category="Core" version="3.1"> <brief_description> - Editor interface and main components. + Godot editor's interface. </brief_description> <description> - Editor interface. Allows saving and (re-)loading scenes, rendering mesh previews, inspecting and editing resources and objects and provides access to [EditorSettings], [EditorFileSystem], [EditorResourcePreview], [ScriptEditor], the editor viewport, as well as information about scenes. Also see [EditorPlugin] and [EditorScript]. + EditorInterface gives you control over Godot editor's window. It allows customizing the window, saving and (re-)loading scenes, rendering mesh previews, inspecting and editing resources and objects, and provides access to [EditorSettings], [EditorFileSystem], [EditorResourcePreview], [ScriptEditor], the editor viewport, and information about scenes. </description> <tutorials> </tutorials> @@ -24,14 +24,14 @@ <return type="Control"> </return> <description> - Returns the base [Control]. + Returns the main container of Godot editor's window. You can use it, for example, to retrieve the size of the container and place your controls accordingly. </description> </method> <method name="get_edited_scene_root"> <return type="Node"> </return> <description> - Returns the edited scene's root [Node]. + Returns the edited (current) scene's root [Node]. </description> </method> <method name="get_editor_settings"> @@ -52,7 +52,7 @@ <return type="Array"> </return> <description> - Returns an [Array] of the currently opened scenes. + Returns an [Array] with the file paths of the currently opened scenes. </description> </method> <method name="get_resource_filesystem"> @@ -66,7 +66,7 @@ <return type="EditorResourcePreview"> </return> <description> - Returns the [EditorResourcePreview]\ er. + Returns the [EditorResourcePreview]. </description> </method> <method name="get_script_editor"> diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml index 62fc56e990..feaa24b0ab 100644 --- a/doc/classes/EditorPlugin.xml +++ b/doc/classes/EditorPlugin.xml @@ -4,10 +4,10 @@ Used by the editor to extend its functionality. </brief_description> <description> - Plugins are used by the editor to extend functionality. The most common types of plugins are those which edit a given node or resource type, import plugins and export plugins. + Plugins are used by the editor to extend functionality. The most common types of plugins are those which edit a given node or resource type, import plugins and export plugins. Also see [EditorScript] to add functions to the editor. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/development/plugins/index.html</link> + <link>https://docs.godotengine.org/en/latest/development/plugins/index.html</link> </tutorials> <demos> </demos> @@ -31,7 +31,7 @@ <argument index="1" name="title" type="String"> </argument> <description> - Add a control to the bottom panel (together with Output, Debug, Animation, etc). Returns a reference to the button added. It's up to you to hide/show the button when needed. If your plugin is being removed, also make sure to remove your control by calling [method remove_control_from_bottom_panel]. + Add a control to the bottom panel (together with Output, Debug, Animation, etc). Returns a reference to the button added. It's up to you to hide/show the button when needed. When your plugin is deactivated, make sure to remove your custom control with [method remove_control_from_bottom_panel] and free it with [code]queue_free()[/code]. </description> </method> <method name="add_control_to_container"> @@ -44,7 +44,7 @@ <description> Add a custom control to a container (see CONTAINER_* enum). There are many locations where custom controls can be added in the editor UI. Please remember that you have to manage the visibility of your custom controls yourself (and likely hide it after adding it). - If your plugin is being removed, also make sure to remove your custom controls too. + When your plugin is deactivated, make sure to remove your custom control with [method remove_control_from_container] and free it with [code]queue_free()[/code]. </description> </method> <method name="add_control_to_dock"> @@ -57,7 +57,7 @@ <description> Add the control to a specific dock slot (see DOCK_* enum for options). If the dock is repositioned and as long as the plugin is active, the editor will save the dock position on further sessions. - If your plugin is being removed, also make sure to remove your control by calling [method remove_control_from_docks]. + When your plugin is deactivated, make sure to remove your custom control with [method remove_control_from_docks] and free it with [code]queue_free()[/code]. </description> </method> <method name="add_custom_type"> @@ -74,7 +74,7 @@ <description> Add a custom type, which will appear in the list of nodes or resources. An icon can be optionally passed. When given node or resource is selected, the base type will be instanced (ie, "Spatial", "Control", "Resource"), then the script will be loaded and set to this object. - You can use the [method EditorPlugin.handles] to check if your custom object is being edited by checking the script or using 'is' keyword. + You can use the virtual method [method handles] to check if your custom object is being edited by checking the script or using 'is' keyword. During run-time, this will be a simple object with a script so this function does not need to be called then. </description> </method> @@ -122,7 +122,7 @@ <argument index="3" name="ud" type="Variant" default="null"> </argument> <description> - Adds a custom menu to 'Project > Tools' as [code]name[/code] that calls [code]callback[/code] on an instance of [code]handler[/code] with a parameter [code]ud[/code] when user activates it. + Add a custom menu to 'Project > Tools' as [code]name[/code] that calls [code]callback[/code] on an instance of [code]handler[/code] with a parameter [code]ud[/code] when user activates it. </description> </method> <method name="add_tool_submenu_item"> @@ -133,6 +133,7 @@ <argument index="1" name="submenu" type="Object"> </argument> <description> + Like [method add_tool_menu_item] but adds the [code]submenu[/code] item inside the [code]name[/code] menu. </description> </method> <method name="apply_changes" qualifiers="virtual"> @@ -165,15 +166,21 @@ This function is used for plugins that edit specific object types (nodes or resources). It requests the editor to edit the given object. </description> </method> - <method name="forward_canvas_gui_input" qualifiers="virtual"> - <return type="bool"> + <method name="forward_canvas_draw_over_viewport" qualifiers="virtual"> + <return type="void"> </return> - <argument index="0" name="event" type="InputEvent"> + <argument index="0" name="overlay" type="Control"> </argument> <description> + This method is called when there is an input event in the 2D viewport, e.g. the user clicks with the mouse in the 2D space (canvas GUI). Keep in mind that for this method to be called you have to first declare the virtual method [method handles] so the editor knows that you want to work with the workspace: + [codeblock] + func handles(object): + return true + [/codeblock] + Also note that the edited scene must have a root node. </description> </method> - <method name="forward_draw_over_viewport" qualifiers="virtual"> + <method name="forward_canvas_force_draw_over_viewport" qualifiers="virtual"> <return type="void"> </return> <argument index="0" name="overlay" type="Control"> @@ -181,10 +188,10 @@ <description> </description> </method> - <method name="forward_force_draw_over_viewport" qualifiers="virtual"> - <return type="void"> + <method name="forward_canvas_gui_input" qualifiers="virtual"> + <return type="bool"> </return> - <argument index="0" name="overlay" type="Control"> + <argument index="0" name="event" type="InputEvent"> </argument> <description> </description> @@ -197,8 +204,12 @@ <argument index="1" name="event" type="InputEvent"> </argument> <description> - Implement this function if you are interested in 3D view screen input events. It will be called only if currently selected node is handled by your plugin. - If you would like to always gets those input events then additionally use [method set_input_forwarding_always_enabled]. + This method is called when there is an input event in the 3D viewport, e.g. the user clicks with the mouse in the 3D space (spatial GUI). Keep in mind that for this method to be called you have to first declare the virtual method [method handles] so the editor knows that you want to work with the workspace: + [codeblock] + func handles(object): + return true + [/codeblock] + Also note that the edited scene must have a root node. </description> </method> <method name="get_breakpoints" qualifiers="virtual"> @@ -212,6 +223,7 @@ <return type="EditorInterface"> </return> <description> + Return the [EditorInterface] object that gives you control over Godot editor's window and its functionalities. </description> </method> <method name="get_plugin_icon" qualifiers="virtual"> @@ -253,7 +265,7 @@ <argument index="0" name="layout" type="ConfigFile"> </argument> <description> - Get the GUI layout of the plugin. This is used to save the project's editor layout when the [method EditorPlugin.queue_save_layout] is called or the editor layout was changed(For example changing the position of a dock). + Get the GUI layout of the plugin. This is used to save the project's editor layout when [method queue_save_layout] is called or the editor layout was changed(For example changing the position of a dock). </description> </method> <method name="handles" qualifiers="virtual"> @@ -262,14 +274,14 @@ <argument index="0" name="object" type="Object"> </argument> <description> - Implement this function if your plugin edits a specific type of object (Resource or Node). If you return true, then you will get the functions [method EditorPlugin.edit] and [method EditorPlugin.make_visible] called when the editor requests them. + Implement this function if your plugin edits a specific type of object (Resource or Node). If you return true, then you will get the functions [method EditorPlugin.edit] and [method EditorPlugin.make_visible] called when the editor requests them. If you have declared the methods [method forward_canvas_gui_input] and [method forward_spatial_gui_input] these will be called too. </description> </method> <method name="has_main_screen" qualifiers="virtual"> <return type="bool"> </return> <description> - Return true if this is a main screen editor plugin (it goes in the main screen selector together with 2D, 3D, Script). + Return true if this is a main screen editor plugin (it goes in the workspaces selector together with '2D', '3D', and 'Script'). </description> </method> <method name="hide_bottom_panel"> @@ -318,7 +330,7 @@ <argument index="0" name="control" type="Control"> </argument> <description> - Remove the control from the bottom panel. Don't forget to call this if you added one, so the editor can remove it cleanly. + Remove the control from the bottom panel. You have to manually [code]queue_free()[/code] the control. </description> </method> <method name="remove_control_from_container"> @@ -329,7 +341,7 @@ <argument index="1" name="control" type="Control"> </argument> <description> - Remove the control from the specified container. Use it when cleaning up after adding a control with [method add_control_to_container]. Note that you can simply free the control if you won't use it anymore. + Remove the control from the specified container. You have to manually [code]queue_free()[/code] the control. </description> </method> <method name="remove_control_from_docks"> @@ -338,7 +350,7 @@ <argument index="0" name="control" type="Control"> </argument> <description> - Remove the control from the dock. Don't forget to call this if you added one, so the editor can save the layout and remove it cleanly. + Remove the control from the dock. You have to manually [code]queue_free()[/code] the control. </description> </method> <method name="remove_custom_type"> @@ -347,7 +359,7 @@ <argument index="0" name="type" type="String"> </argument> <description> - Remove a custom type added by [method EditorPlugin.add_custom_type] + Remove a custom type added by [method add_custom_type] </description> </method> <method name="remove_export_plugin"> @@ -441,27 +453,27 @@ <argument index="0" name="screen_name" type="String"> </argument> <description> - Emitted when user change main screen view (2D, 3D, Script, AssetLib). Works also with screens which are defined by plugins. + Emitted when user changes the workspace (2D, 3D, Script, AssetLib). Also works with custom screens defined by plugins. </description> </signal> <signal name="resource_saved"> - <argument index="0" name="resource" type="Object"> + <argument index="0" name="resource" type="Resource"> </argument> <description> </description> </signal> <signal name="scene_changed"> - <argument index="0" name="scene_root" type="Object"> + <argument index="0" name="scene_root" type="Node"> </argument> <description> - Emitted when user change scene. The argument is a root node of freshly opened scene. + Emitted when the scene is changed in the editor. The argument will return the root node of the scene that has just become active. If this scene is new and empty, the argument will be null. </description> </signal> <signal name="scene_closed"> <argument index="0" name="filepath" type="String"> </argument> <description> - Emitted when user close scene. The argument is file path to a closed scene. + Emitted when user closes a scene. The argument is file path to a closed scene. </description> </signal> </signals> @@ -470,17 +482,21 @@ </constant> <constant name="CONTAINER_SPATIAL_EDITOR_MENU" value="1" enum="CustomControlContainer"> </constant> - <constant name="CONTAINER_SPATIAL_EDITOR_SIDE" value="2" enum="CustomControlContainer"> + <constant name="CONTAINER_SPATIAL_EDITOR_SIDE_LEFT" value="2" enum="CustomControlContainer"> + </constant> + <constant name="CONTAINER_SPATIAL_EDITOR_SIDE_RIGHT" value="3" enum="CustomControlContainer"> + </constant> + <constant name="CONTAINER_SPATIAL_EDITOR_BOTTOM" value="4" enum="CustomControlContainer"> </constant> - <constant name="CONTAINER_SPATIAL_EDITOR_BOTTOM" value="3" enum="CustomControlContainer"> + <constant name="CONTAINER_CANVAS_EDITOR_MENU" value="5" enum="CustomControlContainer"> </constant> - <constant name="CONTAINER_CANVAS_EDITOR_MENU" value="4" enum="CustomControlContainer"> + <constant name="CONTAINER_CANVAS_EDITOR_SIDE_LEFT" value="6" enum="CustomControlContainer"> </constant> - <constant name="CONTAINER_CANVAS_EDITOR_SIDE" value="5" enum="CustomControlContainer"> + <constant name="CONTAINER_CANVAS_EDITOR_SIDE_RIGHT" value="7" enum="CustomControlContainer"> </constant> - <constant name="CONTAINER_CANVAS_EDITOR_BOTTOM" value="6" enum="CustomControlContainer"> + <constant name="CONTAINER_CANVAS_EDITOR_BOTTOM" value="8" enum="CustomControlContainer"> </constant> - <constant name="CONTAINER_PROPERTY_EDITOR_BOTTOM" value="7" enum="CustomControlContainer"> + <constant name="CONTAINER_PROPERTY_EDITOR_BOTTOM" value="9" enum="CustomControlContainer"> </constant> <constant name="DOCK_SLOT_LEFT_UL" value="0" enum="DockSlot"> </constant> diff --git a/doc/classes/EditorProperty.xml b/doc/classes/EditorProperty.xml index 32d3a2703d..7d09554330 100644 --- a/doc/classes/EditorProperty.xml +++ b/doc/classes/EditorProperty.xml @@ -98,7 +98,7 @@ <signal name="resource_selected"> <argument index="0" name="path" type="String"> </argument> - <argument index="1" name="resource" type="Object"> + <argument index="1" name="resource" type="Resource"> </argument> <description> </description> diff --git a/doc/classes/EditorResourcePreviewGenerator.xml b/doc/classes/EditorResourcePreviewGenerator.xml index fb9af47b1f..c4dcbbbc82 100644 --- a/doc/classes/EditorResourcePreviewGenerator.xml +++ b/doc/classes/EditorResourcePreviewGenerator.xml @@ -16,10 +16,12 @@ </return> <argument index="0" name="from" type="Resource"> </argument> + <argument index="1" name="size" type="Vector2"> + </argument> <description> - Generate a preview from a given resource. This must be always implemented. - Returning an empty texture is an OK way to fail and let another generator take care. - Care must be taken because this function is always called from a thread (not the main thread). + Generate a preview from a given resource with the specified size. This must always be implemented. + Returning an empty texture is an OK way to fail and let another generator take care. + Care must be taken because this function is always called from a thread (not the main thread). </description> </method> <method name="generate_from_path" qualifiers="virtual"> @@ -27,10 +29,12 @@ </return> <argument index="0" name="path" type="String"> </argument> + <argument index="1" name="size" type="Vector2"> + </argument> <description> - Generate a preview directly from a path, implementing this is optional, as default code will load and call generate() - Returning an empty texture is an OK way to fail and let another generator take care. - Care must be taken because this function is always called from a thread (not the main thread). + Generate a preview directly from a path with the specified size. Implementing this is optional, as default code will load and call [method generate]. + Returning an empty texture is an OK way to fail and let another generator take care. + Care must be taken because this function is always called from a thread (not the main thread). </description> </method> <method name="handles" qualifiers="virtual"> diff --git a/doc/classes/EditorScenePostImport.xml b/doc/classes/EditorScenePostImport.xml index 09cae25403..0c5de68d98 100644 --- a/doc/classes/EditorScenePostImport.xml +++ b/doc/classes/EditorScenePostImport.xml @@ -4,44 +4,45 @@ Post process scenes after import </brief_description> <description> - 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> - <link>http://docs.godotengine.org/en/latest/learning/workflow/assets/importing_scenes.html?highlight=post%20import</link> - </tutorials> - <demos> + Imported scenes can be automatically modified right after import by setting their [i]Custom Script[/i] Import property to a [code]tool[/code] script that inherits from this class. + The [method post_import] callback receives the imported scene's root node and returns the modified version of the scene. Usage example: [codeblock] -tool # needed so it runs in editor -extends EditorScenePostImport + tool # needed so it runs in editor + extends EditorScenePostImport -# This sample changes all node names + # This sample changes all node names -# get called right after the scene is imported and gets the root-node -func post_import(scene): - # change all node names to "modified_[oldnodename]" - iterate(scene) - return scene # remember to return the imported scene + # Called right after the scene is imported and gets the root node + func post_import(scene): + # change all node names to "modified_[oldnodename]" + iterate(scene) + return scene # remember to return the imported scene -func iterate(node): - if node!=null: - node.name = "modified_"+node.name - for child in node.get_children(): - iterate(child) -[/codeblock] + func iterate(node): + if node != null: + node.name = "modified_"+node.name + for child in node.get_children(): + iterate(child) + [/codeblock] + </description> + <tutorials> + <link>https://docs.godotengine.org/en/latest/getting_started/workflow/assets/importing_scenes.html#custom-script</link> + </tutorials> + <demos> </demos> <methods> <method name="get_source_file" qualifiers="const"> <return type="String"> </return> <description> - Returns the source-file-path which got imported (e.g. [code]res://scene.dae[/code] ) + Returns the source file path which got imported (e.g. [code]res://scene.dae[/code]). </description> </method> <method name="get_source_folder" qualifiers="const"> <return type="String"> </return> <description> - Returns the resource-folder the imported scene-file is located in + Returns the resource folder the imported scene file is located in. </description> </method> <method name="post_import" qualifiers="virtual"> @@ -50,7 +51,7 @@ func iterate(node): <argument index="0" name="scene" type="Object"> </argument> <description> - Gets called after the scene got imported and has to return the modified version of the scene + Gets called after the scene got imported and has to return the modified version of the scene. </description> </method> </methods> diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml index bd85075b7e..9d48669a6b 100644 --- a/doc/classes/EditorSettings.xml +++ b/doc/classes/EditorSettings.xml @@ -48,11 +48,11 @@ Erase a given setting (pass full property path). </description> </method> - <method name="get_favorite_dirs" qualifiers="const"> + <method name="get_favorites" qualifiers="const"> <return type="PoolStringArray"> </return> <description> - Get the list of favorite directories for this project. + Get the list of favorite files and directories for this project. </description> </method> <method name="get_project_metadata" qualifiers="const"> @@ -122,13 +122,13 @@ <description> </description> </method> - <method name="set_favorite_dirs"> + <method name="set_favorites"> <return type="void"> </return> <argument index="0" name="dirs" type="PoolStringArray"> </argument> <description> - Set the list of favorite directories for this project. + Set the list of favorite files and directories for this project. </description> </method> <method name="set_initial_value"> diff --git a/doc/classes/Environment.xml b/doc/classes/Environment.xml index 566322e6c5..4c2c83e55f 100644 --- a/doc/classes/Environment.xml +++ b/doc/classes/Environment.xml @@ -13,8 +13,8 @@ - Adjustments </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/3d/environment_and_post_processing.html</link> - <link>http://docs.godotengine.org/en/3.0/tutorials/3d/high_dynamic_range.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/3d/environment_and_post_processing.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/3d/high_dynamic_range.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/Expression.xml b/doc/classes/Expression.xml index d5b831a61a..c5f71fe55e 100644 --- a/doc/classes/Expression.xml +++ b/doc/classes/Expression.xml @@ -1,8 +1,27 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="Expression" inherits="Reference" category="Core" version="3.1"> <brief_description> + A class that stores an expression you can execute. </brief_description> <description> + An expression can be made of any arithmetic operation, built-in math function call, method call of a passed instance, or built-in type construction call. + An example expression text using the built-in math functions could be [code]sqrt(pow(3,2) + pow(4,2))[/code]. + In the following example we use a [LineEdit] node to write our expression and show the result. + [codeblock] + onready var expression = Expression.new() + + func _ready(): + $LineEdit.connect("text_entered", self, "_on_text_entered") + + func _on_text_entered(command): + var error = expression.parse(command, []) + if error != OK: + print(get_error_text()) + return + var result = expression.execute([], null, true) + if not expression.has_execute_failed(): + $LineEdit.text = str(result) + [/codeblock] </description> <tutorials> </tutorials> @@ -19,18 +38,21 @@ <argument index="2" name="show_error" type="bool" default="true"> </argument> <description> + Executes the expression that was previously parsed by [method parse] and returns the result. Before you use the returned object, you should check if the method failed by calling [method has_execute_failed]. </description> </method> <method name="get_error_text" qualifiers="const"> <return type="String"> </return> <description> + Returns the error text if [method parse] has failed. </description> </method> <method name="has_execute_failed" qualifiers="const"> <return type="bool"> </return> <description> + Returns [code]true[/code] if [method execute] has failed. </description> </method> <method name="parse"> @@ -41,6 +63,7 @@ <argument index="1" name="input_names" type="PoolStringArray" default="PoolStringArray( )"> </argument> <description> + Parses the expression and returns a [enum @GlobalScope.Error]. </description> </method> </methods> diff --git a/doc/classes/File.xml b/doc/classes/File.xml index 20054ac9dc..6c900385f7 100644 --- a/doc/classes/File.xml +++ b/doc/classes/File.xml @@ -22,7 +22,7 @@ [/codeblock] </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/getting_started/step_by_step/filesystem.html</link> + <link>https://docs.godotengine.org/en/latest/getting_started/step_by_step/filesystem.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/FileDialog.xml b/doc/classes/FileDialog.xml index 247228d265..29aa26b67f 100644 --- a/doc/classes/FileDialog.xml +++ b/doc/classes/FileDialog.xml @@ -17,7 +17,7 @@ <argument index="0" name="filter" type="String"> </argument> <description> - Add a custom filter. Filter format is: "mask ; description", example (C++): dialog->add_filter("*.png ; PNG Images"); + Add a custom filter. Example: [code]add_filter("*.png ; PNG Images")[/code] </description> </method> <method name="clear_filters"> @@ -31,12 +31,14 @@ <return type="void"> </return> <description> + Clear currently selected items in the dialog. </description> </method> <method name="get_line_edit"> <return type="LineEdit"> </return> <description> + Returns the LineEdit for the selected file. </description> </method> <method name="get_vbox"> @@ -56,6 +58,7 @@ </methods> <members> <member name="access" type="int" setter="set_access" getter="get_access" enum="FileDialog.Access"> + The file system access scope. See enum [code]Access[/code] constants. </member> <member name="current_dir" type="String" setter="set_current_dir" getter="get_current_dir"> The current working directory of the file dialog. @@ -67,13 +70,16 @@ The currently selected file path of the file dialog. </member> <member name="filters" type="PoolStringArray" setter="set_filters" getter="get_filters"> + Set file type filters. This example shows only .png and .gd files [code]set_filters(PoolStringArray(["*.png ; PNG Images","*.gd ; GD Script"]))[/code]. </member> <member name="mode" type="int" setter="set_mode" getter="get_mode" enum="FileDialog.Mode"> + Set dialog to open or save mode, changes selection behavior. See enum [code]Mode[/code] constants. </member> <member name="mode_overrides_title" type="bool" setter="set_mode_overrides_title" getter="is_mode_overriding_title"> - If [code]true[/code], changing the [code]mode[/code] property will set the window title accordingly (e. g. setting mode to [code]MODE_OPEN_FILE[/code] will change the window title to "Open a File"). + If [code]true[/code], changing the [code]Mode[/code] property will set the window title accordingly (e. g. setting mode to [code]MODE_OPEN_FILE[/code] will change the window title to "Open a File"). </member> <member name="show_hidden_files" type="bool" setter="set_show_hidden_files" getter="is_showing_hidden_files"> + If [code]true[/code], the dialog will show hidden files. </member> </members> <signals> diff --git a/doc/classes/GIProbe.xml b/doc/classes/GIProbe.xml index 77dea73564..5fb0ccc33d 100644 --- a/doc/classes/GIProbe.xml +++ b/doc/classes/GIProbe.xml @@ -5,7 +5,7 @@ <description> </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/3d/gi_probes.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/3d/gi_probes.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/Gradient.xml b/doc/classes/Gradient.xml index df4a507b65..bf3f125e48 100644 --- a/doc/classes/Gradient.xml +++ b/doc/classes/Gradient.xml @@ -4,7 +4,7 @@ Color interpolator node. </brief_description> <description> - Given a set of colors, this node will interpolate them in order, meaning, that if you have color 1, color 2 and color3, the ramp will interpolate (generate the colors between two colors) from color 1 to color 2 and from color 2 to color 3. Initially the ramp will have 2 colors (black and white), one (black) at ramp lower offset offset 0 and the other (white) at the ramp higher offset 1. + Given a set of colors, this node will interpolate them in order, meaning, that if you have color 1, color 2 and color 3, the ramp will interpolate (generate the colors between two colors) from color 1 to color 2 and from color 2 to color 3. Initially the ramp will have 2 colors (black and white), one (black) at ramp lower offset 0 and the other (white) at the ramp higher offset 1. </description> <tutorials> </tutorials> diff --git a/doc/classes/GraphEdit.xml b/doc/classes/GraphEdit.xml index 48e3c295f1..ce0b619d67 100644 --- a/doc/classes/GraphEdit.xml +++ b/doc/classes/GraphEdit.xml @@ -247,7 +247,7 @@ </description> </signal> <signal name="node_selected"> - <argument index="0" name="node" type="Object"> + <argument index="0" name="node" type="Node"> </argument> <description> Emitted when a GraphNode is selected. diff --git a/doc/classes/GridContainer.xml b/doc/classes/GridContainer.xml index 8a8a9a2d24..346ab9d357 100644 --- a/doc/classes/GridContainer.xml +++ b/doc/classes/GridContainer.xml @@ -11,16 +11,6 @@ <demos> </demos> <methods> - <method name="get_child_control_at_cell"> - <return type="Control"> - </return> - <argument index="0" name="row" type="int"> - </argument> - <argument index="1" name="column" type="int"> - </argument> - <description> - </description> - </method> </methods> <members> <member name="columns" type="int" setter="set_columns" getter="get_columns"> diff --git a/doc/classes/HTTPClient.xml b/doc/classes/HTTPClient.xml index 08e2f649a0..2f21505757 100644 --- a/doc/classes/HTTPClient.xml +++ b/doc/classes/HTTPClient.xml @@ -10,8 +10,8 @@ For more information on HTTP, see https://developer.mozilla.org/en-US/docs/Web/HTTP (or read RFC 2616 to get it straight from the source: https://tools.ietf.org/html/rfc2616). </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/networking/http_client_class.html</link> - <link>http://docs.godotengine.org/en/3.0/tutorials/networking/ssl_certificates.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/networking/http_client_class.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/networking/ssl_certificates.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/HTTPRequest.xml b/doc/classes/HTTPRequest.xml index c5bb10a23a..3916eb2b5b 100644 --- a/doc/classes/HTTPRequest.xml +++ b/doc/classes/HTTPRequest.xml @@ -8,7 +8,7 @@ Can be used to make HTTP requests, i.e. download or upload files or web content via HTTP. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/networking/ssl_certificates.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/networking/ssl_certificates.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml index 55693bd49c..56accdcd9e 100644 --- a/doc/classes/Image.xml +++ b/doc/classes/Image.xml @@ -487,10 +487,10 @@ OpenGL texture format RG with two components and a bitdepth of 8 for each. </constant> <constant name="FORMAT_RGB8" value="4" enum="Format"> - OpenGL texture format RGB with three components, each with a bitdepth of 8. + OpenGL texture format RGB with three components, each with a bitdepth of 8. Note that when creating an [ImageTexture], an sRGB to linear color space conversion is performed. </constant> <constant name="FORMAT_RGBA8" value="5" enum="Format"> - OpenGL texture format RGBA with four components, each with a bitdepth of 8. + OpenGL texture format RGBA with four components, each with a bitdepth of 8. Note that when creating an [ImageTexture], an sRGB to linear color space conversion is performed. </constant> <constant name="FORMAT_RGBA4444" value="6" enum="Format"> OpenGL texture format RGBA with four components, each with a bitdepth of 4. @@ -526,13 +526,13 @@ A special OpenGL texture format where the three color components have 9 bits of precision and all three share a single exponent. </constant> <constant name="FORMAT_DXT1" value="17" enum="Format"> - The S3TC texture format that uses Block Compression 1, and is the smallest variation of S3TC, only providing 1 bit of alpha and color data being premultiplied with alpha. More information can be found at https://www.khronos.org/opengl/wiki/S3_Texture_Compression. + The S3TC texture format that uses Block Compression 1, and is the smallest variation of S3TC, only providing 1 bit of alpha and color data being premultiplied with alpha. More information can be found at https://www.khronos.org/opengl/wiki/S3_Texture_Compression. Note that when creating an [ImageTexture], an sRGB to linear color space conversion is performed. </constant> <constant name="FORMAT_DXT3" value="18" enum="Format"> - The S3TC texture format that uses Block Compression 2, and color data is interpreted as not having been premultiplied by alpha. Well suited for images with sharp alpha transitions between translucent and opaque areas. + The S3TC texture format that uses Block Compression 2, and color data is interpreted as not having been premultiplied by alpha. Well suited for images with sharp alpha transitions between translucent and opaque areas. Note that when creating an [ImageTexture], an sRGB to linear color space conversion is performed. </constant> <constant name="FORMAT_DXT5" value="19" enum="Format"> - The S3TC texture format also known as Block Compression 3 or BC3 that contains 64 bits of alpha channel data followed by 64 bits of DXT1-encoded color data. Color data is not premultiplied by alpha, same as DXT3. DXT5 generally produces superior results for transparency gradients than DXT3. + The S3TC texture format also known as Block Compression 3 or BC3 that contains 64 bits of alpha channel data followed by 64 bits of DXT1-encoded color data. Color data is not premultiplied by alpha, same as DXT3. DXT5 generally produces superior results for transparency gradients than DXT3. Note that when creating an [ImageTexture], an sRGB to linear color space conversion is performed. </constant> <constant name="FORMAT_RGTC_R" value="20" enum="Format"> Texture format that uses Red Green Texture Compression, normalizing the red channel data using the same compression algorithm that DXT5 uses for the alpha channel. More information can be found here https://www.khronos.org/opengl/wiki/Red_Green_Texture_Compression. @@ -541,7 +541,7 @@ Texture format that uses Red Green Texture Compression, normalizing the red and green channel data using the same compression algorithm that DXT5 uses for the alpha channel. </constant> <constant name="FORMAT_BPTC_RGBA" value="22" enum="Format"> - Texture format that uses BPTC compression with unsigned normalized RGBA components. More information can be found at https://www.khronos.org/opengl/wiki/BPTC_Texture_Compression. + Texture format that uses BPTC compression with unsigned normalized RGBA components. More information can be found at https://www.khronos.org/opengl/wiki/BPTC_Texture_Compression. Note that when creating an [ImageTexture], an sRGB to linear color space conversion is performed. </constant> <constant name="FORMAT_BPTC_RGBF" value="23" enum="Format"> Texture format that uses BPTC compression with signed floating-point RGB components. @@ -550,7 +550,7 @@ Texture format that uses BPTC compression with unsigned floating-point RGB components. </constant> <constant name="FORMAT_PVRTC2" value="25" enum="Format"> - Texture format used on PowerVR-supported mobile platforms, uses 2 bit color depth with no alpha. More information on PVRTC can be found here https://en.wikipedia.org/wiki/PVRTC. + Texture format used on PowerVR-supported mobile platforms, uses 2 bit color depth with no alpha. More information on PVRTC can be found here https://en.wikipedia.org/wiki/PVRTC. Note that when creating an [ImageTexture], an sRGB to linear color space conversion is performed. </constant> <constant name="FORMAT_PVRTC2A" value="26" enum="Format"> Same as PVRTC2, but with an alpha component. @@ -577,13 +577,13 @@ Ericsson Texture Compression format 2 variant SIGNED_RG11_EAC, which provides two channels of signed data. </constant> <constant name="FORMAT_ETC2_RGB8" value="34" enum="Format"> - Ericsson Texture Compression format 2 variant RGB8, which is a followup of ETC1 and compresses RGB888 data. + Ericsson Texture Compression format 2 variant RGB8, which is a followup of ETC1 and compresses RGB888 data. Note that when creating an [ImageTexture], an sRGB to linear color space conversion is performed. </constant> <constant name="FORMAT_ETC2_RGBA8" value="35" enum="Format"> - Ericsson Texture Compression format 2 variant RGBA8, which compresses RGBA8888 data with full alpha support. + Ericsson Texture Compression format 2 variant RGBA8, which compresses RGBA8888 data with full alpha support. Note that when creating an [ImageTexture], an sRGB to linear color space conversion is performed. </constant> <constant name="FORMAT_ETC2_RGB8A1" value="36" enum="Format"> - Ericsson Texture Compression format 2 variant RGB8_PUNCHTHROUGH_ALPHA1, which compresses RGBA data to make alpha either fully transparent or fully opaque. + Ericsson Texture Compression format 2 variant RGB8_PUNCHTHROUGH_ALPHA1, which compresses RGBA data to make alpha either fully transparent or fully opaque. Note that when creating an [ImageTexture], an sRGB to linear color space conversion is performed. </constant> <constant name="FORMAT_MAX" value="37" enum="Format"> </constant> diff --git a/doc/classes/ImageTexture.xml b/doc/classes/ImageTexture.xml index 0bff3317db..5c57899468 100644 --- a/doc/classes/ImageTexture.xml +++ b/doc/classes/ImageTexture.xml @@ -36,7 +36,7 @@ <argument index="1" name="flags" type="int" default="7"> </argument> <description> - Create a new [code]ImageTexture[/code] from an [Image] with "flags" from [Texture].FLAG_*. + Create a new [code]ImageTexture[/code] from an [Image] with "flags" from [Texture].FLAG_*. An sRGB to linear color space conversion can take place, according to [Image].FORMAT_*. </description> </method> <method name="get_format" qualifiers="const"> diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml index a4346c1485..38804e40b4 100644 --- a/doc/classes/Input.xml +++ b/doc/classes/Input.xml @@ -7,7 +7,7 @@ A Singleton that deals with inputs. This includes key presses, mouse buttons and movement, joypads, and input actions. Actions and their events can be set in the Project Settings / Input Map tab. Or be set with [InputMap]. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/index.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/inputs/index.html</link> </tutorials> <demos> </demos> @@ -46,6 +46,7 @@ </return> <description> If the device has an accelerometer, this will return the acceleration. Otherwise, it returns an empty [Vector3]. + Note this method returns an empty [Vector3] when running from the editor even when your device has an accelerometer. You must export your project to a supported device to read values from the accelerometer. </description> </method> <method name="get_action_strength" qualifiers="const"> @@ -54,6 +55,7 @@ <argument index="0" name="action" type="String"> </argument> <description> + Returns a value between 0 and 1 representing the intensity of the given action. In a joypad, for example, the further away the axis (analog sticks or L2, R2 triggers) is from the dead zone, the closer the value will be to 1. If the action is mapped to a control that has no axis as the keyboard, the value returned will be 0 or 1. </description> </method> <method name="get_connected_joypads"> @@ -94,6 +96,7 @@ <argument index="0" name="axis" type="String"> </argument> <description> + Returns the index of the provided axis name. </description> </method> <method name="get_joy_axis_string"> @@ -102,6 +105,7 @@ <argument index="0" name="axis_index" type="int"> </argument> <description> + Receives a [code]JOY_AXIS_*[/code] Enum and returns its equivalent name as a string. </description> </method> <method name="get_joy_button_index_from_string"> @@ -110,6 +114,7 @@ <argument index="0" name="button" type="String"> </argument> <description> + Returns the index of the provided button name. </description> </method> <method name="get_joy_button_string"> @@ -118,6 +123,7 @@ <argument index="0" name="button_index" type="int"> </argument> <description> + Receives a [code]JOY_BUTTON_*[/code] Enum and returns its equivalent name as a string. </description> </method> <method name="get_joy_guid" qualifiers="const"> @@ -292,7 +298,9 @@ <argument index="2" name="hotspot" type="Vector2" default="Vector2( 0, 0 )"> </argument> <description> - Set a custom mouse cursor image, which is only visible inside the game window. The hotspot can also be specified. Passing [code]null[/code] to the image parameter resets to the system cursor. See enum [code]CURSOR_*[/code] for the list of shapes. + Sets a custom mouse cursor image, which is only visible inside the game window. The hotspot can also be specified. Passing [code]null[/code] to the image parameter resets to the system cursor. See enum [code]CURSOR_*[/code] for the list of shapes. + [code]image[/code]'s size must be lower than 256x256. + [code]hotspot[/code] must be within [code]image[/code]'s size. </description> </method> <method name="set_default_cursor_shape"> @@ -301,6 +309,8 @@ <argument index="0" name="shape" type="int" enum="Input.CursorShape" default="0"> </argument> <description> + Sets the default cursor shape to be used in the viewport instead of [code]CURSOR_ARROW[/code]. + Note that if you want to change the default cursor shape for [Control]'s nodes, use [member Control.mouse_default_cursor_shape] instead. </description> </method> <method name="set_mouse_mode"> @@ -381,7 +391,7 @@ Pointing hand cursor. Usually used to indicate the pointer is over a link or other interactable item. </constant> <constant name="CURSOR_CROSS" value="3" enum="CursorShape"> - Cross cursor. Typically appears over regions in which a drawing operation can be performance or for selections. + Cross cursor. Typically appears over regions in which a drawing operation can be performed or for selections. </constant> <constant name="CURSOR_WAIT" value="4" enum="CursorShape"> Wait cursor. Indicates that the application is busy performing an operation. diff --git a/doc/classes/InputEvent.xml b/doc/classes/InputEvent.xml index 993d62d188..c880823aee 100644 --- a/doc/classes/InputEvent.xml +++ b/doc/classes/InputEvent.xml @@ -7,8 +7,8 @@ Base class of all sort of input event. See [method Node._input]. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html</link> - <link>http://docs.godotengine.org/en/3.0/tutorials/2d/2d_transforms.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/inputs/inputevent.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/2d/2d_transforms.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/InputEventAction.xml b/doc/classes/InputEventAction.xml index 16000231cb..8a10dc6ab9 100644 --- a/doc/classes/InputEventAction.xml +++ b/doc/classes/InputEventAction.xml @@ -7,7 +7,7 @@ Contains a generic action which can be targeted from several type of inputs. Actions can be created from the project settings menu [code]Project > Project Settings > Input Map[/code]. See [method Node._input]. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html#actions</link> + <link>https://docs.godotengine.org/en/latest/tutorials/inputs/inputevent.html#actions</link> </tutorials> <demos> </demos> diff --git a/doc/classes/InputEventJoypadButton.xml b/doc/classes/InputEventJoypadButton.xml index adaeae685e..18e33c2131 100644 --- a/doc/classes/InputEventJoypadButton.xml +++ b/doc/classes/InputEventJoypadButton.xml @@ -7,7 +7,7 @@ Input event type for gamepad buttons. For joysticks see [InputEventJoypadMotion]. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/inputs/inputevent.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/InputEventJoypadMotion.xml b/doc/classes/InputEventJoypadMotion.xml index f86aec4ce0..0c73f53158 100644 --- a/doc/classes/InputEventJoypadMotion.xml +++ b/doc/classes/InputEventJoypadMotion.xml @@ -7,7 +7,7 @@ Stores information about joystick motions. One [code]InputEventJoypadMotion[/code] represents one axis at a time. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/inputs/inputevent.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/InputEventKey.xml b/doc/classes/InputEventKey.xml index a013ee6266..0118fda2df 100644 --- a/doc/classes/InputEventKey.xml +++ b/doc/classes/InputEventKey.xml @@ -7,7 +7,7 @@ Stores key presses on the keyboard. Supports key presses, key releases and [member echo] events. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/inputs/inputevent.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/InputEventMouse.xml b/doc/classes/InputEventMouse.xml index 06de96890a..27e8d17407 100644 --- a/doc/classes/InputEventMouse.xml +++ b/doc/classes/InputEventMouse.xml @@ -7,7 +7,7 @@ Stores general mouse events information. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/inputs/inputevent.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/InputEventMouseButton.xml b/doc/classes/InputEventMouseButton.xml index 50641dceed..1342c7bf7e 100644 --- a/doc/classes/InputEventMouseButton.xml +++ b/doc/classes/InputEventMouseButton.xml @@ -7,7 +7,7 @@ Contains mouse click information. See [method Node._input]. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/mouse_and_input_coordinates.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/inputs/mouse_and_input_coordinates.html</link> </tutorials> <demos> </demos> @@ -18,7 +18,7 @@ Mouse button identifier, one of the BUTTON_* or BUTTON_WHEEL_* constants in [@GlobalScope]. </member> <member name="doubleclick" type="bool" setter="set_doubleclick" getter="is_doubleclick"> - If [code]true[/code] the mouse button's state is a double-click. If [code]false[/code] the mouse button's state is released. + If [code]true[/code] the mouse button's state is a double-click. </member> <member name="factor" type="float" setter="set_factor" getter="get_factor"> Magnitude. Amount (or delta) of the event. Used for scroll events, indicates scroll amount (vertically or horizontally). Only supported on some platforms, sensitivity varies by platform. May be 0 if not supported. diff --git a/doc/classes/InputEventMouseMotion.xml b/doc/classes/InputEventMouseMotion.xml index 05e3e79d26..f7f6358910 100644 --- a/doc/classes/InputEventMouseMotion.xml +++ b/doc/classes/InputEventMouseMotion.xml @@ -7,7 +7,7 @@ Contains mouse motion information. Supports relative, absolute positions and speed. See [method Node._input]. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/mouse_and_input_coordinates.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/inputs/mouse_and_input_coordinates.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/InputEventScreenDrag.xml b/doc/classes/InputEventScreenDrag.xml index f777d90ccb..a320c0a028 100644 --- a/doc/classes/InputEventScreenDrag.xml +++ b/doc/classes/InputEventScreenDrag.xml @@ -8,7 +8,7 @@ Contains screen drag information. See [method Node._input]. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/inputs/inputevent.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/InputEventScreenTouch.xml b/doc/classes/InputEventScreenTouch.xml index 39cd0a9657..55f04cdd94 100644 --- a/doc/classes/InputEventScreenTouch.xml +++ b/doc/classes/InputEventScreenTouch.xml @@ -8,7 +8,7 @@ Stores multi-touch press/release information. Supports touch press, touch release and [member index] for multi-touch count and order. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/inputs/inputevent.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/InputEventWithModifiers.xml b/doc/classes/InputEventWithModifiers.xml index 9c1814fedd..fcf2fd545e 100644 --- a/doc/classes/InputEventWithModifiers.xml +++ b/doc/classes/InputEventWithModifiers.xml @@ -7,7 +7,7 @@ Contains keys events information with modifiers support like [code]SHIFT[/code] or [code]ALT[/code]. See [method Node._input]. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/inputs/inputevent.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/InputMap.xml b/doc/classes/InputMap.xml index 7fd1d7e8ac..8c25f851db 100644 --- a/doc/classes/InputMap.xml +++ b/doc/classes/InputMap.xml @@ -7,7 +7,7 @@ Manages all [InputEventAction] which can be created/modified from the project settings menu [code]Project > Project Settings > Input Map[/code] or in code with [method add_action] and [method action_add_event]. See [method Node._input]. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/inputs/inputevent.html#inputmap</link> + <link>https://docs.godotengine.org/en/latest/tutorials/inputs/inputevent.html#inputmap</link> </tutorials> <demos> </demos> diff --git a/doc/classes/ItemList.xml b/doc/classes/ItemList.xml index 4723cf8ee4..4e4947de6c 100644 --- a/doc/classes/ItemList.xml +++ b/doc/classes/ItemList.xml @@ -33,7 +33,7 @@ <argument index="2" name="selectable" type="bool" default="true"> </argument> <description> - Adds an item to the item list with specified text. Specify an icon of null for a list item with no icon. + Adds an item to the item list with specified text. Specify an icon of null for a list item with no icon. If selectable is true the list item will be selectable. </description> </method> @@ -222,7 +222,7 @@ </argument> <description> Select the item at the specified index. - Note: This method does not trigger the item selection signal. + Note: This method does not trigger the item selection signal. </description> </method> <method name="set_item_custom_bg_color"> @@ -412,6 +412,7 @@ Fired when specified list item has been selected via right mouse clicking. The click position is also provided to allow appropriate popup of context menus at the correct location. + [member allow_rmb_select] must be enabled. </description> </signal> <signal name="item_selected"> @@ -419,6 +420,7 @@ </argument> <description> Fired when specified item has been selected. + [member allow_reselect] must be enabled to reselect an item. </description> </signal> <signal name="multi_selected"> diff --git a/doc/classes/JavaScript.xml b/doc/classes/JavaScript.xml index 17588717c2..fa3aaed684 100644 --- a/doc/classes/JavaScript.xml +++ b/doc/classes/JavaScript.xml @@ -7,7 +7,7 @@ The JavaScript singleton is implemented only in HTML5 export. It's used to access the browser's JavaScript context. This allows interaction with embedding pages or calling third-party JavaScript APIs. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/getting_started/workflow/export/exporting_for_web.html#calling-javascript-from-script</link> + <link>https://docs.godotengine.org/en/latest/getting_started/workflow/export/exporting_for_web.html#calling-javascript-from-script</link> </tutorials> <demos> </demos> diff --git a/doc/classes/KinematicBody.xml b/doc/classes/KinematicBody.xml index 0c66319ae7..0656341ffa 100644 --- a/doc/classes/KinematicBody.xml +++ b/doc/classes/KinematicBody.xml @@ -9,7 +9,7 @@ Kinematic Characters: KinematicBody also has an API for moving objects (the [method move_and_collide] and [method move_and_slide] methods) while performing collision tests. This makes them really useful to implement characters that collide against a world, but that don't require advanced physics. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/physics/kinematic_character_2d.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/physics/kinematic_character_2d.html</link> </tutorials> <demos> </demos> @@ -69,7 +69,7 @@ </argument> <description> Moves the body along the vector [code]rel_vec[/code]. The body will stop if it collides. Returns a [KinematicCollision], which contains information about the collision. - If [code]test_only[/code] is [code]true[/true], the body does not move but the would-be collision information is given. + If [code]test_only[/code] is [code]true[/code], the body does not move but the would-be collision information is given. </description> </method> <method name="move_and_slide"> @@ -115,6 +115,8 @@ <argument index="6" name="floor_max_angle" type="float" default="0.785398"> </argument> <description> + Moves the body while keeping it attached to slopes. Similar to [method move_and_slide]. + As long as the [code]snap[/code] vector is in contact with the ground, the body will remain attached to the surface. This means you must disable snap in order to jump, for example. You can do this by setting[code]snap[/code] to[code](0, 0, 0)[/code] or by using [method move_and_slide] instead. </description> </method> <method name="test_move"> diff --git a/doc/classes/KinematicBody2D.xml b/doc/classes/KinematicBody2D.xml index e48660a889..6511b2f182 100644 --- a/doc/classes/KinematicBody2D.xml +++ b/doc/classes/KinematicBody2D.xml @@ -116,7 +116,7 @@ </argument> <description> Moves the body while keeping it attached to slopes. Similar to [method move_and_slide]. - As long as the [code]snap[/code] vector is in contact with the ground, the body will remain attached to the surface. This means you must disable snap in order to jump, for example. You can do this by setting[code]snap[/code] to[code](0, 0)[/code] or by using [method move_and_slide] instead. + As long as the [code]snap[/code] vector is in contact with the ground, the body will remain attached to the surface. This means you must disable snap in order to jump, for example. You can do this by setting [code]snap[/code] to [code](0, 0)[/code] or by using [method move_and_slide] instead. </description> </method> <method name="test_move"> diff --git a/doc/classes/LargeTexture.xml b/doc/classes/LargeTexture.xml index 763b38f49e..9526604e6d 100644 --- a/doc/classes/LargeTexture.xml +++ b/doc/classes/LargeTexture.xml @@ -74,7 +74,7 @@ <argument index="1" name="texture" type="Texture"> </argument> <description> - Sets the [Texture] of the piece with index "idx" to "ofs". + Sets the [Texture] of the piece with index "idx" to "texture". </description> </method> <method name="set_size"> diff --git a/doc/classes/Light.xml b/doc/classes/Light.xml index 04191136a8..cf873e5b28 100644 --- a/doc/classes/Light.xml +++ b/doc/classes/Light.xml @@ -7,7 +7,7 @@ Light is the abstract base class for light nodes, so it shouldn't be used directly (It can't be instanced). Other types of light nodes inherit from it. Light contains the common variables and parameters used for lighting. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/3d/lights_and_shadows.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/3d/lights_and_shadows.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml index c244b8b7a7..f842dd8311 100644 --- a/doc/classes/LineEdit.xml +++ b/doc/classes/LineEdit.xml @@ -89,6 +89,7 @@ The cursor's position inside the [code]LineEdit[/code]. When set, the text may scroll to accommodate it. </member> <member name="clear_button_enabled" type="bool" setter="set_clear_button_enabled" getter="is_clear_button_enabled"> + If [code]true[/code] the [code]LineEdit[/code] will show a clear button if [code]text[/code] is not empty. </member> <member name="context_menu_enabled" type="bool" setter="set_context_menu_enabled" getter="is_context_menu_enabled"> If [code]true[/code] the context menu will appear when right clicked. @@ -169,6 +170,7 @@ Undoes the previous action. </constant> <constant name="MENU_REDO" value="6" enum="MenuItems"> + Reverse the last undo action. </constant> <constant name="MENU_MAX" value="7" enum="MenuItems"> </constant> diff --git a/doc/classes/MainLoop.xml b/doc/classes/MainLoop.xml index 9454a16988..ad763e6532 100644 --- a/doc/classes/MainLoop.xml +++ b/doc/classes/MainLoop.xml @@ -47,7 +47,7 @@ <method name="_input_event" qualifiers="virtual"> <return type="void"> </return> - <argument index="0" name="ev" type="InputEvent"> + <argument index="0" name="event" type="InputEvent"> </argument> <description> </description> @@ -91,7 +91,7 @@ <method name="input_event"> <return type="void"> </return> - <argument index="0" name="ev" type="InputEvent"> + <argument index="0" name="event" type="InputEvent"> </argument> <description> </description> diff --git a/doc/classes/Mesh.xml b/doc/classes/Mesh.xml index afb5d7db93..4852d4701d 100644 --- a/doc/classes/Mesh.xml +++ b/doc/classes/Mesh.xml @@ -48,6 +48,40 @@ Returns all the vertices that make up the faces of the mesh. Each three vertices represent one triangle. </description> </method> + <method name="get_surface_count" qualifiers="const"> + <return type="int"> + </return> + <description> + Return the amount of surfaces that the [code]Mesh[/code] holds. + </description> + </method> + <method name="surface_get_arrays" qualifiers="const"> + <return type="Array"> + </return> + <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 ArrayMesh.add_surface_from_arrays]). + </description> + </method> + <method name="surface_get_blend_shape_arrays" qualifiers="const"> + <return type="Array"> + </return> + <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_material" qualifiers="const"> + <return type="Material"> + </return> + <argument index="0" name="surf_idx" type="int"> + </argument> + <description> + Return a [Material] in a given surface. Surface is rendered using this material. + </description> + </method> </methods> <members> <member name="lightmap_size_hint" type="Vector2" setter="set_lightmap_size_hint" getter="get_lightmap_size_hint"> @@ -124,22 +158,31 @@ <constant name="ARRAY_COMPRESS_DEFAULT" value="97280" enum="ArrayFormat"> </constant> <constant name="ARRAY_VERTEX" value="0" enum="ArrayType"> + Array of vertices. </constant> <constant name="ARRAY_NORMAL" value="1" enum="ArrayType"> + Array of normals. </constant> <constant name="ARRAY_TANGENT" value="2" enum="ArrayType"> + Array of tangents as an array of floats, 4 floats per tangent. </constant> <constant name="ARRAY_COLOR" value="3" enum="ArrayType"> + Array of colors. </constant> <constant name="ARRAY_TEX_UV" value="4" enum="ArrayType"> + Array of UV coordinates. </constant> <constant name="ARRAY_TEX_UV2" value="5" enum="ArrayType"> + Array of second set of UV coordinates. </constant> <constant name="ARRAY_BONES" value="6" enum="ArrayType"> + Array of bone data. </constant> <constant name="ARRAY_WEIGHTS" value="7" enum="ArrayType"> + Array of weights. </constant> <constant name="ARRAY_INDEX" value="8" enum="ArrayType"> + Array of indices. </constant> <constant name="ARRAY_MAX" value="9" enum="ArrayType"> </constant> diff --git a/doc/classes/MeshDataTool.xml b/doc/classes/MeshDataTool.xml index 43d94004a8..c90dba1561 100644 --- a/doc/classes/MeshDataTool.xml +++ b/doc/classes/MeshDataTool.xml @@ -1,8 +1,22 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="MeshDataTool" inherits="Reference" category="Core" version="3.1"> <brief_description> + Helper tool to access and edit [Mesh] data. </brief_description> <description> + The MeshDataTool provides access to individual vertices in a [Mesh]. It allows users to read and edit vertex data of meshes. It also creates an array of faces and edges. + To use the MeshDataTool, load a mesh with [method create_from_surface]. When you are finished editing the data commit the data to a mesh with [method commit_to_surface]. + Below is an example of how the MeshDataTool may be used. + [codeblock] + var mdt = MeshDataTool.new() + mdt.create_from_surface(mesh, 0) + for i in range(mdt.get_vertex_count()): + var vertex = mdt.get_vertex(i) + ... + mdt.set_vertex(i, vertex) + mesh.surface_remove(0) + mdt.commit_to_surface(mesh) + [/codeblock] </description> <tutorials> </tutorials> @@ -13,6 +27,7 @@ <return type="void"> </return> <description> + Clears all data currently in MeshDataTool. </description> </method> <method name="commit_to_surface"> @@ -21,6 +36,7 @@ <argument index="0" name="mesh" type="ArrayMesh"> </argument> <description> + Adds a new surface to specified [Mesh] with edited data. </description> </method> <method name="create_from_surface"> @@ -31,12 +47,15 @@ <argument index="1" name="surface" type="int"> </argument> <description> + Uses specified surface of given [Mesh] to populate data for MeshDataTool. + Requires [Mesh] with primitive type [code]PRIMITIVE_TRIANGLES[/code]. </description> </method> <method name="get_edge_count" qualifiers="const"> <return type="int"> </return> <description> + Returns the number of edges in this [Mesh]. </description> </method> <method name="get_edge_faces" qualifiers="const"> @@ -45,6 +64,7 @@ <argument index="0" name="idx" type="int"> </argument> <description> + Returns array of faces that touch given edge. </description> </method> <method name="get_edge_meta" qualifiers="const"> @@ -53,6 +73,7 @@ <argument index="0" name="idx" type="int"> </argument> <description> + Returns meta information assigned to given edge. </description> </method> <method name="get_edge_vertex" qualifiers="const"> @@ -63,12 +84,15 @@ <argument index="1" name="vertex" type="int"> </argument> <description> + Returns index of specified vertex connected to given edge. + Vertex argument can only be 0 or 1 because edges are comprised of two vertices. </description> </method> <method name="get_face_count" qualifiers="const"> <return type="int"> </return> <description> + Returns the number of faces in this [Mesh]. </description> </method> <method name="get_face_edge" qualifiers="const"> @@ -79,6 +103,8 @@ <argument index="1" name="edge" type="int"> </argument> <description> + Returns specified edge associated with given face. + Edge argument must 2 or less because a face only has three edges. </description> </method> <method name="get_face_meta" qualifiers="const"> @@ -87,6 +113,7 @@ <argument index="0" name="idx" type="int"> </argument> <description> + Returns meta data associated with given face. </description> </method> <method name="get_face_normal" qualifiers="const"> @@ -95,6 +122,7 @@ <argument index="0" name="idx" type="int"> </argument> <description> + Calculates and returns face normal of given face. </description> </method> <method name="get_face_vertex" qualifiers="const"> @@ -105,18 +133,23 @@ <argument index="1" name="vertex" type="int"> </argument> <description> + Returns specified vertex of given face. + Vertex argument must be 2 or less because faces contain three vertices. </description> </method> <method name="get_format" qualifiers="const"> <return type="int"> </return> <description> + Returns format of [Mesh]. Format is an integer made up of [Mesh] format flags combined together. For example, a mesh containing both vertices and normals would return a format of [code]3[/code] because [code]ARRAY_FORMAT_VERTEX[/code] is [code]1[/code] and [code]ARRAY_FORMAT_NORMAL[/code] is [code]2[/code]. + For list of format flags see [ArrayMesh]. </description> </method> <method name="get_material" qualifiers="const"> <return type="Material"> </return> <description> + Returns material assigned to the [Mesh]. </description> </method> <method name="get_vertex" qualifiers="const"> @@ -125,6 +158,7 @@ <argument index="0" name="idx" type="int"> </argument> <description> + Returns the vertex at given index. </description> </method> <method name="get_vertex_bones" qualifiers="const"> @@ -133,6 +167,7 @@ <argument index="0" name="idx" type="int"> </argument> <description> + Returns the bones of the given vertex. </description> </method> <method name="get_vertex_color" qualifiers="const"> @@ -141,12 +176,14 @@ <argument index="0" name="idx" type="int"> </argument> <description> + Returns the color of the given vertex. </description> </method> <method name="get_vertex_count" qualifiers="const"> <return type="int"> </return> <description> + Returns the total number of vertices in [Mesh]. </description> </method> <method name="get_vertex_edges" qualifiers="const"> @@ -155,6 +192,7 @@ <argument index="0" name="idx" type="int"> </argument> <description> + Returns array of edges that share given vertex. </description> </method> <method name="get_vertex_faces" qualifiers="const"> @@ -163,6 +201,7 @@ <argument index="0" name="idx" type="int"> </argument> <description> + Returns array of faces that share given vertex. </description> </method> <method name="get_vertex_meta" qualifiers="const"> @@ -171,6 +210,7 @@ <argument index="0" name="idx" type="int"> </argument> <description> + Returns meta data associated with given vertex. </description> </method> <method name="get_vertex_normal" qualifiers="const"> @@ -179,6 +219,7 @@ <argument index="0" name="idx" type="int"> </argument> <description> + Returns normal of given vertex. </description> </method> <method name="get_vertex_tangent" qualifiers="const"> @@ -187,6 +228,7 @@ <argument index="0" name="idx" type="int"> </argument> <description> + Returns tangent of given vertex. </description> </method> <method name="get_vertex_uv" qualifiers="const"> @@ -195,6 +237,7 @@ <argument index="0" name="idx" type="int"> </argument> <description> + Returns UV of given vertex. </description> </method> <method name="get_vertex_uv2" qualifiers="const"> @@ -203,6 +246,7 @@ <argument index="0" name="idx" type="int"> </argument> <description> + Returns UV2 of given vertex. </description> </method> <method name="get_vertex_weights" qualifiers="const"> @@ -211,6 +255,7 @@ <argument index="0" name="idx" type="int"> </argument> <description> + Returns bone weights of given vertex. </description> </method> <method name="set_edge_meta"> @@ -221,6 +266,7 @@ <argument index="1" name="meta" type="Variant"> </argument> <description> + Sets the meta data of given edge. </description> </method> <method name="set_face_meta"> @@ -231,6 +277,7 @@ <argument index="1" name="meta" type="Variant"> </argument> <description> + Sets the meta data of given face. </description> </method> <method name="set_material"> @@ -239,6 +286,7 @@ <argument index="0" name="material" type="Material"> </argument> <description> + Sets the material to be used by newly constructed [Mesh]. </description> </method> <method name="set_vertex"> @@ -249,6 +297,7 @@ <argument index="1" name="vertex" type="Vector3"> </argument> <description> + Sets the position of given vertex. </description> </method> <method name="set_vertex_bones"> @@ -259,6 +308,7 @@ <argument index="1" name="bones" type="PoolIntArray"> </argument> <description> + Sets the bones of given vertex. </description> </method> <method name="set_vertex_color"> @@ -269,6 +319,7 @@ <argument index="1" name="color" type="Color"> </argument> <description> + Sets the color of given vertex. </description> </method> <method name="set_vertex_meta"> @@ -279,6 +330,7 @@ <argument index="1" name="meta" type="Variant"> </argument> <description> + Sets the meta data associated with given vertex. </description> </method> <method name="set_vertex_normal"> @@ -289,6 +341,7 @@ <argument index="1" name="normal" type="Vector3"> </argument> <description> + Sets the normal of given vertex. </description> </method> <method name="set_vertex_tangent"> @@ -299,6 +352,7 @@ <argument index="1" name="tangent" type="Plane"> </argument> <description> + Sets the tangent of given vertex. </description> </method> <method name="set_vertex_uv"> @@ -309,6 +363,7 @@ <argument index="1" name="uv" type="Vector2"> </argument> <description> + Sets the UV of given vertex. </description> </method> <method name="set_vertex_uv2"> @@ -319,6 +374,7 @@ <argument index="1" name="uv2" type="Vector2"> </argument> <description> + Sets the UV2 of given vertex. </description> </method> <method name="set_vertex_weights"> @@ -329,6 +385,7 @@ <argument index="1" name="weights" type="PoolRealArray"> </argument> <description> + Sets the bone weights of given vertex. </description> </method> </methods> diff --git a/doc/classes/MeshInstance.xml b/doc/classes/MeshInstance.xml index ef42726ca9..bcc9a7cad9 100644 --- a/doc/classes/MeshInstance.xml +++ b/doc/classes/MeshInstance.xml @@ -41,6 +41,12 @@ Returns the [Material] for a surface of the [Mesh] resource. </description> </method> + <method name="get_surface_material_count" qualifiers="const"> + <return type="int"> + </return> + <description> + </description> + </method> <method name="set_surface_material"> <return type="void"> </return> diff --git a/doc/classes/MultiMesh.xml b/doc/classes/MultiMesh.xml index b78d82199c..1908fdd684 100644 --- a/doc/classes/MultiMesh.xml +++ b/doc/classes/MultiMesh.xml @@ -30,6 +30,14 @@ Get the color of a specific instance. </description> </method> + <method name="get_instance_custom_data" qualifiers="const"> + <return type="Color"> + </return> + <argument index="0" name="instance" type="int"> + </argument> + <description> + </description> + </method> <method name="get_instance_transform" qualifiers="const"> <return type="Transform"> </return> @@ -50,6 +58,16 @@ Set the color of a specific instance. </description> </method> + <method name="set_instance_custom_data"> + <return type="void"> + </return> + <argument index="0" name="instance" type="int"> + </argument> + <argument index="1" name="custom_data" type="Color"> + </argument> + <description> + </description> + </method> <method name="set_instance_transform"> <return type="void"> </return> @@ -65,6 +83,8 @@ <members> <member name="color_format" type="int" setter="set_color_format" getter="get_color_format" enum="MultiMesh.ColorFormat"> </member> + <member name="custom_data_format" type="int" setter="set_custom_data_format" getter="get_custom_data_format" enum="MultiMesh.CustomDataFormat"> + </member> <member name="instance_count" type="int" setter="set_instance_count" getter="get_instance_count"> </member> <member name="mesh" type="Mesh" setter="set_mesh" getter="get_mesh"> @@ -83,5 +103,11 @@ </constant> <constant name="COLOR_FLOAT" value="2" enum="ColorFormat"> </constant> + <constant name="CUSTOM_DATA_NONE" value="0" enum="CustomDataFormat"> + </constant> + <constant name="CUSTOM_DATA_8BIT" value="1" enum="CustomDataFormat"> + </constant> + <constant name="CUSTOM_DATA_FLOAT" value="2" enum="CustomDataFormat"> + </constant> </constants> </class> diff --git a/doc/classes/MultiplayerAPI.xml b/doc/classes/MultiplayerAPI.xml index 10e7f1b172..d8d7d9acfc 100644 --- a/doc/classes/MultiplayerAPI.xml +++ b/doc/classes/MultiplayerAPI.xml @@ -90,7 +90,7 @@ </methods> <members> <member name="network_peer" type="NetworkedMultiplayerPeer" setter="set_network_peer" getter="get_network_peer"> - The peer object to handle the RPC system (effectively enabling networking when set). Depending on the peer itself, the MultiplayerAPI will become a network server (check with [method is_network_server]) and will set root node's network mode to master (see NETWORK_MODE_* constants in [Node]), or it will become a regular peer with root node set to slave. All child nodes are set to inherit the network mode by default. Handling of networking-related events (connection, disconnection, new clients) is done by connecting to MultiplayerAPI's signals. + The peer object to handle the RPC system (effectively enabling networking when set). Depending on the peer itself, the MultiplayerAPI will become a network server (check with [method is_network_server]) and will set root node's network mode to master (see NETWORK_MODE_* constants in [Node]), or it will become a regular peer with root node set to puppet. All child nodes are set to inherit the network mode by default. Handling of networking-related events (connection, disconnection, new clients) is done by connecting to MultiplayerAPI's signals. </member> <member name="refuse_new_network_connections" type="bool" setter="set_refuse_new_network_connections" getter="is_refusing_new_network_connections"> If [code]true[/code] the MultiplayerAPI's [member network_peer] refuses new incoming connections. @@ -141,25 +141,28 @@ Used with [method Node.rpc_config] or [method Node.rset_config] to disable a method or property for all RPC calls, making it unavailable. Default for all methods. </constant> <constant name="RPC_MODE_REMOTE" value="1" enum="RPCMode"> - Used with [method Node.rpc_config] or [method Node.rset_config] to set a method to be called or a property to be changed only on the remote end, not locally. Analogous to the [code]remote[/code] keyword. Calls and property changes are accepted from all remote peers, no matter if they are node's master or slaves. + Used with [method Node.rpc_config] or [method Node.rset_config] to set a method to be called or a property to be changed only on the remote end, not locally. Analogous to the [code]remote[/code] keyword. Calls and property changes are accepted from all remote peers, no matter if they are node's master or puppets. </constant> - <constant name="RPC_MODE_SYNC" value="2" enum="RPCMode"> - Behave like [code]RPC_MODE_REMOTE[/code] but also make the call or property change locally. Analogous to the [code]sync[/code] keyword. + <constant name="RPC_MODE_MASTER" value="2" enum="RPCMode"> + Used with [method Node.rpc_config] or [method Node.rset_config] to set a method to be called or a property to be changed only on the network master for this node. Analogous to the [code]master[/code] keyword. Only accepts calls or property changes from the node's network puppets, see [method Node.set_network_master]. </constant> - <constant name="RPC_MODE_MASTER" value="3" enum="RPCMode"> - Used with [method Node.rpc_config] or [method Node.rset_config] to set a method to be called or a property to be changed only on the network master for this node. Analogous to the [code]master[/code] keyword. Only accepts calls or property changes from the node's network slaves, see [method Node.set_network_master]. + <constant name="RPC_MODE_PUPPET" value="3" enum="RPCMode"> + Used with [method Node.rpc_config] or [method Node.rset_config] to set a method to be called or a property to be changed only on puppets for this node. Analogous to the [code]puppet[/code] keyword. Only accepts calls or property changes from the node's network master, see [method Node.set_network_master]. </constant> - <constant name="RPC_MODE_SLAVE" value="4" enum="RPCMode"> - Used with [method Node.rpc_config] or [method Node.rset_config] to set a method to be called or a property to be changed only on slaves for this node. Analogous to the [code]slave[/code] keyword. Only accepts calls or property changes from the node's network master, see [method Node.set_network_master]. + <constant name="RPC_MODE_SLAVE" value="3" enum="RPCMode"> + Deprecated. Use [code]RPC_MODE_PUPPET[/code] instead. Analogous to the [code]slave[/code] keyword. </constant> - <constant name="RPC_MODE_REMOTESYNC" value="5" enum="RPCMode"> - Behave like [code]RPC_MODE_REMOTE[/code] but also make the call or property change locally. Same as [code]RPC_MODE_SYNC[/code] which is only kept for compatibility. Analogous to the [code]remotesync[/code] keyword. + <constant name="RPC_MODE_REMOTESYNC" value="4" enum="RPCMode"> + Behave like [code]RPC_MODE_REMOTE[/code] but also make the call or property change locally. Analogous to the [code]remotesync[/code] keyword. </constant> - <constant name="RPC_MODE_MASTERSYNC" value="6" enum="RPCMode"> + <constant name="RPC_MODE_SYNC" value="4" enum="RPCMode"> + Deprecated. Use [code]RPC_MODE_REMOTESYNC[/code] instead. Analogous to the [code]sync[/code] keyword. + </constant> + <constant name="RPC_MODE_MASTERSYNC" value="5" enum="RPCMode"> Behave like [code]RPC_MODE_MASTER[/code] but also make the call or property change locally. Analogous to the [code]mastersync[/code] keyword. </constant> - <constant name="RPC_MODE_SLAVESYNC" value="7" enum="RPCMode"> - Behave like [code]RPC_MODE_SLAVE[/code] but also make the call or property change locally. Analogous to the [code]slavesync[/code] keyword. + <constant name="RPC_MODE_PUPPETSYNC" value="6" enum="RPCMode"> + Behave like [code]RPC_MODE_PUPPET[/code] but also make the call or property change locally. Analogous to the [code]puppetsync[/code] keyword. </constant> </constants> </class> diff --git a/doc/classes/NavigationPolygon.xml b/doc/classes/NavigationPolygon.xml index b29e19e5d8..4ede80b98c 100644 --- a/doc/classes/NavigationPolygon.xml +++ b/doc/classes/NavigationPolygon.xml @@ -1,8 +1,27 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="NavigationPolygon" inherits="Resource" category="Core" version="3.1"> <brief_description> + A node that has methods to draw outlines or use indices of vertices to create navigation polygons. </brief_description> <description> + There are two ways to create polygons. Either by using the [method add_outline] method or using the [method add_polygon] method. + Using [method add_outline]: + [code] + var polygon = NavigationPolygon.new() + var outline = PoolVector2Array([Vector2(0, 0), Vector2(0, 50), Vector2(50, 50), Vector2(50, 0)]) + polygon.add_outline(outline) + polygon.make_polygons_from_outlines() + $NavigationPolygonInstance.navpoly = polygon + [/code] + Using [method add_polygon] and indices of the vertices array. + [code] + var polygon = NavigationPolygon.new() + var vertices = PoolVector2Array([Vector2(0, 0), Vector2(0, 50), Vector2(50, 50), Vector2(50, 0)]) + polygon.set_vertices(vertices) + var indices = PoolIntArray(0, 3, 1) + polygon.add_polygon(indices) + $NavigationPolygonInstance.navpoly = polygon + [/code] </description> <tutorials> </tutorials> @@ -15,6 +34,7 @@ <argument index="0" name="outline" type="PoolVector2Array"> </argument> <description> + Appends a [PoolVector2Array] that contains the vertices of an outline to the internal array that contains all the outlines. You have to call [method make_polygons_from_outlines] in order for this array to be converted to polygons that the engine will use. </description> </method> <method name="add_outline_at_index"> @@ -25,6 +45,7 @@ <argument index="1" name="index" type="int"> </argument> <description> + Adds a [PoolVector2Array] that contains the vertices of an outline to the internal array that contains all the outlines at a fixed position. You have to call [method make_polygons_from_outlines] in order for this array to be converted to polygons that the engine will use. </description> </method> <method name="add_polygon"> @@ -33,18 +54,21 @@ <argument index="0" name="polygon" type="PoolIntArray"> </argument> <description> + Adds a polygon using the indices of the vertices you get when calling [method get_vertices]. </description> </method> <method name="clear_outlines"> <return type="void"> </return> <description> + Clears the array of the outlines, but it doesn't clear the vertices and the polygons that were created by them. </description> </method> <method name="clear_polygons"> <return type="void"> </return> <description> + Clears the array of polygons, but it doesn't clear the array of outlines and vertices. </description> </method> <method name="get_outline" qualifiers="const"> @@ -53,12 +77,14 @@ <argument index="0" name="idx" type="int"> </argument> <description> + Returns a [PoolVector2Array] containing the vertices of an outline that was created in the editor or by script. </description> </method> <method name="get_outline_count" qualifiers="const"> <return type="int"> </return> <description> + Returns the number of outlines that were created in the editor or by script. </description> </method> <method name="get_polygon"> @@ -67,24 +93,28 @@ <argument index="0" name="idx" type="int"> </argument> <description> + Returns a [PoolIntArray] containing the indices of the vertices of a created polygon. </description> </method> <method name="get_polygon_count" qualifiers="const"> <return type="int"> </return> <description> + Returns the count of all polygons. </description> </method> <method name="get_vertices" qualifiers="const"> <return type="PoolVector2Array"> </return> <description> + Returns a [PoolVector2Array] containing all the vertices being used to create the polygons. </description> </method> <method name="make_polygons_from_outlines"> <return type="void"> </return> <description> + Creates polygons from the outlines added in the editor or by script. </description> </method> <method name="remove_outline"> @@ -93,6 +123,7 @@ <argument index="0" name="idx" type="int"> </argument> <description> + Removes an outline created in the editor or by script. You have to call [method make_polygons_from_outlines] for the polygons to update. </description> </method> <method name="set_outline"> @@ -103,6 +134,7 @@ <argument index="1" name="outline" type="PoolVector2Array"> </argument> <description> + Changes an outline created in the editor or by script. You have to call [method make_polygons_from_outlines] for the polygons to update. </description> </method> <method name="set_vertices"> @@ -111,6 +143,7 @@ <argument index="0" name="vertices" type="PoolVector2Array"> </argument> <description> + Sets the vertices that can be then indexed to create polygons with the [method add_polygon] method. </description> </method> </methods> diff --git a/doc/classes/NetworkedMultiplayerPeer.xml b/doc/classes/NetworkedMultiplayerPeer.xml index e878b3a746..990f928b25 100644 --- a/doc/classes/NetworkedMultiplayerPeer.xml +++ b/doc/classes/NetworkedMultiplayerPeer.xml @@ -7,7 +7,7 @@ Manages the connection to network peers. Assigns unique IDs to each client connected to the server. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/networking/high_level_multiplayer.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/networking/high_level_multiplayer.html</link> </tutorials> <demos> </demos> @@ -92,13 +92,13 @@ </signals> <constants> <constant name="TRANSFER_MODE_UNRELIABLE" value="0" enum="TransferMode"> - Packets are sent via unordered UDP packets. + Packets are not acknowledged, no resend attempts are made for lost packets. Packets may arrive in any order. Potentially faster than [code]TRANSFER_MODE_UNRELIABLE_ORDERED[/code]. Use for non-critical data, and always consider whether the order matters. </constant> <constant name="TRANSFER_MODE_UNRELIABLE_ORDERED" value="1" enum="TransferMode"> - Packets are sent via ordered UDP packets. + Packets are not acknowledged, no resend attempts are made for lost packets. Packets are received in the order they were sent in. Potentially faster than [code]TRANSFER_MODE_RELIABLE[/code]. Use for non-critical data or data that would be outdated if received late due to resend attempt(s) anyway, for example movement and positional data. </constant> <constant name="TRANSFER_MODE_RELIABLE" value="2" enum="TransferMode"> - Packets are sent via TCP packets. + Packets must be received and resend attempts should be made until the packets are acknowledged. Packets must be received in the order they were sent in. Most reliable transfer mode, but potentially slowest due to the overhead. Use for critical data that must be transmitted and arrive in order, for example an ability being triggered or a chat message. Consider carefully if the information really is critical, and use sparingly. </constant> <constant name="CONNECTION_DISCONNECTED" value="0" enum="ConnectionStatus"> The ongoing connection disconnected. diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index d02e3dfdfa..dcdb6d0500 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -12,12 +12,12 @@ [b]Processing:[/b] Nodes can override the "process" state, so that they receive a callback on each frame requesting them to process (do something). Normal processing (callback [method _process], toggled with [method set_process]) happens as fast as possible and is dependent on the frame rate, so the processing time [i]delta[/i] is passed as an argument. Physics processing (callback [method _physics_process], toggled with [method set_physics_process]) happens a fixed number of times per second (60 by default) and is useful for code related to the physics engine. Nodes can also process input events. When present, the [method _input] function will be called for each input that the program receives. In many cases, this can be overkill (unless used for simple projects), and the [method _unhandled_input] function might be preferred; it is called when the input event was not handled by anyone else (typically, GUI [Control] nodes), ensuring that the node only receives the events that were meant for it. To keep track of the scene hierarchy (especially when instancing scenes into other scenes), an "owner" can be set for the node with [method set_owner]. This keeps track of who instanced what. This is mostly useful when writing editors and tools, though. - Finally, when a node is freed with [method free] or [method queue_free], it will also free all its children. + Finally, when a node is freed with [method Object.free] or [method queue_free], it will also free all its children. [b]Groups:[/b] Nodes can be added to as many groups as you want to be easy to manage, you could create groups like "enemies" or "collectables" for example, depending on your game. See [method add_to_group], [method is_in_group] and [method remove_from_group]. You can then retrieve all nodes in these groups, iterate them and even call methods on groups via the methods on [SceneTree]. [b]Networking with nodes:[/b] After connecting to a server (or making one, see [NetworkedMultiplayerENet]) it is possible to use the built-in RPC (remote procedure call) system to communicate over the network. By calling [method rpc] with a method name, it will be called locally and in all connected peers (peers = clients and the server that accepts connections). To identify which node receives the RPC call Godot will use its [NodePath] (make sure node names are the same on all peers). Also take a look at the high-level networking tutorial and corresponding demos. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/getting_started/step_by_step/scenes_and_nodes.html</link> + <link>https://docs.godotengine.org/en/latest/getting_started/step_by_step/scenes_and_nodes.html</link> </tutorials> <demos> </demos> @@ -50,7 +50,7 @@ <argument index="0" name="event" type="InputEvent"> </argument> <description> - Called when there is an input event. The input event propagates through the node tree until a node consumes it. + Called when there is an input event. The input event propagates up through the node tree until a node consumes it. It is only called if input processing is enabled, which is done automatically if this method is overridden, and can be toggled with [method set_process_input]. To consume the input event and stop it propagating further to other nodes, [method SceneTree.set_input_as_handled] can be called. For gameplay input, [method _unhandled_input] and [method _unhandled_key_input] are usually a better fit as they allow the GUI to intercept the events first. @@ -93,7 +93,7 @@ <argument index="0" name="event" type="InputEvent"> </argument> <description> - Propagated to all nodes when the previous [InputEvent] is not consumed by any nodes. + Called when an [InputEvent] hasn't been consumed by [method _input] or any GUI. The input event propagates up through the node tree until a node consumes it. It is only called if unhandled input processing is enabled, which is done automatically if this method is overridden, and can be toggled with [method set_process_unhandled_input]. To consume the input event and stop it propagating further to other nodes, [method SceneTree.set_input_as_handled] can be called. For gameplay input, this and [method _unhandled_key_input] are usually a better fit than [method _input] as they allow the GUI to intercept the events first. @@ -105,7 +105,7 @@ <argument index="0" name="event" type="InputEventKey"> </argument> <description> - Propagated to all nodes when the previous [InputEventKey] is not consumed by any nodes. + Called when an [InputEventKey] hasn't been consumed by [method _input] or any GUI. The input event propagates up through the node tree until a node consumes it. It is only called if unhandled key input processing is enabled, which is done automatically if this method is overridden, and can be toggled with [method set_process_unhandled_key_input]. To consume the input event and stop it propagating further to other nodes, [method SceneTree.set_input_as_handled] can be called. For gameplay input, this and [method _unhandled_input] are usually a better fit than [method _input] as they allow the GUI to intercept the events first. @@ -176,6 +176,16 @@ </argument> <description> Finds a descendant of this node whose name matches [code]mask[/code] as in [method String.match] (i.e. case sensitive, but '*' matches zero or more characters and '?' matches any single character except '.'). Note that it does not match against the full path, just against individual node names. + If [code]owned[/code] is [code]true[/code], this method only finds nodes whose owner is this node. This is especially important for scenes instantiated through script, because those scenes don't have an owner. + </description> + </method> + <method name="find_parent" qualifiers="const"> + <return type="Node"> + </return> + <argument index="0" name="mask" type="String"> + </argument> + <description> + Finds the first parent of the current node whose name matches [code]mask[/code] as in [method String.match] (i.e. case sensitive, but '*' matches zero or more characters and '?' matches any single character except '.'). Note that it does not match against the full path, just against individual node names. </description> </method> <method name="get_child" qualifiers="const"> @@ -185,6 +195,7 @@ </argument> <description> Returns a child node by its index (see [method get_child_count]). This method is often used for iterating all children of a node. + To access a child node via its name, use [method get_node]. </description> </method> <method name="get_child_count" qualifiers="const"> @@ -581,7 +592,7 @@ <argument index="1" name="mode" type="int" enum="MultiplayerAPI.RPCMode"> </argument> <description> - Changes the RPC mode for the given [code]method[/code] to the given [code]mode[/code]. See [enum MultiplayerAPI.RPCMode]. An alternative is annotating methods and properties with the corresponding keywords ([code]remote[/code], [code]sync[/code], [code]master[/code], [code]slave[/code]). By default, methods are not exposed to networking (and RPCs). Also see [method rset] and [method rset_config] for properties. + Changes the RPC mode for the given [code]method[/code] to the given [code]mode[/code]. See [enum MultiplayerAPI.RPCMode]. An alternative is annotating methods and properties with the corresponding keywords ([code]remote[/code], [code]master[/code], [code]puppet[/code], [code]remotesync[/code], [code]mastersync[/code], [code]puppetsync[/code]). By default, methods are not exposed to networking (and RPCs). Also see [method rset] and [method rset_config] for properties. </description> </method> <method name="rpc_id" qualifiers="vararg"> @@ -634,7 +645,7 @@ <argument index="1" name="mode" type="int" enum="MultiplayerAPI.RPCMode"> </argument> <description> - Changes the RPC mode for the given [code]property[/code] to the given [code]mode[/code]. See [enum MultiplayerAPI.RPCMode]. An alternative is annotating methods and properties with the corresponding keywords ([code]remote[/code], [code]sync[/code], [code]master[/code], [code]slave[/code]). By default, properties are not exposed to networking (and RPCs). Also see [method rpc] and [method rpc_config] for methods. + Changes the RPC mode for the given [code]property[/code] to the given [code]mode[/code]. See [enum MultiplayerAPI.RPCMode]. An alternative is annotating methods and properties with the corresponding keywords ([code]remote[/code], [code]master[/code], [code]puppet[/code], [code]remotesync[/code], [code]mastersync[/code], [code]puppetsync[/code]). By default, properties are not exposed to networking (and RPCs). Also see [method rpc] and [method rpc_config] for methods. </description> </method> <method name="rset_id"> @@ -691,7 +702,7 @@ <argument index="1" name="recursive" type="bool" default="true"> </argument> <description> - Sets the node's network master to the peer with the given peer ID. The network master is the peer that has authority over the node on the network. Useful in conjunction with the [code]master[/code] and [code]slave[/code] keywords. Inherited from the parent node by default, which ultimately defaults to peer ID 1 (the server). If [code]recursive[/code], the given peer is recursively set as the master for all children of this node. + Sets the node's network master to the peer with the given peer ID. The network master is the peer that has authority over the node on the network. Useful in conjunction with the [code]master[/code] and [code]puppet[/code] keywords. Inherited from the parent node by default, which ultimately defaults to peer ID 1 (the server). If [code]recursive[/code], the given peer is recursively set as the master for all children of this node. </description> </method> <method name="set_physics_process"> diff --git a/doc/classes/Node2D.xml b/doc/classes/Node2D.xml index 13eabeca17..e955f6bb7d 100644 --- a/doc/classes/Node2D.xml +++ b/doc/classes/Node2D.xml @@ -7,7 +7,7 @@ A 2D game object, with a position, rotation and scale. All 2D physics nodes and sprites inherit from Node2D. Use Node2D as a parent node to move, scale and rotate children in a 2D project. Also gives control on the node's render order. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/2d/custom_drawing_in_2d.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/2d/custom_drawing_in_2d.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml index cf86176086..e218949757 100644 --- a/doc/classes/OS.xml +++ b/doc/classes/OS.xml @@ -43,6 +43,12 @@ Centers the window on the screen if in windowed mode. </description> </method> + <method name="close_midi_inputs"> + <return type="void"> + </return> + <description> + </description> + </method> <method name="delay_msec" qualifiers="const"> <return type="void"> </return> @@ -95,7 +101,7 @@ </argument> <description> Execute the file at the given path with the arguments passed as an array of strings. Platform path resolution will take place. The resolved file must exist and be executable. - The arguments are used in the given order and separated by a space, so [code]OS.execute('ping', ['-c', '3', 'godotengine.org'])[/code] will resolve to [code]ping -c 3 godotengine.org[/code] in the system's shell. + The arguments are used in the given order and separated by a space, so [code]OS.execute('ping', ['-w', '3', 'godotengine.org'], false)[/code] will resolve to [code]ping -w 3 godotengine.org[/code] in the system's shell. This method has slightly different behaviour based on whether the [code]blocking[/code] mode is enabled. When [code]blocking[/code] is enabled, the Godot thread will pause its execution while waiting for the process to terminate. The shell output of the process will be written to the [code]output[/code] array as a single string. When the process terminates, the Godot thread will resume execution. When [code]blocking[/code] is disabled, the Godot thread will continue while the new process runs. It is not possible to retrieve the shell output in non-blocking mode, so [code]output[/code] will be empty. @@ -550,6 +556,13 @@ Note that this method can also be used to kill processes that were not spawned by the game. </description> </method> + <method name="move_window_to_foreground"> + <return type="void"> + </return> + <description> + Moves the window to the front. + </description> + </method> <method name="native_video_is_playing"> <return type="bool"> </return> @@ -577,6 +590,7 @@ </argument> <description> Plays native video from the specified path, at the given volume and with audio and subtitle tracks. + Note: This method is only implemented on Android and iOS, and the current Android implementation does not support the [code]volume[/code], [code]audio_track[/code] and [code]subtitle_track[/code] options. </description> </method> <method name="native_video_stop"> @@ -593,6 +607,12 @@ Resumes native video playback. </description> </method> + <method name="open_midi_inputs"> + <return type="void"> + </return> + <description> + </description> + </method> <method name="print_all_resources"> <return type="void"> </return> diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml index f4ad196ad2..fcd105d66b 100644 --- a/doc/classes/Object.xml +++ b/doc/classes/Object.xml @@ -185,7 +185,7 @@ </argument> <description> Get indexed object property by String. - Property indices get accessed with colon seperation, for example: [code]position:x[/code] + Property indices get accessed with colon separation, for example: [code]position:x[/code] </description> </method> <method name="get_instance_id" qualifiers="const"> diff --git a/doc/classes/OmniLight.xml b/doc/classes/OmniLight.xml index 5ed058bb06..8d67cb626b 100644 --- a/doc/classes/OmniLight.xml +++ b/doc/classes/OmniLight.xml @@ -7,7 +7,7 @@ An Omnidirectional light is a type of [Light] that emits light in all directions. The light is attenuated by distance and this attenuation can be configured by changing its energy, radius, and attenuation parameters. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/3d/lights_and_shadows.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/3d/lights_and_shadows.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/PackedScene.xml b/doc/classes/PackedScene.xml index 8d810bc9c4..7dea004b90 100644 --- a/doc/classes/PackedScene.xml +++ b/doc/classes/PackedScene.xml @@ -6,12 +6,25 @@ <description> A simplified interface to a scene file. Provides access to operations and checks that can be performed on the scene resource itself. Can be used to save a node to a file. When saving, the node as well as all the node it owns get saved (see [code]owner[/code] property on [Node]). Note that the node doesn't need to own itself. - Example of saving a node: + Example of saving a node with different owners: The following example creates 3 objects: [code]Node2D[/code] ([code]node[/code]), [code]RigidBody2D[/code] ([code]rigid[/code]) and [code]CollisionObject2D[/code] ([code]collision[/code]). [code]collision[/code] is a child of [code]rigid[/code] which is a child of [code]node[/code]. Only [code]rigid[/code] is owned by [code]node[/code] and [code]pack[/code] will therefore only save those two nodes, but not [code]collision[/code]. [codeblock] + # create the objects + var node = Node2D.new() + var rigid = RigidBody2D.new() + var collision = CollisionShape2D.new() + + # create the object hierarchy + rigid.add_child(collision) + node.add_child(rigid) + + # change owner of rigid, but not of collision + rigid.set_owner(node) + var scene = PackedScene.new() - var result = scene.pack(child) + # only node and rigid are now packed + var result = scene.pack(node) if result == OK: - ResourceSaver.save("res://path/name.scn", scene) // or user://... + ResourceSaver.save("res://path/name.scn", scene) # or user://... [/codeblock] </description> <tutorials> @@ -39,7 +52,7 @@ <argument index="0" name="edit_state" type="int" enum="PackedScene.GenEditState" default="0"> </argument> <description> - Instantiates the scene's node hierarchy. Triggers child scene instantiation(s). Triggers the [enum Object.NOTIFICATION_INSTANCED] notification on the root node. + Instantiates the scene's node hierarchy. Triggers child scene instantiation(s). Triggers [Node]'s [code]NOTIFICATION_INSTANCED[/code] notification on the root node. </description> </method> <method name="pack"> diff --git a/doc/classes/ParallaxLayer.xml b/doc/classes/ParallaxLayer.xml index 662a15e043..e6ea166282 100644 --- a/doc/classes/ParallaxLayer.xml +++ b/doc/classes/ParallaxLayer.xml @@ -6,6 +6,7 @@ <description> A ParallaxLayer must be the child of a [ParallaxBackground] node. Each ParallaxLayer can be set to move at different speeds relative to the camera movement or the [member ParallaxBackground.scroll_offset] value. This node's children will be affected by its scroll offset. + Note that any changes to this node's position and scale made after it enters the scene will be ignored. </description> <tutorials> </tutorials> diff --git a/doc/classes/Particles.xml b/doc/classes/Particles.xml index b03cf6cadb..2e30750bf9 100644 --- a/doc/classes/Particles.xml +++ b/doc/classes/Particles.xml @@ -77,7 +77,7 @@ Emission randomness ratio. Default value: [code]0[/code]. </member> <member name="speed_scale" type="float" setter="set_speed_scale" getter="get_speed_scale"> - Speed scaling ratio. Default value: [code]1[/code]. + Speed scaling ratio. Default value: [code]1[/code]. A value of [code]0[/code] can be used to pause the particles. </member> <member name="visibility_aabb" type="AABB" setter="set_visibility_aabb" getter="get_visibility_aabb"> The [AABB] that determines the area of the world part of which needs to be visible on screen for the particle system to be active. diff --git a/doc/classes/Particles2D.xml b/doc/classes/Particles2D.xml index 47265054b0..f872552a49 100644 --- a/doc/classes/Particles2D.xml +++ b/doc/classes/Particles2D.xml @@ -66,7 +66,7 @@ Emission lifetime randomness ratio. Default value: [code]0[/code]. </member> <member name="speed_scale" type="float" setter="set_speed_scale" getter="get_speed_scale"> - Particle system's running speed scaling ratio. Default value: [code]1[/code]. + Particle system's running speed scaling ratio. Default value: [code]1[/code]. A value of [code]0[/code] can be used to pause the particles. </member> <member name="texture" type="Texture" setter="set_texture" getter="get_texture"> Particle texture. If [code]null[/code] particles will be squares. diff --git a/doc/classes/PathFollow2D.xml b/doc/classes/PathFollow2D.xml index f9940dab2f..515c921d0d 100644 --- a/doc/classes/PathFollow2D.xml +++ b/doc/classes/PathFollow2D.xml @@ -23,6 +23,7 @@ The node's offset along the curve. </member> <member name="lookahead" type="float" setter="set_lookahead" getter="get_lookahead"> + How far to look ahead of the curve to calculate the tangent if the node is rotating. E.g. shorter lookaheads will lead to faster rotations. Default value: [code]4[/code]. </member> <member name="loop" type="bool" setter="set_loop" getter="has_loop"> If [code]true[/code], any offset outside the path's length will wrap around, instead of stopping at the ends. Use it for cyclic paths. diff --git a/doc/classes/Physics2DDirectSpaceState.xml b/doc/classes/Physics2DDirectSpaceState.xml index f0fee77a5a..ebd17fd921 100644 --- a/doc/classes/Physics2DDirectSpaceState.xml +++ b/doc/classes/Physics2DDirectSpaceState.xml @@ -7,7 +7,7 @@ Direct access object to a space in the [Physics2DServer]. It's used mainly to do queries against objects and areas residing in a given space. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/physics/ray-casting.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/physics/ray-casting.html</link> </tutorials> <demos> </demos> @@ -19,7 +19,7 @@ </argument> <description> Checks how far the shape can travel toward a point. Note that both the shape and the motion are supplied through a [Physics2DShapeQueryParameters] object. The method will return an array with two floats between 0 and 1, both representing a fraction of [code]motion[/code]. The first is how far the shape can move without triggering a collision, and the second is the point at which a collision will occur. If no collision is detected, the returned array will be [1, 1]. - If the shape can not move, the array will be empty ([code]dir.empty()==true[/code]). + If the shape can not move, the array will be empty. </description> </method> <method name="collide_shape"> @@ -47,7 +47,7 @@ [code]point[/code]: The intersection point. [code]rid[/code]: The intersecting object's [RID]. [code]shape[/code]: The shape index of the colliding shape. - If the shape did not intersect anything, then an empty dictionary ([code]dir.empty()==true[/code]) is returned instead. + If the shape did not intersect anything, then an empty dictionary is returned instead. </description> </method> <method name="intersect_point"> @@ -61,6 +61,10 @@ </argument> <argument index="3" name="collision_layer" type="int" default="2147483647"> </argument> + <argument index="4" name="collide_with_bodies" type="bool" default="true"> + </argument> + <argument index="5" name="collide_with_areas" type="bool" default="false"> + </argument> <description> Checks whether a point is inside any shape. The shapes the point is inside of are returned in an array containing dictionaries with the following fields: [code]collider[/code]: The colliding object. @@ -68,7 +72,7 @@ [code]metadata[/code]: The intersecting shape's metadata. This metadata is different from [method Object.get_meta], and is set with [method Physics2DServer.shape_set_data]. [code]rid[/code]: The intersecting object's [RID]. [code]shape[/code]: The shape index of the colliding shape. - Additionally, the method can take an array of objects or [RID]s that are to be excluded from collisions, or a bitmask representing the physics layers to check in. + Additionally, the method can take an [code]exclude[/code] array of objects or [RID]s that are to be excluded from collisions, a [code]collision_mask[/code] bitmask representing the physics layers to check in, or booleans to determine if the ray should collide with [PhysicsBody]s or [Area]s, respectively. </description> </method> <method name="intersect_ray"> @@ -82,6 +86,10 @@ </argument> <argument index="3" name="collision_layer" type="int" default="2147483647"> </argument> + <argument index="4" name="collide_with_bodies" type="bool" default="true"> + </argument> + <argument index="5" name="collide_with_areas" type="bool" default="false"> + </argument> <description> Intersects a ray in a given space. The returned object is a dictionary with the following fields: [code]collider[/code]: The colliding object. @@ -91,8 +99,8 @@ [code]position[/code]: The intersection point. [code]rid[/code]: The intersecting object's [RID]. [code]shape[/code]: The shape index of the colliding shape. - If the ray did not intersect anything, then an empty dictionary ([code]dir.empty()==true[/code]) is returned instead. - Additionally, the method can take an array of objects or [RID]s that are to be excluded from collisions, or a bitmask representing the physics layers to check in. + If the ray did not intersect anything, then an empty dictionary is returned instead. + Additionally, the method can take an [code]exclude[/code] array of objects or [RID]s that are to be excluded from collisions, a [code]collision_mask[/code] bitmask representing the physics layers to check in, or booleans to determine if the ray should collide with [PhysicsBody]s or [Area]s, respectively. </description> </method> <method name="intersect_shape"> @@ -109,7 +117,7 @@ [code]metadata[/code]: The intersecting shape's metadata. This metadata is different from [method Object.get_meta], and is set with [method Physics2DServer.shape_set_data]. [code]rid[/code]: The intersecting object's [RID]. [code]shape[/code]: The shape index of the colliding shape. - The number of intersections can be limited with the second parameter, to reduce the processing time. + The number of intersections can be limited with the [code]max_results[/code] parameter, to reduce the processing time. </description> </method> </methods> diff --git a/doc/classes/Physics2DShapeQueryParameters.xml b/doc/classes/Physics2DShapeQueryParameters.xml index 391ad6276f..f9e0c5e3de 100644 --- a/doc/classes/Physics2DShapeQueryParameters.xml +++ b/doc/classes/Physics2DShapeQueryParameters.xml @@ -22,6 +22,10 @@ </method> </methods> <members> + <member name="collide_with_areas" type="bool" setter="set_collide_with_areas" getter="is_collide_with_areas_enabled"> + </member> + <member name="collide_with_bodies" type="bool" setter="set_collide_with_bodies" getter="is_collide_with_bodies_enabled"> + </member> <member name="collision_layer" type="int" setter="set_collision_layer" getter="get_collision_layer"> The physics layer the query should be made on. </member> diff --git a/doc/classes/PhysicsBody.xml b/doc/classes/PhysicsBody.xml index 14053c6a35..2658732f84 100644 --- a/doc/classes/PhysicsBody.xml +++ b/doc/classes/PhysicsBody.xml @@ -7,7 +7,7 @@ PhysicsBody is an abstract base class for implementing a physics body. All *Body types inherit from it. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html</link> </tutorials> <demos> </demos> @@ -21,12 +21,20 @@ Adds a body to the list of bodies that this body can't collide with. </description> </method> + <method name="get_collision_exceptions"> + <return type="Array"> + </return> + <description> + Returns an array of nodes that were added as collision exceptions for this body. + </description> + </method> <method name="get_collision_layer_bit" qualifiers="const"> <return type="bool"> </return> <argument index="0" name="bit" type="int"> </argument> <description> + Returns an individual bit on the collision mask. </description> </method> <method name="get_collision_mask_bit" qualifiers="const"> @@ -35,6 +43,7 @@ <argument index="0" name="bit" type="int"> </argument> <description> + Returns an individual bit on the collision mask. </description> </method> <method name="remove_collision_exception_with"> @@ -54,6 +63,7 @@ <argument index="1" name="value" type="bool"> </argument> <description> + Sets individual bits on the layer mask. Use this if you only need to change one layer's value. </description> </method> <method name="set_collision_mask_bit"> @@ -64,6 +74,7 @@ <argument index="1" name="value" type="bool"> </argument> <description> + Sets individual bits on the collision mask. Use this if you only need to change one layer's value. </description> </method> </methods> @@ -74,7 +85,7 @@ A contact is detected if object A is in any of the layers that object B scans, or object B is in any layer scanned by object A. </member> <member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask"> - The physics layers this area can scan for collisions. + The physics layers this area scans for collisions. </member> </members> <constants> diff --git a/doc/classes/PhysicsBody2D.xml b/doc/classes/PhysicsBody2D.xml index ccc704c7ec..f848b6df70 100644 --- a/doc/classes/PhysicsBody2D.xml +++ b/doc/classes/PhysicsBody2D.xml @@ -7,7 +7,7 @@ PhysicsBody2D is an abstract base class for implementing a physics body. All *Body2D types inherit from it. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html</link> </tutorials> <demos> </demos> @@ -21,13 +21,20 @@ Adds a body to the list of bodies that this body can't collide with. </description> </method> + <method name="get_collision_exceptions"> + <return type="Array"> + </return> + <description> + Returns an array of nodes that were added as collision exceptions for this body. + </description> + </method> <method name="get_collision_layer_bit" qualifiers="const"> <return type="bool"> </return> <argument index="0" name="bit" type="int"> </argument> <description> - Return an individual bit on the collision mask. + Returns an individual bit on the collision mask. </description> </method> <method name="get_collision_mask_bit" qualifiers="const"> @@ -36,7 +43,7 @@ <argument index="0" name="bit" type="int"> </argument> <description> - Return an individual bit on the collision mask. + Returns an individual bit on the collision mask. </description> </method> <method name="remove_collision_exception_with"> @@ -56,7 +63,7 @@ <argument index="1" name="value" type="bool"> </argument> <description> - Set/clear individual bits on the layer mask. This makes getting a body in/out of only one layer easier. + Sets individual bits on the layer mask. Use this if you only need to change one layer's value. </description> </method> <method name="set_collision_mask_bit"> @@ -67,7 +74,7 @@ <argument index="1" name="value" type="bool"> </argument> <description> - Set/clear individual bits on the collision mask. This makes selecting the areas scanned easier. + Sets individual bits on the collision mask. Use this if you only need to change one layer's value. </description> </method> </methods> @@ -78,10 +85,10 @@ A contact is detected if object A is in any of the layers that object B scans, or object B is in any layer scanned by object A. </member> <member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask"> - The physics layers this area can scan for collisions. + The physics layers this area scans for collisions. </member> <member name="layers" type="int" setter="_set_layers" getter="_get_layers"> - Both collision_layer and collision_mask. Returns collision_layer when accessed. Updates collision_layers and collision_mask when modified. + Both [member collision_layer] and [member collision_mask]. Returns [member collision_layer] when accessed. Updates [member collision_layer] and [member collision_mask] when modified. </member> </members> <constants> diff --git a/doc/classes/PhysicsDirectBodyState.xml b/doc/classes/PhysicsDirectBodyState.xml index 91fc4df4ff..2f3501ae5d 100644 --- a/doc/classes/PhysicsDirectBodyState.xml +++ b/doc/classes/PhysicsDirectBodyState.xml @@ -15,6 +15,8 @@ <argument index="0" name="force" type="Vector3"> </argument> <description> + Adds a constant directional force without affecting rotation. + This is equivalent to [code]add_force(force, Vector3(0,0,0))[/code]. </description> </method> <method name="add_force"> @@ -25,6 +27,7 @@ <argument index="1" name="position" type="Vector3"> </argument> <description> + Adds a constant force (i.e. acceleration). </description> </method> <method name="add_torque"> @@ -33,6 +36,7 @@ <argument index="0" name="torque" type="Vector3"> </argument> <description> + Adds a constant rotational force (i.e. a motor) without affecting position. </description> </method> <method name="apply_central_impulse"> @@ -41,6 +45,8 @@ <argument index="0" name="j" type="Vector3"> </argument> <description> + Applies a single directional impulse without affecting rotation. + This is equivalent to ``apply_impulse(Vector3(0,0,0), impulse)``. </description> </method> <method name="apply_impulse"> @@ -51,6 +57,7 @@ <argument index="1" name="j" type="Vector3"> </argument> <description> + Apply a positioned impulse (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 once, and only once. Both the impulse and the position are in global coordinates, and the position is relative to the object's origin. </description> </method> <method name="apply_torque_impulse"> @@ -59,6 +66,7 @@ <argument index="0" name="j" type="Vector3"> </argument> <description> + Apply a torque impulse (which will be affected by the body mass and shape). This will rotate the body around the passed in vector. </description> </method> <method name="get_contact_collider" qualifiers="const"> diff --git a/doc/classes/PhysicsDirectSpaceState.xml b/doc/classes/PhysicsDirectSpaceState.xml index 3f0e1a4f70..350a9ed3ce 100644 --- a/doc/classes/PhysicsDirectSpaceState.xml +++ b/doc/classes/PhysicsDirectSpaceState.xml @@ -7,7 +7,7 @@ Direct access object to a space in the [PhysicsServer]. It's used mainly to do queries against objects and areas residing in a given space. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/physics/ray-casting.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/physics/ray-casting.html</link> </tutorials> <demos> </demos> @@ -21,7 +21,7 @@ </argument> <description> Checks whether the shape can travel to a point. The method will return an array with two floats between 0 and 1, both representing a fraction of [code]motion[/code]. The first is how far the shape can move without triggering a collision, and the second is the point at which a collision will occur. If no collision is detected, the returned array will be [1, 1]. - If the shape can not move, the array will be empty ([code]dir.empty()==true[/code]). + If the shape can not move, the returned array will be [0, 0]. </description> </method> <method name="collide_shape"> @@ -41,14 +41,14 @@ <argument index="0" name="shape" type="PhysicsShapeQueryParameters"> </argument> <description> - Checks the intersections of a shape, given through a [PhysicsShapeQueryParameters] object, against the space. If it collides with more than a shape, the nearest one is selected. The returned object is a dictionary containing the following fields: + Checks the intersections of a shape, given through a [PhysicsShapeQueryParameters] object, against the space. If it collides with more than one shape, the nearest one is selected. The returned object is a dictionary containing the following fields: [code]collider_id[/code]: The colliding object's ID. [code]linear_velocity[/code]: The colliding object's velocity [Vector3]. If the object is an [Area], the result is [code](0, 0, 0)[/code]. [code]normal[/code]: The object's surface normal at the intersection point. [code]point[/code]: The intersection point. [code]rid[/code]: The intersecting object's [RID]. [code]shape[/code]: The shape index of the colliding shape. - If the shape did not intersect anything, then an empty dictionary ([code]dir.empty()==true[/code]) is returned instead. + If the shape did not intersect anything, then an empty dictionary is returned instead. </description> </method> <method name="intersect_ray"> @@ -60,7 +60,11 @@ </argument> <argument index="2" name="exclude" type="Array" default="[ ]"> </argument> - <argument index="3" name="collision_layer" type="int" default="2147483647"> + <argument index="3" name="collision_mask" type="int" default="2147483647"> + </argument> + <argument index="4" name="collide_with_bodies" type="bool" default="true"> + </argument> + <argument index="5" name="collide_with_areas" type="bool" default="false"> </argument> <description> Intersects a ray in a given space. The returned object is a dictionary with the following fields: @@ -70,8 +74,8 @@ [code]position[/code]: The intersection point. [code]rid[/code]: The intersecting object's [RID]. [code]shape[/code]: The shape index of the colliding shape. - If the ray did not intersect anything, then an empty dictionary ([code]dir.empty()==true[/code]) is returned instead. - Additionally, the method can take an array of objects or [RID]s that are to be excluded from collisions, or a bitmask representing the physics layers to check in. + If the ray did not intersect anything, then an empty dictionary is returned instead. + Additionally, the method can take an [code]exclude[/code] array of objects or [RID]s that are to be excluded from collisions, a [code]collision_mask[/code] bitmask representing the physics layers to check in, or booleans to determine if the ray should collide with [PhysicsBody]s or [Area]s, respectively. </description> </method> <method name="intersect_shape"> @@ -87,7 +91,7 @@ [code]collider_id[/code]: The colliding object's ID. [code]rid[/code]: The intersecting object's [RID]. [code]shape[/code]: The shape index of the colliding shape. - The number of intersections can be limited with the second parameter, to reduce the processing time. + The number of intersections can be limited with the [code]max_results[/code] parameter, to reduce the processing time. </description> </method> </methods> diff --git a/doc/classes/PhysicsMaterial.xml b/doc/classes/PhysicsMaterial.xml index bfebb472a5..3eebcc57a1 100644 --- a/doc/classes/PhysicsMaterial.xml +++ b/doc/classes/PhysicsMaterial.xml @@ -1,8 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="PhysicsMaterial" inherits="Resource" category="Core" version="3.1"> <brief_description> + A material for physics properties. </brief_description> <description> + Provides a means of modifying the collision properties of a [PhysicsBody]. </description> <tutorials> </tutorials> @@ -14,8 +16,10 @@ <member name="absorbent" type="bool" setter="set_absorbent" getter="is_absorbent"> </member> <member name="bounce" type="float" setter="set_bounce" getter="get_bounce"> + The body's bounciness. Default value: [code]0[/code]. </member> <member name="friction" type="float" setter="set_friction" getter="get_friction"> + The body's friction. Values range from [code]0[/code] (frictionless) to [code]1[/code] (maximum friction). Default value: [code]1[/code]. </member> <member name="rough" type="bool" setter="set_rough" getter="is_rough"> </member> diff --git a/doc/classes/PhysicsServer.xml b/doc/classes/PhysicsServer.xml index 88a104cb11..c836414dd2 100644 --- a/doc/classes/PhysicsServer.xml +++ b/doc/classes/PhysicsServer.xml @@ -1260,7 +1260,7 @@ The higher, the stronger. </constant> <constant name="PIN_JOINT_IMPULSE_CLAMP" value="2" enum="PinJointParam"> - If above 0, this value is the maximum value for an impulse that this Joint puts on it's ends. + If above 0, this value is the maximum value for an impulse that this Joint puts on its ends. </constant> <constant name="HINGE_JOINT_BIAS" value="0" enum="HingeJointParam"> The speed with which the two bodies get pulled together when they move in different directions. @@ -1518,9 +1518,7 @@ <constant name="BODY_MODE_RIGID" value="2" enum="BodyMode"> Constant for rigid bodies. </constant> - <constant name="BODY_MODE_SOFT" value="3" enum="BodyMode"> - </constant> - <constant name="BODY_MODE_CHARACTER" value="4" enum="BodyMode"> + <constant name="BODY_MODE_CHARACTER" value="3" enum="BodyMode"> Constant for rigid bodies in character mode. In this mode, a body can not rotate, and only its linear velocity is affected by physics. </constant> <constant name="BODY_PARAM_BOUNCE" value="0" enum="BodyParameter"> diff --git a/doc/classes/PhysicsShapeQueryParameters.xml b/doc/classes/PhysicsShapeQueryParameters.xml index 2f36e81e27..7cca231ad2 100644 --- a/doc/classes/PhysicsShapeQueryParameters.xml +++ b/doc/classes/PhysicsShapeQueryParameters.xml @@ -19,6 +19,10 @@ </method> </methods> <members> + <member name="collide_with_areas" type="bool" setter="set_collide_with_areas" getter="is_collide_with_areas_enabled"> + </member> + <member name="collide_with_bodies" type="bool" setter="set_collide_with_bodies" getter="is_collide_with_bodies_enabled"> + </member> <member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask"> </member> <member name="exclude" type="Array" setter="set_exclude" getter="get_exclude"> diff --git a/doc/classes/Plane.xml b/doc/classes/Plane.xml index 62e4fc5d9d..a57659abda 100644 --- a/doc/classes/Plane.xml +++ b/doc/classes/Plane.xml @@ -7,7 +7,7 @@ Plane represents a normalized plane equation. Basically, "normal" is the normal of the plane (a,b,c normalized), and "d" is the distance from the origin to the plane (in the direction of "normal"). "Over" or "Above" the plane is considered the side of the plane towards where the normal is pointing. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/math/index.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/math/index.html</link> </tutorials> <demos> </demos> @@ -157,11 +157,11 @@ </member> </members> <constants> - <constant name="X" value="Plane( 1, 0, 0, 0 )"> + <constant name="PLANE_YZ" value="Plane( 1, 0, 0, 0 )"> </constant> - <constant name="Y" value="Plane( 0, 1, 0, 0 )"> + <constant name="PLANE_XZ" value="Plane( 0, 1, 0, 0 )"> </constant> - <constant name="Z" value="Plane( 0, 0, 1, 0 )"> + <constant name="PLANE_XY" value="Plane( 0, 0, 1, 0 )"> </constant> </constants> </class> diff --git a/doc/classes/PoolByteArray.xml b/doc/classes/PoolByteArray.xml index 120d80ba39..ae722b1053 100644 --- a/doc/classes/PoolByteArray.xml +++ b/doc/classes/PoolByteArray.xml @@ -81,7 +81,7 @@ </method> <method name="invert"> <description> - Reverse the order of the elements in the array (so first element will now be the last). + Reverse the order of the elements in the array. </description> </method> <method name="push_back"> @@ -129,7 +129,7 @@ <argument index="1" name="to" type="int"> </argument> <description> - Returns the slice of the [code]PoolByteArray[/code] between indices (inclusive) as a new [code]PoolByteArray[/code]. Any negative index is considered to be from the end of the array. + Returns the slice of the [code]PoolByteArray[/code] between indices (inclusive) as a new [code]PoolByteArray[/code]. Any negative index is considered to be from the end of the array. </description> </method> </methods> diff --git a/doc/classes/PoolColorArray.xml b/doc/classes/PoolColorArray.xml index 0c93a565f5..021d5f5089 100644 --- a/doc/classes/PoolColorArray.xml +++ b/doc/classes/PoolColorArray.xml @@ -47,7 +47,7 @@ </method> <method name="invert"> <description> - Reverse the order of the elements in the array (so first element will now be the last). + Reverse the order of the elements in the array. </description> </method> <method name="push_back"> diff --git a/doc/classes/PoolIntArray.xml b/doc/classes/PoolIntArray.xml index 43cb5d77de..347dcb09f2 100644 --- a/doc/classes/PoolIntArray.xml +++ b/doc/classes/PoolIntArray.xml @@ -47,7 +47,7 @@ </method> <method name="invert"> <description> - Reverse the order of the elements in the array (so first element will now be the last). + Reverse the order of the elements in the array. </description> </method> <method name="push_back"> diff --git a/doc/classes/PoolRealArray.xml b/doc/classes/PoolRealArray.xml index 0808b44104..c0c6ef8700 100644 --- a/doc/classes/PoolRealArray.xml +++ b/doc/classes/PoolRealArray.xml @@ -47,7 +47,7 @@ </method> <method name="invert"> <description> - Reverse the order of the elements in the array (so first element will now be the last). + Reverse the order of the elements in the array. </description> </method> <method name="push_back"> diff --git a/doc/classes/PoolStringArray.xml b/doc/classes/PoolStringArray.xml index 9f6c4306cb..8b3ac4c16a 100644 --- a/doc/classes/PoolStringArray.xml +++ b/doc/classes/PoolStringArray.xml @@ -47,7 +47,7 @@ </method> <method name="invert"> <description> - Reverse the order of the elements in the array (so first element will now be the last). + Reverse the order of the elements in the array. </description> </method> <method name="join"> diff --git a/doc/classes/PoolVector2Array.xml b/doc/classes/PoolVector2Array.xml index 072281158c..ecf8f5a6ba 100644 --- a/doc/classes/PoolVector2Array.xml +++ b/doc/classes/PoolVector2Array.xml @@ -47,7 +47,7 @@ </method> <method name="invert"> <description> - Reverse the order of the elements in the array (so first element will now be the last). + Reverse the order of the elements in the array. </description> </method> <method name="push_back"> diff --git a/doc/classes/PoolVector3Array.xml b/doc/classes/PoolVector3Array.xml index 7aa5dfc090..456b54d209 100644 --- a/doc/classes/PoolVector3Array.xml +++ b/doc/classes/PoolVector3Array.xml @@ -47,7 +47,7 @@ </method> <method name="invert"> <description> - Reverse the order of the elements in the array (so first element will now be the last). + Reverse the order of the elements in the array. </description> </method> <method name="push_back"> diff --git a/doc/classes/ProceduralSky.xml b/doc/classes/ProceduralSky.xml index df0519b2ad..090b626433 100644 --- a/doc/classes/ProceduralSky.xml +++ b/doc/classes/ProceduralSky.xml @@ -5,7 +5,7 @@ </brief_description> <description> ProceduralSky provides a way to create an effective background quickly by defining procedural parameters for the sun, the sky and the ground. The sky and ground are very similar, they are defined by a color at the horizon, another color, and finally an easing curve to interpolate between these two colors. Similarly the sun is described by a position in the sky, a color, and an easing curve. However, the sun also defines a minimum and maximum angle, these two values define at what distance the easing curve begins and ends from the sun, and thus end up defining the size of the sun in the sky. - The ProceduralSky is updated on the CPU after the parameters change and stored in a texture and then displayed as a background in the scene. This makes it relatively unsuitable for realtime updates during gameplay. But with a small texture size it is still feasible to update relatively frequently becuase it is updated on a background thread when multi-threading is available. + The ProceduralSky is updated on the CPU after the parameters change and stored in a texture and then displayed as a background in the scene. This makes it relatively unsuitable for realtime updates during gameplay. But with a small texture size it is still feasible to update relatively frequently because it is updated on a background thread when multi-threading is available. </description> <tutorials> </tutorials> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 358b7292a5..3b7356ab39 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -83,6 +83,8 @@ <argument index="0" name="pack" type="String"> </argument> <description> + Loads the contents of the .pck or .zip file specified by [code]pack[/code] into the resource filesystem (res://). Returns true on success. + Note: If a file from [code]pack[/code] shares the same path as a file already in the resource filesystem, any attempts to load that file will use the file from [code]pack[/code]. </description> </method> <method name="localize_path" qualifiers="const"> @@ -100,6 +102,7 @@ <argument index="0" name="name" type="String"> </argument> <description> + Returns true if the specified property exists and its initial value differs from the current value. </description> </method> <method name="property_get_revert"> @@ -108,12 +111,14 @@ <argument index="0" name="name" type="String"> </argument> <description> + Returns the initial value of the specified property. Returns null if the property does not exist. </description> </method> <method name="save"> <return type="int" enum="Error"> </return> <description> + Saves the configuration to the project.godot file. </description> </method> <method name="save_custom"> @@ -122,6 +127,7 @@ <argument index="0" name="file" type="String"> </argument> <description> + Saves the configuration to a custom file. </description> </method> <method name="set_initial_value"> @@ -157,6 +163,8 @@ </method> </methods> <members> + <member name="application/boot_splash/bg_color" type="Color" setter="" getter=""> + </member> <member name="application/boot_splash/fullsize" type="bool" setter="" getter=""> Scale the boot splash image to the full window length when engine starts (will leave it as default pixel size otherwise). </member> @@ -174,7 +182,7 @@ Name of the project. It is used from both project manager and by the exporters. Overriding this as name.locale allows setting it in multiple languages. </member> <member name="application/config/use_custom_user_dir" type="bool" setter="" getter=""> - Allow the project to save to it's own custom user dir (in AppData on windows or ~/.config on unixes). This setting only works for desktop exporters. A name must be set in the "custom_user_dir_name" setting for this to take effect. + Allow the project to save to its own custom user dir (in AppData on windows or ~/.config on unixes). This setting only works for desktop exporters. A name must be set in the "custom_user_dir_name" setting for this to take effect. </member> <member name="application/run/disable_stderr" type="bool" setter="" getter=""> Disable printing to stderr on exported build. @@ -208,7 +216,7 @@ <member name="audio/output_latency" type="int" setter="" getter=""> </member> <member name="audio/video_delay_compensation_ms" type="int" setter="" getter=""> - Setting to harcode audio delay when playing video. Best to leave this untouched unless you know what you are doing. + Setting to hardcode audio delay when playing video. Best to leave this untouched unless you know what you are doing. </member> <member name="compression/formats/gzip/compression_level" type="int" setter="" getter=""> Default compression level for gzip. Affects compressed scenes and resources. @@ -224,8 +232,12 @@ </member> <member name="compression/formats/zstd/window_log_size" type="int" setter="" getter=""> </member> + <member name="debug/gdscript/completion/autocomplete_setters_and_getters" type="bool" setter="" getter=""> + </member> <member name="debug/gdscript/warnings/constant_used_as_function" type="bool" setter="" getter=""> </member> + <member name="debug/gdscript/warnings/deprecated_keyword" type="bool" setter="" getter=""> + </member> <member name="debug/gdscript/warnings/enable" type="bool" setter="" getter=""> </member> <member name="debug/gdscript/warnings/function_conflicts_constant" type="bool" setter="" getter=""> @@ -364,7 +376,7 @@ <member name="gui/timers/incremental_search_max_interval_msec" type="int" setter="" getter=""> Timer setting for incremental search in Tree, IntemList, etc. controls. </member> - <member name="gui/timers/text_edit_idle_detect_sec" type="int" setter="" getter=""> + <member name="gui/timers/text_edit_idle_detect_sec" type="float" setter="" getter=""> Timer for detecting idle in the editor. </member> <member name="input/ui_accept" type="Dictionary" setter="" getter=""> @@ -576,6 +588,14 @@ <member name="memory/limits/multithreaded_server/rid_pool_prealloc" type="int" setter="" getter=""> This is used by servers when used in multi threading mode (servers and visual). RIDs are preallocated to avoid stalling the server requesting them on threads. If servers get stalled too often when loading resources in a thread, increase this number. </member> + <member name="mono/debugger_agent/port" type="int" setter="" getter=""> + </member> + <member name="mono/debugger_agent/wait_for_debugger" type="bool" setter="" getter=""> + </member> + <member name="mono/debugger_agent/wait_timeout" type="int" setter="" getter=""> + </member> + <member name="mono/export/include_scripts_content" type="bool" setter="" getter=""> + </member> <member name="network/limits/debugger_stdout/max_chars_per_second" type="int" setter="" getter=""> Maximum amount of characters allowed to send as output from the debugger. Over this value, content is dropped. This helps not to stall the debugger connection. </member> @@ -588,18 +608,12 @@ <member name="network/limits/packet_peer_stream/max_buffer_po2" type="int" setter="" getter=""> Default size of packet peer stream for deserializing godot data. Over this size, data is dropped. </member> - <member name="network/remote_fs/max_pages" type="int" setter="" getter=""> - Maximum amount of pages used for remote filesystem (used by debugging). - </member> <member name="network/remote_fs/page_read_ahead" type="int" setter="" getter=""> Amount of read ahead used by remote filesystem. Improves latency. </member> <member name="network/remote_fs/page_size" type="int" setter="" getter=""> Page size used by remote filesystem. </member> - <member name="network/ssl/certificates" type="String" setter="" getter=""> - If your game or application uses HTTPS, a certificates file is needed. It must be set here. - </member> <member name="node/name_casing" type="int" setter="" getter=""> When creating nodes names automatically, set the type of casing in this project. This is mostly an editor setting. </member> @@ -622,7 +636,7 @@ Fix to improve physics jitter, specially on monitors where refresh rate is different than physics FPS. </member> <member name="rendering/environment/default_clear_color" type="Color" setter="" getter=""> - Default background clear color. + Default background clear color. Overridable per [Viewport] using its [Environment]. See [member Environment.background_mode] and [member Environment.background_color] in particular. To change this default color programmatically, use [method VisualServer.set_default_clear_color]. </member> <member name="rendering/limits/buffers/blend_shape_max_buffer_size_kb" type="int" setter="" getter=""> Max buffer size for blend shapes. Any blend shape bigger than this will not work. @@ -639,7 +653,7 @@ <member name="rendering/limits/rendering/max_renderable_elements" type="int" setter="" getter=""> Max amount of elements renderable in a frame. If more than this are visible per frame, they will be dropped. Keep in mind elements refer to mesh surfaces and not mesh themselves. </member> - <member name="rendering/limits/time/time_rollover_secs" type="int" setter="" getter=""> + <member name="rendering/limits/time/time_rollover_secs" type="float" setter="" getter=""> Shaders have a time variable that constantly increases. At some point it needs to be rolled back to zero to avoid numerical errors on shader animations. This setting specifies when. </member> <member name="rendering/quality/2d/use_pixel_snap" type="bool" setter="" getter=""> @@ -656,6 +670,9 @@ </member> <member name="rendering/quality/directional_shadow/size.mobile" type="int" setter="" getter=""> </member> + <member name="rendering/quality/driver/driver_fallback" type="String" setter="" getter=""> + Whether to allow falling back to other graphics drivers if the preferred driver is not available. Best means use the best working driver (this is the default). Never means never fall back to another driver even if it does not work. This means the project will not run if the preferred driver does not function. + </member> <member name="rendering/quality/driver/driver_name" type="String" setter="" getter=""> </member> <member name="rendering/quality/filters/anisotropic_filter_level" type="int" setter="" getter=""> @@ -675,12 +692,20 @@ <member name="rendering/quality/reflections/high_quality_ggx.mobile" type="bool" setter="" getter=""> </member> <member name="rendering/quality/reflections/texture_array_reflections" type="bool" setter="" getter=""> - For reflection probes and panorama backgrounds (sky), use a texure array instead of mipmaps. This reduces jitter noise on reflections, but costs more performance and memory. + For reflection probes and panorama backgrounds (sky), use a texture array instead of mipmaps. This reduces jitter noise on reflections, but costs more performance and memory. </member> <member name="rendering/quality/reflections/texture_array_reflections.mobile" type="bool" setter="" getter=""> </member> + <member name="rendering/quality/shading/force_blinn_over_ggx" type="bool" setter="" getter=""> + </member> + <member name="rendering/quality/shading/force_blinn_over_ggx.mobile" type="bool" setter="" getter=""> + </member> + <member name="rendering/quality/shading/force_lambert_over_burley" type="bool" setter="" getter=""> + </member> + <member name="rendering/quality/shading/force_lambert_over_burley.mobile" type="bool" setter="" getter=""> + </member> <member name="rendering/quality/shading/force_vertex_shading" type="bool" setter="" getter=""> - Force vertex shading for all rendering. This can increase performance a lot, but also reduces quality inmensely. Can work to optimize on very low end mobile. + Force vertex shading for all rendering. This can increase performance a lot, but also reduces quality immensely. Can work to optimize on very low end mobile. </member> <member name="rendering/quality/shading/force_vertex_shading.mobile" type="bool" setter="" getter=""> </member> @@ -723,6 +748,8 @@ <member name="rendering/threads/thread_model" type="int" setter="" getter=""> Thread model for rendering. Rendering on a thread can vastly improve performance, but syncinc to the main thread can cause a bit more jitter. </member> + <member name="rendering/vram_compression/import_bptc" type="bool" setter="" getter=""> + </member> <member name="rendering/vram_compression/import_etc" type="bool" setter="" getter=""> If the project uses this compression (usually low end mobile), texture importer will import these. </member> diff --git a/doc/classes/Quat.xml b/doc/classes/Quat.xml index 67631bbc92..468839dfba 100644 --- a/doc/classes/Quat.xml +++ b/doc/classes/Quat.xml @@ -9,8 +9,8 @@ Quaternions need to be (re)normalized. </description> <tutorials> - <link>http://docs.godotengine.org/en/latest/tutorials/3d/using_transforms.html#interpolating-with-quaternions</link> - <link>http://docs.godotengine.org/en/latest/tutorials/math/rotations.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/3d/using_transforms.html#interpolating-with-quaternions</link> + <link>https://docs.godotengine.org/en/latest/tutorials/math/rotations.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/RID.xml b/doc/classes/RID.xml index ee34560afd..a289b68c9a 100644 --- a/doc/classes/RID.xml +++ b/doc/classes/RID.xml @@ -4,7 +4,7 @@ Handle for a [Resource]'s unique ID. </brief_description> <description> - The RID type is used to access the unique integer ID of a resource. They are opaque, so they do not grant access to the associated resource by themselves. They are used by and with the low-level Server classes such as [VisualServer]. + The RID type is used to access the unique integer ID of a resource. They are opaque, so they do not grant access to the associated resource by themselves. They are used by and with the low-level Server classes such as [VisualServer]. </description> <tutorials> </tutorials> @@ -17,14 +17,14 @@ <argument index="0" name="from" type="Object"> </argument> <description> - Create a new RID instance with the ID of a given resource. When not handed a valid resource, silently stores the unused ID 0. + Creates a new RID instance with the ID of a given resource. When not handed a valid resource, silently stores the unused ID 0. </description> </method> <method name="get_id"> <return type="int"> </return> <description> - Retrieve the ID of the referenced resource. + Returns the ID of the referenced resource. </description> </method> </methods> diff --git a/doc/classes/RayCast.xml b/doc/classes/RayCast.xml index dce73d9c29..84c83d1282 100644 --- a/doc/classes/RayCast.xml +++ b/doc/classes/RayCast.xml @@ -6,6 +6,7 @@ <description> A RayCast represents a line from its origin to its destination position, [code]cast_to[/code]. It is used to query the 3D space in order to find the closest object along the path of the ray. RayCast can ignore some objects by adding them to the exception list via [code]add_exception[/code], by setting proper filtering with collision layers, or by filtering object types with type masks. + RayCast can be configured to report collisions with [Area]s ([member collide_with_areas]) and/or [PhysicsBody]s ([member collide_with_bodies]). Only enabled raycasts will be able to query the space and report collisions. RayCast calculates intersection every physics frame (see [Node]), and the result is cached so it can be used later until the next frame. If multiple queries are required between physics frames (or during the same frame) use [method force_raycast_update] after adjusting the raycast. </description> @@ -51,24 +52,14 @@ <return type="Object"> </return> <description> - Return the closest object the ray is pointing to. Note that this does not consider the length of the ray, so you must also use [method is_colliding] to check if the object returned is actually colliding with the ray. - Example: - [codeblock] - if RayCast.is_colliding(): - var collider = RayCast.get_collider() - [/codeblock] + Return the first object that the ray intersects, or [code]null[/code] if no object is intersecting the ray (i.e. [method is_colliding] returns [code]false[/code]). </description> </method> <method name="get_collider_shape" qualifiers="const"> <return type="int"> </return> <description> - Returns the collision shape of the closest object the ray is pointing to. Note that this does not consider the length of the ray, so you must also use [method is_colliding] to check if the object returned is actually colliding with the ray. - Example: - [codeblock] - if RayCast.is_colliding(): - var shape = RayCast.get_collider_shape() - [/codeblock] + Returns the shape ID of the first object that the ray intersects, or [code]0[/code] if no object is intersecting the ray (i.e. [method is_colliding] returns [code]false[/code]). </description> </method> <method name="get_collision_mask_bit" qualifiers="const"> @@ -98,7 +89,7 @@ <return type="bool"> </return> <description> - Return whether the closest object the ray is pointing to is colliding with the vector (considering the vector length). + Return whether any object is intersecting with the ray's vector (considering the vector length). </description> </method> <method name="remove_exception"> @@ -135,6 +126,12 @@ <member name="cast_to" type="Vector3" setter="set_cast_to" getter="get_cast_to"> The ray's destination point, relative to the RayCast's [code]position[/code]. </member> + <member name="collide_with_areas" type="bool" setter="set_collide_with_areas" getter="is_collide_with_areas_enabled"> + If [code]true[/code], collision with [Area]s will be reported. Default value: [code]false[/code]. + </member> + <member name="collide_with_bodies" type="bool" setter="set_collide_with_bodies" getter="is_collide_with_bodies_enabled"> + If [code]true[/code], collision with [PhysicsBody]s will be reported. Default value: [code]true[/code]. + </member> <member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask"> The ray's collision mask. Only objects in at least one collision layer enabled in the mask will be detected. </member> diff --git a/doc/classes/RayCast2D.xml b/doc/classes/RayCast2D.xml index 79e733bcdc..afb80f2f6e 100644 --- a/doc/classes/RayCast2D.xml +++ b/doc/classes/RayCast2D.xml @@ -6,6 +6,7 @@ <description> A RayCast represents a line from its origin to its destination position, [code]cast_to[/code]. It is used to query the 2D space in order to find the closest object along the path of the ray. RayCast2D can ignore some objects by adding them to the exception list via [code]add_exception[/code], by setting proper filtering with collision layers, or by filtering object types with type masks. + RayCast2D can be configured to report collisions with [Area2D]s ([member collide_with_areas]) and/or [PhysicsBody2D]s ([member collide_with_bodies]). Only enabled raycasts will be able to query the space and report collisions. RayCast2D calculates intersection every physics frame (see [Node]), and the result is cached so it can be used later until the next frame. If multiple queries are required between physics frames (or during the same frame) use [method force_raycast_update] after adjusting the raycast. </description> @@ -43,31 +44,21 @@ <return type="void"> </return> <description> - Updates the collision information for the ray. Use this method to update the collision information immediately instead of waiting for the next [code]_physics_process[/code] call, for example if the ray or its parent has changed state. Note: [code]enabled == true[/code] is not required for this to work. + Updates the collision information for the ray. Use this method to update the collision information immediately instead of waiting for the next [code]_physics_process[/code] call, for example if the ray or its parent has changed state. Note: [code]enabled == true[/code] is not required for this to work. </description> </method> <method name="get_collider" qualifiers="const"> <return type="Object"> </return> <description> - Returns the closest object the ray is pointing to. Note that this does not consider the length of the ray, so you must also use [method is_colliding] to check if the object returned is actually colliding with the ray. - Example: - [codeblock] - if RayCast2D.is_colliding(): - var collider = RayCast2D.get_collider() - [/codeblock] + Return the first object that the ray intersects, or [code]null[/code] if no object is intersecting the ray (i.e. [method is_colliding] returns [code]false[/code]). </description> </method> <method name="get_collider_shape" qualifiers="const"> <return type="int"> </return> <description> - Returns the collision shape of the closest object the ray is pointing to. Note that this does not consider the length of the ray, so you must also use [method is_colliding] to check if the object returned is actually colliding with the ray. - Example: - [codeblock] - if RayCast2D.is_colliding(): - var shape = RayCast2D.get_collider_shape() - [/codeblock] + Returns the shape ID of the first object that the ray intersects, or [code]0[/code] if no object is intersecting the ray (i.e. [method is_colliding] returns [code]false[/code]). </description> </method> <method name="get_collision_mask_bit" qualifiers="const"> @@ -97,7 +88,7 @@ <return type="bool"> </return> <description> - Return whether the closest object the ray is pointing to is colliding with the vector (considering the vector length). + Return whether any object is intersecting with the ray's vector (considering the vector length). </description> </method> <method name="remove_exception"> @@ -134,6 +125,12 @@ <member name="cast_to" type="Vector2" setter="set_cast_to" getter="get_cast_to"> The ray's destination point, relative to the RayCast's [code]position[/code]. </member> + <member name="collide_with_areas" type="bool" setter="set_collide_with_areas" getter="is_collide_with_areas_enabled"> + If [code]true[/code], collision with [Area2D]s will be reported. Default value: [code]false[/code]. + </member> + <member name="collide_with_bodies" type="bool" setter="set_collide_with_bodies" getter="is_collide_with_bodies_enabled"> + If [code]true[/code], collision with [PhysicsBody2D]s will be reported. Default value: [code]true[/code]. + </member> <member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask"> The ray's collision mask. Only objects in at least one collision layer enabled in the mask will be detected. </member> diff --git a/doc/classes/Rect2.xml b/doc/classes/Rect2.xml index 1eea940da9..1392e53f8d 100644 --- a/doc/classes/Rect2.xml +++ b/doc/classes/Rect2.xml @@ -7,7 +7,7 @@ Rect2 consists of a position, a size, and several utility functions. It is typically used for fast overlap tests. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/math/index.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/math/index.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/ReflectionProbe.xml b/doc/classes/ReflectionProbe.xml index a9a897ebaf..7662f72eca 100644 --- a/doc/classes/ReflectionProbe.xml +++ b/doc/classes/ReflectionProbe.xml @@ -5,7 +5,7 @@ <description> </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/3d/reflection_probes.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/3d/reflection_probes.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/RemoteTransform.xml b/doc/classes/RemoteTransform.xml index a7deb273b6..1a5d1eb907 100644 --- a/doc/classes/RemoteTransform.xml +++ b/doc/classes/RemoteTransform.xml @@ -1,11 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="RemoteTransform" inherits="Spatial" category="Core" version="3.1"> <brief_description> - RemoteTransform leads the [Transform] of another [Spatial] derived Node in the scene. + RemoteTransform pushes its own [Transform] to another [Spatial] derived Node in the scene. </brief_description> <description> - RemoteTransform leads the [Transform] of another [Spatial] derived Node (called the remote node) in the scene. - It can be set to track another Node's position, rotation and/or scale. It can update using either global or local coordinates. + RemoteTransform pushes its own [Transform] to another [Spatial] derived Node (called the remote node) in the scene. + It can be set to update another Node's position, rotation and/or scale. It can use either global or local coordinates. </description> <tutorials> </tutorials> @@ -18,13 +18,13 @@ The [NodePath] to the remote node, relative to the RemoteTransform's position in the scene. </member> <member name="update_position" type="bool" setter="set_update_position" getter="get_update_position"> - If [code]true[/code] the remote node's position is tracked. Default value: [code]true[/code]. + If [code]true[/code] the remote node's position is updated. Default value: [code]true[/code]. </member> <member name="update_rotation" type="bool" setter="set_update_rotation" getter="get_update_rotation"> - If [code]true[/code] the remote node's rotation is tracked. Default value: [code]true[/code]. + If [code]true[/code] the remote node's rotation is updated. Default value: [code]true[/code]. </member> <member name="update_scale" type="bool" setter="set_update_scale" getter="get_update_scale"> - If [code]true[/code] the remote node's scale is tracked. Default value: [code]true[/code]. + If [code]true[/code] the remote node's scale is updated. Default value: [code]true[/code]. </member> <member name="use_global_coordinates" type="bool" setter="set_use_global_coordinates" getter="get_use_global_coordinates"> If [code]true[/code] global coordinates are used. If [code]false[/code] local coordinates are used. Default value: [code]true[/code]. diff --git a/doc/classes/RemoteTransform2D.xml b/doc/classes/RemoteTransform2D.xml index bc562dcdad..d83ec9f6b1 100644 --- a/doc/classes/RemoteTransform2D.xml +++ b/doc/classes/RemoteTransform2D.xml @@ -1,11 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="RemoteTransform2D" inherits="Node2D" category="Core" version="3.1"> <brief_description> - RemoteTransform2D leads the [Transform2D] of another [CanvasItem] derived Node in the scene. + RemoteTransform2D pushes its own [Transform2D] to another [CanvasItem] derived Node in the scene. </brief_description> <description> - RemoteTransform2D leads the [Transform2D] of another [CanvasItem] derived Node (called the remote node) in the scene. - It can be set to track another Node's position, rotation and/or scale. It can update using either global or local coordinates. + RemoteTransform2D pushes its own [Transform2D] to another [CanvasItem] derived Node (called the remote node) in the scene. + It can be set to update another Node's position, rotation and/or scale. It can use either global or local coordinates. </description> <tutorials> </tutorials> @@ -18,13 +18,13 @@ The [NodePath] to the remote node, relative to the RemoteTransform2D's position in the scene. </member> <member name="update_position" type="bool" setter="set_update_position" getter="get_update_position"> - If [code]true[/code] the remote node's position is tracked. Default value: [code]true[/code]. + If [code]true[/code] the remote node's position is updated. Default value: [code]true[/code]. </member> <member name="update_rotation" type="bool" setter="set_update_rotation" getter="get_update_rotation"> - If [code]true[/code] the remote node's rotation is tracked. Default value: [code]true[/code]. + If [code]true[/code] the remote node's rotation is updated. Default value: [code]true[/code]. </member> <member name="update_scale" type="bool" setter="set_update_scale" getter="get_update_scale"> - If [code]true[/code] the remote node's scale is tracked. Default value: [code]true[/code]. + If [code]true[/code] the remote node's scale is updated. Default value: [code]true[/code]. </member> <member name="use_global_coordinates" type="bool" setter="set_use_global_coordinates" getter="get_use_global_coordinates"> If [code]true[/code] global coordinates are used. If [code]false[/code] local coordinates are used. Default value: [code]true[/code]. diff --git a/doc/classes/ResourceSaver.xml b/doc/classes/ResourceSaver.xml index ae0d8e909d..f388667891 100644 --- a/doc/classes/ResourceSaver.xml +++ b/doc/classes/ResourceSaver.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="ResourceSaver" inherits="Object" category="Core" version="3.1"> <brief_description> - Resource Saving Interface. + Resource saving interface. </brief_description> <description> - Resource Saving Interface. This interface is used for saving resources to disk. + Resource saving interface, used for saving resources to disk. </description> <tutorials> </tutorials> @@ -17,7 +17,7 @@ <argument index="0" name="type" type="Resource"> </argument> <description> - Return the list of extensions available for saving a resource of a given type. + Returns the list of extensions available for saving a resource of a given type. </description> </method> <method name="save"> @@ -30,7 +30,7 @@ <argument index="2" name="flags" type="int" default="0"> </argument> <description> - Save a resource to disk, to a given path. + Saves a resource to disk. </description> </method> </methods> @@ -47,5 +47,7 @@ </constant> <constant name="FLAG_COMPRESS" value="32" enum="SaverFlags"> </constant> + <constant name="FLAG_REPLACE_SUBRESOURCE_PATHS" value="64" enum="SaverFlags"> + </constant> </constants> </class> diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml index 618f2f42b2..145ce7537b 100644 --- a/doc/classes/RichTextLabel.xml +++ b/doc/classes/RichTextLabel.xml @@ -8,7 +8,7 @@ Note that assignments to [member bbcode_text] clear the tag stack and reconstruct it from the property's contents. Any edits made to [member bbcode_text] will erase previous edits made from other manual sources such as [method append_bbcode] and the [code]push_*[/code] / [method pop] methods. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/gui/bbcode_in_richtextlabel.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/gui/bbcode_in_richtextlabel.html</link> </tutorials> <demos> </demos> @@ -111,7 +111,7 @@ <argument index="0" name="align" type="int" enum="RichTextLabel.Align"> </argument> <description> - Adds a [code][right][/code] tag to the tag stack. + Adds an alignment tag based on the given [code]align[/code] value. See [enum Align] for possible values. </description> </method> <method name="push_cell"> @@ -166,6 +166,13 @@ Adds a meta tag to the tag stack. Similar to the bbcode [code][url=something]{text}[/url][/code], but supports non-[String] metadata types. </description> </method> + <method name="push_strikethrough"> + <return type="void"> + </return> + <description> + Adds a [code][s][/code] tag to the tag stack. + </description> + </method> <method name="push_table"> <return type="void"> </return> @@ -304,15 +311,17 @@ </constant> <constant name="ITEM_UNDERLINE" value="6" enum="ItemType"> </constant> - <constant name="ITEM_ALIGN" value="7" enum="ItemType"> + <constant name="ITEM_STRIKETHROUGH" value="7" enum="ItemType"> + </constant> + <constant name="ITEM_ALIGN" value="8" enum="ItemType"> </constant> - <constant name="ITEM_INDENT" value="8" enum="ItemType"> + <constant name="ITEM_INDENT" value="9" enum="ItemType"> </constant> - <constant name="ITEM_LIST" value="9" enum="ItemType"> + <constant name="ITEM_LIST" value="10" enum="ItemType"> </constant> - <constant name="ITEM_TABLE" value="10" enum="ItemType"> + <constant name="ITEM_TABLE" value="11" enum="ItemType"> </constant> - <constant name="ITEM_META" value="11" enum="ItemType"> + <constant name="ITEM_META" value="12" enum="ItemType"> </constant> </constants> <theme_items> diff --git a/doc/classes/RigidBody.xml b/doc/classes/RigidBody.xml index 038464e127..4ff332402b 100644 --- a/doc/classes/RigidBody.xml +++ b/doc/classes/RigidBody.xml @@ -10,7 +10,7 @@ If you need to override the default physics behavior, you can write a custom force integration. See [member custom_integrator]. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html</link> </tutorials> <demos> </demos> @@ -30,6 +30,8 @@ <argument index="0" name="force" type="Vector3"> </argument> <description> + Adds a constant directional force without affecting rotation. + This is equivalent to [code]add_force(force, Vector3(0,0,0))[/code]. </description> </method> <method name="add_force"> @@ -40,6 +42,7 @@ <argument index="1" name="position" type="Vector3"> </argument> <description> + Adds a constant force (i.e. acceleration). </description> </method> <method name="add_torque"> @@ -48,6 +51,7 @@ <argument index="0" name="torque" type="Vector3"> </argument> <description> + Adds a constant rotational force (i.e. a motor) without affecting position. </description> </method> <method name="apply_central_impulse"> @@ -56,6 +60,8 @@ <argument index="0" name="impulse" type="Vector3"> </argument> <description> + Applies a directional impulse without affecting rotation. + This is equivalent to [code]apply_impulse(Vector3(0,0,0), impulse)[/code]. </description> </method> <method name="apply_impulse"> @@ -66,7 +72,7 @@ <argument index="1" name="impulse" type="Vector3"> </argument> <description> - Apply a positioned impulse (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 once, and only once. Both the impulse and the position are in global coordinates, and the position is relative to the object's origin. + Applies a positioned impulse 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 once, and only once. Both the impulse and the position are in global coordinates, and the position is relative to the object's origin. </description> </method> <method name="apply_torque_impulse"> @@ -75,7 +81,7 @@ <argument index="0" name="impulse" type="Vector3"> </argument> <description> - Apply a torque impulse (which will be affected by the body mass and shape). This will rotate the body around the passed in vector. + Applies a torque impulse which will be affected by the body mass and shape. This will rotate the body around the passed in vector. </description> </method> <method name="get_colliding_bodies" qualifiers="const"> @@ -91,7 +97,7 @@ <argument index="0" name="axis_velocity" type="Vector3"> </argument> <description> - Set an axis velocity. The velocity in the given vector axis will be set as the given vector length. This is useful for jumping behavior. + Sets an axis velocity. The velocity in the given vector axis will be set as the given vector length. This is useful for jumping behavior. </description> </method> </methods> @@ -103,16 +109,22 @@ RigidBody's rotational velocity. </member> <member name="axis_lock_angular_x" type="bool" setter="set_axis_lock" getter="get_axis_lock"> + Lock the body's rotation in the x-axis. </member> <member name="axis_lock_angular_y" type="bool" setter="set_axis_lock" getter="get_axis_lock"> + Lock the body's rotation in the y-axis. </member> <member name="axis_lock_angular_z" type="bool" setter="set_axis_lock" getter="get_axis_lock"> + Lock the body's rotation in the z-axis. </member> <member name="axis_lock_linear_x" type="bool" setter="set_axis_lock" getter="get_axis_lock"> + Lock the body's movement in the x-axis. </member> <member name="axis_lock_linear_y" type="bool" setter="set_axis_lock" getter="get_axis_lock"> + Lock the body's movement in the x-axis. </member> <member name="axis_lock_linear_z" type="bool" setter="set_axis_lock" getter="get_axis_lock"> + Lock the body's movement in the x-axis. </member> <member name="bounce" type="float" setter="set_bounce" getter="get_bounce"> RigidBody's bounciness. @@ -121,7 +133,7 @@ If [code]true[/code] the RigidBody will not calculate forces and will act as a static body while there is no movement. It will wake up when forces are applied through other collisions or when the [code]apply_impulse[/code] method is used. </member> <member name="contact_monitor" type="bool" setter="set_contact_monitor" getter="is_contact_monitor_enabled"> - If true, the RigidBody will emit signals when it collides with another RigidBody. + If [code]true[/code] the RigidBody will emit signals when it collides with another RigidBody. </member> <member name="contacts_reported" type="int" setter="set_max_contacts_reported" getter="get_max_contacts_reported"> The maximum contacts to report. Bodies can keep a log of the contacts with other bodies, this is enabled by setting the maximum amount of contacts reported to a number greater than 0. @@ -134,19 +146,19 @@ If [code]true[/code] internal force integration will be disabled (like gravity or air friction) for this body. Other than collision response, the body will only move as determined by the [method _integrate_forces] function, if defined. </member> <member name="friction" type="float" setter="set_friction" getter="get_friction"> - The body friction, from 0 (frictionless) to 1 (max friction). + The body's friction, from 0 (frictionless) to 1 (max friction). </member> <member name="gravity_scale" type="float" setter="set_gravity_scale" getter="get_gravity_scale"> This is multiplied by the global 3D gravity setting found in "Project > Project Settings > Physics > 3d" to produce RigidBody's gravity. E.g. a value of 1 will be normal gravity, 2 will apply double gravity, and 0.5 will apply half gravity to this object. </member> <member name="linear_damp" type="float" setter="set_linear_damp" getter="get_linear_damp"> - RigidBody's linear damp. Default value: -1, cannot be less than -1. If this value is different from -1, any linear damp derived from the world or areas will be overridden. + The body's linear damp. Default value: -1, cannot be less than -1. If this value is different from -1, any linear damp derived from the world or areas will be overridden. </member> <member name="linear_velocity" type="Vector3" setter="set_linear_velocity" getter="get_linear_velocity"> - RigidBody's linear velocity. Can be used sporadically, but [b]DON'T SET THIS IN EVERY FRAME[/b], because physics may run in another thread and runs at a different granularity. Use [method _integrate_forces] as your process loop for precise control of the body state. + The body's linear velocity. Can be used sporadically, but [b]DON'T SET THIS IN EVERY FRAME[/b], because physics may run in another thread and runs at a different granularity. Use [method _integrate_forces] as your process loop for precise control of the body state. </member> <member name="mass" type="float" setter="set_mass" getter="get_mass"> - RigidBody's mass. + The body's mass. </member> <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. @@ -154,22 +166,22 @@ <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. + If [code]true[/code] the body is sleeping and will not calculate forces until woken up by a collision or the [code]apply_impulse[/code] method. </member> <member name="weight" type="float" setter="set_weight" getter="get_weight"> - RigidBody's weight based on its mass and the global 3D gravity. Global values are set in "Project > Project Settings > Physics > 3d". + The body's weight based on its mass and the global 3D gravity. Global values are set in "Project > Project Settings > Physics > 3d". </member> </members> <signals> <signal name="body_entered"> - <argument index="0" name="body" type="Object"> + <argument index="0" name="body" type="Node"> </argument> <description> Emitted when a body enters into contact with this one. Contact monitor and contacts reported must be enabled for this to work. </description> </signal> <signal name="body_exited"> - <argument index="0" name="body" type="Object"> + <argument index="0" name="body" type="Node"> </argument> <description> Emitted when a body shape exits contact with this one. Contact monitor and contacts reported must be enabled for this to work. @@ -178,7 +190,7 @@ <signal name="body_shape_entered"> <argument index="0" name="body_id" type="int"> </argument> - <argument index="1" name="body" type="Object"> + <argument index="1" name="body" type="Node"> </argument> <argument index="2" name="body_shape" type="int"> </argument> @@ -192,7 +204,7 @@ <signal name="body_shape_exited"> <argument index="0" name="body_id" type="int"> </argument> - <argument index="1" name="body" type="Object"> + <argument index="1" name="body" type="Node"> </argument> <argument index="2" name="body_shape" type="int"> </argument> @@ -211,16 +223,16 @@ </signals> <constants> <constant name="MODE_RIGID" value="0" enum="Mode"> - Rigid body. This is the "natural" state of a rigid body. It is affected by forces, and can move, rotate, and be affected by user code. + Rigid body mode. This is the "natural" state of a rigid body. It is affected by forces, and can move, rotate, and be affected by user code. </constant> <constant name="MODE_STATIC" value="1" enum="Mode"> Static mode. The body behaves like a [StaticBody], and can only move by user code. </constant> <constant name="MODE_CHARACTER" value="2" enum="Mode"> - Character body. This behaves like a rigid body, but can not rotate. + Character body mode. This behaves like a rigid body, but can not rotate. </constant> <constant name="MODE_KINEMATIC" value="3" enum="Mode"> - Kinematic body. The body behaves like a [KinematicBody], and can only move by user code. + Kinematic body mode. The body behaves like a [KinematicBody], and can only move by user code. </constant> </constants> </class> diff --git a/doc/classes/RigidBody2D.xml b/doc/classes/RigidBody2D.xml index 2265c777c8..079440ab8b 100644 --- a/doc/classes/RigidBody2D.xml +++ b/doc/classes/RigidBody2D.xml @@ -29,6 +29,7 @@ <argument index="0" name="force" type="Vector2"> </argument> <description> + Adds a constant directional force without affecting rotation. </description> </method> <method name="add_force"> @@ -48,6 +49,7 @@ <argument index="0" name="torque" type="float"> </argument> <description> + Adds a constant rotational force. </description> </method> <method name="apply_central_impulse"> @@ -56,6 +58,7 @@ <argument index="0" name="impulse" type="Vector2"> </argument> <description> + Applies a directional impulse without affecting rotation. </description> </method> <method name="apply_impulse"> @@ -75,6 +78,7 @@ <argument index="0" name="torque" type="float"> </argument> <description> + Applies a rotational impulse to the body. </description> </method> <method name="get_colliding_bodies" qualifiers="const"> @@ -173,14 +177,14 @@ </members> <signals> <signal name="body_entered"> - <argument index="0" name="body" type="Object"> + <argument index="0" name="body" type="Node"> </argument> <description> Emitted when a body enters into contact with this one. [member contact_monitor] must be [code]true[/code] and [member contacts_reported] greater than [code]0[/code]. </description> </signal> <signal name="body_exited"> - <argument index="0" name="body" type="Object"> + <argument index="0" name="body" type="Node"> </argument> <description> Emitted when a body exits contact with this one. [member contact_monitor] must be [code]true[/code] and [member contacts_reported] greater than [code]0[/code]. @@ -189,7 +193,7 @@ <signal name="body_shape_entered"> <argument index="0" name="body_id" type="int"> </argument> - <argument index="1" name="body" type="Object"> + <argument index="1" name="body" type="Node"> </argument> <argument index="2" name="body_shape" type="int"> </argument> @@ -202,7 +206,7 @@ <signal name="body_shape_exited"> <argument index="0" name="body_id" type="int"> </argument> - <argument index="1" name="body" type="Object"> + <argument index="1" name="body" type="Node"> </argument> <argument index="2" name="body_shape" type="int"> </argument> diff --git a/doc/classes/SceneState.xml b/doc/classes/SceneState.xml index 36cddf08df..bd2f883cae 100644 --- a/doc/classes/SceneState.xml +++ b/doc/classes/SceneState.xml @@ -4,7 +4,7 @@ A script interface to a scene file's data. </brief_description> <description> - Maintains a list of resources, nodes, exported and overridden properties, and built-in scripts associated with a scene. + Maintains a list of resources, nodes, exported, and overridden properties, and built-in scripts associated with a scene. </description> <tutorials> </tutorials> diff --git a/doc/classes/SceneTree.xml b/doc/classes/SceneTree.xml index c85bee9b84..dd94ee66d2 100644 --- a/doc/classes/SceneTree.xml +++ b/doc/classes/SceneTree.xml @@ -7,8 +7,8 @@ As one of the most important classes, the [code]SceneTree[/code] manages the hierarchy of nodes in a scene as well as scenes themselves. Nodes can be added, retrieved and removed. The whole scene tree (and thus the current scene) can be paused. Scenes can be loaded, switched and reloaded. You can also use the SceneTree to organize your nodes into groups: every node can be assigned as many groups as you want to create, e.g. a "enemy" group. You can then iterate these groups or even call methods and set properties on all the group's members at once. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/getting_started/step_by_step/scene_tree.html</link> - <link>http://docs.godotengine.org/en/3.0/tutorials/viewports/multiple_resolutions.html</link> + <link>https://docs.godotengine.org/en/latest/getting_started/step_by_step/scene_tree.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/viewports/multiple_resolutions.html</link> </tutorials> <demos> </demos> @@ -271,13 +271,17 @@ </member> <member name="multiplayer_poll" type="bool" setter="set_multiplayer_poll_enabled" getter="is_multiplayer_poll_enabled"> If [code]true[/code] (default) enable the automatic polling of the [MultiplayerAPI] for this SceneTree during [signal idle_frame]. - When [code]false[/code] you need to manually call [method MultiplayerAPI.poll] for processing network packets and delivering RPCs/RSETs. This allows to run RPCs/RSETs in a different loop (e.g. physics, thread, specific time step) and for manual [Mutex] protecion when accessing the [MultiplayerAPI] from threads. + When [code]false[/code] you need to manually call [method MultiplayerAPI.poll] for processing network packets and delivering RPCs/RSETs. This allows to run RPCs/RSETs in a different loop (e.g. physics, thread, specific time step) and for manual [Mutex] protection when accessing the [MultiplayerAPI] from threads. </member> <member name="network_peer" type="NetworkedMultiplayerPeer" setter="set_network_peer" getter="get_network_peer"> - The peer object to handle the RPC system (effectively enabling networking when set). Depending on the peer itself, the SceneTree will become a network server (check with [method is_network_server()]) and will set root node's network mode to master (see NETWORK_MODE_* constants in [Node]), or it will become a regular peer with root node set to slave. All child nodes are set to inherit the network mode by default. Handling of networking-related events (connection, disconnection, new clients) is done by connecting to SceneTree's signals. + The peer object to handle the RPC system (effectively enabling networking when set). Depending on the peer itself, the SceneTree will become a network server (check with [method is_network_server()]) and will set root node's network mode to master (see NETWORK_MODE_* constants in [Node]), or it will become a regular peer with root node set to puppet. All child nodes are set to inherit the network mode by default. Handling of networking-related events (connection, disconnection, new clients) is done by connecting to SceneTree's signals. </member> <member name="paused" type="bool" setter="set_pause" getter="is_paused"> If [code]true[/code] the SceneTree is paused. + Doing so will have the following behavior: + * 2D and 3D physics will be stopped. + * _process and _physics_process will not be called anymore in nodes. + * _input and _input_event will not be called anymore either. </member> <member name="refuse_new_network_connections" type="bool" setter="set_refuse_new_network_connections" getter="is_refusing_new_network_connections"> If [code]true[/code] the SceneTree's [member network_peer] refuses new incoming connections. @@ -329,21 +333,21 @@ </description> </signal> <signal name="node_added"> - <argument index="0" name="node" type="Object"> + <argument index="0" name="node" type="Node"> </argument> <description> Emitted whenever a node is added to the SceneTree. </description> </signal> <signal name="node_configuration_warning_changed"> - <argument index="0" name="node" type="Object"> + <argument index="0" name="node" type="Node"> </argument> <description> Emitted when a node's configuration changed. Only emitted in tool mode. </description> </signal> <signal name="node_removed"> - <argument index="0" name="node" type="Object"> + <argument index="0" name="node" type="Node"> </argument> <description> Emitted whenever a node is removed from the SceneTree. diff --git a/doc/classes/Script.xml b/doc/classes/Script.xml index 09c60afc2f..ec1d544c03 100644 --- a/doc/classes/Script.xml +++ b/doc/classes/Script.xml @@ -4,11 +4,11 @@ A class stored as a resource. </brief_description> <description> - A class stored as a resource. The script exends the functionality of all objects that instance it. - The 'new' method of a script subclass creates a new instance. [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes. + A class stored as a resource. A script exends the functionality of all objects that instance it. + The [code]new[/code] method of a script subclass creates a new instance. [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/getting_started/step_by_step/scripting.html</link> + <link>https://docs.godotengine.org/en/latest/getting_started/step_by_step/scripting.html</link> </tutorials> <demos> </demos> @@ -17,7 +17,7 @@ <return type="bool"> </return> <description> - Returns true if the script can be instanced. + Returns [code]true[/code] if the script can be instanced. </description> </method> <method name="get_base_script" qualifiers="const"> @@ -31,6 +31,7 @@ <return type="String"> </return> <description> + Returns the script's base type. </description> </method> <method name="has_script_signal" qualifiers="const"> @@ -39,14 +40,14 @@ <argument index="0" name="signal_name" type="String"> </argument> <description> - Returns true if the script, or a base class, defines a signal with the given name. + Returns [code]true[/code] if the script, or a base class, defines a signal with the given name. </description> </method> <method name="has_source_code" qualifiers="const"> <return type="bool"> </return> <description> - Returns true if the script contains non-empty source code. + Returns [code]true[/code] if the script contains non-empty source code. </description> </method> <method name="instance_has" qualifiers="const"> @@ -55,14 +56,14 @@ <argument index="0" name="base_object" type="Object"> </argument> <description> - Returns true if 'base_object' is an instance of this script. + Returns [code]true[/code] if [code]base_object[/code] is an instance of this script. </description> </method> <method name="is_tool" qualifiers="const"> <return type="bool"> </return> <description> - Returns true if the script is a tool script. A tool script can run in the editor. + Returns [code]true[/code] if the script is a tool script. A tool script can run in the editor. </description> </method> <method name="reload"> @@ -77,7 +78,7 @@ </methods> <members> <member name="source_code" type="String" setter="set_source_code" getter="get_source_code"> - The script source code, or an empty string if source code is not available. When set, does not reload the class implementation automatically. + The script source code or an empty string if source code is not available. When set, does not reload the class implementation automatically. </member> </members> <constants> diff --git a/doc/classes/ScriptCreateDialog.xml b/doc/classes/ScriptCreateDialog.xml index a3ad3a778e..67ce9a8e87 100644 --- a/doc/classes/ScriptCreateDialog.xml +++ b/doc/classes/ScriptCreateDialog.xml @@ -24,6 +24,8 @@ </argument> <argument index="1" name="path" type="String"> </argument> + <argument index="2" name="built_in_enabled" type="bool" default="true"> + </argument> <description> Prefills required fields to configure the ScriptCreateDialog for use. </description> @@ -31,7 +33,7 @@ </methods> <signals> <signal name="script_created"> - <argument index="0" name="script" type="Object"> + <argument index="0" name="script" type="Script"> </argument> <description> Emitted when the user clicks the OK button. diff --git a/doc/classes/ScriptEditor.xml b/doc/classes/ScriptEditor.xml index 4bfd07fdad..435ab8aafc 100644 --- a/doc/classes/ScriptEditor.xml +++ b/doc/classes/ScriptEditor.xml @@ -70,14 +70,14 @@ </methods> <signals> <signal name="editor_script_changed"> - <argument index="0" name="script" type="Object"> + <argument index="0" name="script" type="Script"> </argument> <description> Emitted when user changed active script. Argument is a freshly activated [Script]. </description> </signal> <signal name="script_close"> - <argument index="0" name="script" type="Object"> + <argument index="0" name="script" type="Script"> </argument> <description> Emitted when editor is about to close the active script. Argument is a [Script] that is going to be closed. diff --git a/doc/classes/ScrollContainer.xml b/doc/classes/ScrollContainer.xml index 02f58a88cb..d310561233 100644 --- a/doc/classes/ScrollContainer.xml +++ b/doc/classes/ScrollContainer.xml @@ -4,7 +4,7 @@ A helper node for displaying scrollable elements (e.g. lists). </brief_description> <description> - A ScrollContainer node with a [Control] child and scrollbar child ([HScrollbar], [VScrollBar], or both) will only draw the Control within the ScrollContainer area. Scrollbars will automatically be drawn at the right (for vertical) or bottom (for horizontal) and will enable dragging to move the viewable Control (and its children) within the ScrollContainer. Scrollbars will also automatically resize the grabber based on the minimum_size of the Control relative to the ScrollContainer. Works great with a [Panel] control. You can set EXPAND on children size flags, so they will upscale to ScrollContainer size if ScrollContainer size is bigger (scroll is invisible for chosen dimension). + A ScrollContainer node with a [Control] child and scrollbar child ([HScrollbar], [VScrollBar], or both) will only draw the Control within the ScrollContainer area. Scrollbars will automatically be drawn at the right (for vertical) or bottom (for horizontal) and will enable dragging to move the viewable Control (and its children) within the ScrollContainer. Scrollbars will also automatically resize the grabber based on the minimum_size of the Control relative to the ScrollContainer. Works great with a [Panel] control. You can set EXPAND on children size flags, so they will upscale to ScrollContainer size if ScrollContainer size is bigger (scroll is invisible for chosen dimension). </description> <tutorials> </tutorials> diff --git a/doc/classes/Shader.xml b/doc/classes/Shader.xml index 76049d8947..dd6a023af4 100644 --- a/doc/classes/Shader.xml +++ b/doc/classes/Shader.xml @@ -7,7 +7,7 @@ This class allows you to define a custom shader program that can be used for various materials to render objects. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/shading/index.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/shading/index.html</link> </tutorials> <demos> </demos> @@ -24,7 +24,7 @@ <return type="int" enum="Shader.Mode"> </return> <description> - Returns the shader mode for the shader, eiter [code]MODE_CANVAS_ITEM[/code], [code]MODE_SPATIAL[/code] or [code]MODE_PARTICLES[/code] + Returns the shader mode for the shader, either [code]MODE_CANVAS_ITEM[/code], [code]MODE_SPATIAL[/code] or [code]MODE_PARTICLES[/code] </description> </method> <method name="has_param" qualifiers="const"> diff --git a/doc/classes/ShaderMaterial.xml b/doc/classes/ShaderMaterial.xml index 5abba9fba9..7491d22479 100644 --- a/doc/classes/ShaderMaterial.xml +++ b/doc/classes/ShaderMaterial.xml @@ -20,6 +20,22 @@ Returns the current value set for this material of a uniform in the shader. </description> </method> + <method name="property_can_revert"> + <return type="bool"> + </return> + <argument index="0" name="name" type="String"> + </argument> + <description> + </description> + </method> + <method name="property_get_revert"> + <return type="Variant"> + </return> + <argument index="0" name="name" type="String"> + </argument> + <description> + </description> + </method> <method name="set_shader_param"> <return type="void"> </return> diff --git a/doc/classes/Shape.xml b/doc/classes/Shape.xml index fcd01bc25a..6e51d3156f 100644 --- a/doc/classes/Shape.xml +++ b/doc/classes/Shape.xml @@ -4,15 +4,19 @@ Base class for all 3D shape resources. </brief_description> <description> - Base class for all 3D shape resources. All 3D shapes that inherit from this can be set into a [PhysicsBody] or [Area]. + Base class for all 3D shape resources. Nodes that inherit from this can be used as shapes for a [PhysicsBody] or [Area] objects. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html</link> </tutorials> <demos> </demos> <methods> </methods> + <members> + <member name="margin" type="float" setter="set_margin" getter="get_margin"> + </member> + </members> <constants> </constants> </class> diff --git a/doc/classes/Shape2D.xml b/doc/classes/Shape2D.xml index 6c13496fc4..96b8a77d9e 100644 --- a/doc/classes/Shape2D.xml +++ b/doc/classes/Shape2D.xml @@ -7,7 +7,7 @@ Base class for all 2D Shapes. All 2D shape types inherit from this. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html</link> </tutorials> <demos> </demos> @@ -22,7 +22,7 @@ <argument index="2" name="shape_xform" type="Transform2D"> </argument> <description> - Return whether this shape is colliding with another. + Returns [code]true[/code] if this shape is colliding with another. This method needs the transformation matrix for this shape ([code]local_xform[/code]), the shape to check collisions with ([code]with_shape[/code]), and the transformation matrix of that shape ([code]shape_xform[/code]). </description> </method> @@ -36,7 +36,7 @@ <argument index="2" name="shape_xform" type="Transform2D"> </argument> <description> - Return a list of the points where this shape touches another. If there are no collisions, the list is empty. + Returns a list of the points where this shape touches another. If there are no collisions the list is empty. This method needs the transformation matrix for this shape ([code]local_xform[/code]), the shape to check collisions with ([code]with_shape[/code]), and the transformation matrix of that shape ([code]shape_xform[/code]). </description> </method> @@ -72,7 +72,7 @@ <argument index="4" name="shape_motion" type="Vector2"> </argument> <description> - Return a list of the points where this shape would touch another, if a given movement was applied. If there are no collisions, the list is empty. + Returns a list of the points where this shape would touch another, if a given movement was applied. If there are no collisions the list is empty. This method needs the transformation matrix for this shape ([code]local_xform[/code]), the movement to test on this shape ([code]local_motion[/code]), the shape to check collisions with ([code]with_shape[/code]), the transformation matrix of that shape ([code]shape_xform[/code]), and the movement to test onto the other object ([code]shape_motion[/code]). </description> </method> diff --git a/doc/classes/ShortCut.xml b/doc/classes/ShortCut.xml index 6da9d7c59d..1b5fc035c2 100644 --- a/doc/classes/ShortCut.xml +++ b/doc/classes/ShortCut.xml @@ -16,7 +16,7 @@ <return type="String"> </return> <description> - Returns the Shortcut's [InputEvent] as a [String]. + Returns the shortcut's [InputEvent] as a [String]. </description> </method> <method name="is_shortcut" qualifiers="const"> @@ -25,20 +25,20 @@ <argument index="0" name="event" type="InputEvent"> </argument> <description> - Returns [code]true[/code] if the Shortcut's [InputEvent] equals [code]event[/code]. + Returns [code]true[/code] if the shortcut's [InputEvent] equals [code]event[/code]. </description> </method> <method name="is_valid" qualifiers="const"> <return type="bool"> </return> <description> - If [code]true[/code] this Shortcut is valid. + If [code]true[/code] this shortcut is valid. </description> </method> </methods> <members> <member name="shortcut" type="InputEvent" setter="set_shortcut" getter="get_shortcut"> - The Shortcut's [InputEvent]. + The shortcut's [InputEvent]. Generally the [InputEvent] is a keyboard key, though it can be any [InputEvent]. </member> </members> diff --git a/doc/classes/SoftBody.xml b/doc/classes/SoftBody.xml index c3c789a6de..6c3929d65b 100644 --- a/doc/classes/SoftBody.xml +++ b/doc/classes/SoftBody.xml @@ -1,8 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="SoftBody" inherits="MeshInstance" category="Core" version="3.1"> <brief_description> + A soft mesh physics body. </brief_description> <description> + A deformable physics body. Used to create elastic or deformable objects such as cloth, rubber, or other flexible materials. </description> <tutorials> </tutorials> @@ -15,6 +17,14 @@ <argument index="0" name="body" type="Node"> </argument> <description> + Adds a body to the list of bodies that this body can't collide with. + </description> + </method> + <method name="get_collision_exceptions"> + <return type="Array"> + </return> + <description> + Returns an array of nodes that were added as collision exceptions for this body. </description> </method> <method name="get_collision_layer_bit" qualifiers="const"> @@ -23,6 +33,7 @@ <argument index="0" name="bit" type="int"> </argument> <description> + Returns an individual bit on the collision mask. </description> </method> <method name="get_collision_mask_bit" qualifiers="const"> @@ -31,6 +42,7 @@ <argument index="0" name="bit" type="int"> </argument> <description> + Returns an individual bit on the collision mask. </description> </method> <method name="is_ray_pickable" qualifiers="const"> @@ -45,6 +57,7 @@ <argument index="0" name="body" type="Node"> </argument> <description> + Removes a body from the list of bodies that this body can't collide with. </description> </method> <method name="set_collision_layer_bit"> @@ -55,6 +68,7 @@ <argument index="1" name="value" type="bool"> </argument> <description> + Sets individual bits on the layer mask. Use this if you only need to change one layer's value. </description> </method> <method name="set_collision_mask_bit"> @@ -65,6 +79,7 @@ <argument index="1" name="value" type="bool"> </argument> <description> + Sets individual bits on the collision mask. Use this if you only need to change one layer's value. </description> </method> <method name="set_ray_pickable"> @@ -80,8 +95,12 @@ <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"> + The physics layers this area is in. + Collidable objects can exist in any of 32 different layers. These layers work like a tagging system, and are not visual. A collidable can use these layers to select with which objects it can collide, using the collision_mask property. + A contact is detected if object A is in any of the layers that object B scans, or object B is in any layer scanned by object A. </member> <member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask"> + The physics layers this area scans for collisions. </member> <member name="damping_coefficient" type="float" setter="set_damping_coefficient" getter="get_damping_coefficient"> </member> @@ -96,6 +115,7 @@ <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"> + Increasing this value will improve the resulting simulation, but can affect performance. Use with care. </member> <member name="total_mass" type="float" setter="set_total_mass" getter="get_total_mass"> </member> diff --git a/doc/classes/Spatial.xml b/doc/classes/Spatial.xml index ef1bcc30b3..6f9d779737 100644 --- a/doc/classes/Spatial.xml +++ b/doc/classes/Spatial.xml @@ -8,11 +8,17 @@ Affine operations (rotate, scale, translate) happen in parent's local coordinate system, unless the Spatial object is set as top level. Affine operations in this coordinate system correspond to direct affine operations on the Spatial's transform. The word local below refers to this coordinate system. The coordinate system that is attached to the Spatial object itself is referred to as object-local coordinate system. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/3d/introduction_to_3d.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/3d/introduction_to_3d.html</link> </tutorials> <demos> </demos> <methods> + <method name="force_update_transform"> + <return type="void"> + </return> + <description> + </description> + </method> <method name="get_parent_spatial" qualifiers="const"> <return type="Spatial"> </return> @@ -296,11 +302,11 @@ World space (global) [Transform] of this node. </member> <member name="rotation" type="Vector3" setter="set_rotation" getter="get_rotation"> - Rotation part of the local transformation, specified in terms of YXZ-Euler angles in the format (X-angle, Y-angle, Z-angle), in radians. - Note that in the mathematical sense, rotation is a matrix and not a vector. The three Euler angles, which are the three indepdent parameters of the Euler-angle parametrization of the rotation matrix, are stored in a [Vector3] data structure not because the rotation is a vector, but only because [Vector3] exists as a convenient data-structure to store 3 floating point numbers. Therefore, applying affine operations on the rotation "vector" is not meaningful. + Rotation part of the local transformation in radians, specified in terms of YXZ-Euler angles in the format (X-angle, Y-angle, Z-angle). + Note that in the mathematical sense, rotation is a matrix and not a vector. The three Euler angles, which are the three independent parameters of the Euler-angle parametrization of the rotation matrix, are stored in a [Vector3] data structure not because the rotation is a vector, but only because [Vector3] exists as a convenient data-structure to store 3 floating point numbers. Therefore, applying affine operations on the rotation "vector" is not meaningful. </member> <member name="rotation_degrees" type="Vector3" setter="set_rotation_degrees" getter="get_rotation_degrees"> - Rotation part of the local transformation, specified in terms of YXZ-Euler angles in the format (X-angle, Y-angle, Z-angle), in degrees. + Rotation part of the local transformation in degrees, specified in terms of YXZ-Euler angles in the format (X-angle, Y-angle, Z-angle). </member> <member name="scale" type="Vector3" setter="set_scale" getter="get_scale"> Scale part of the local transformation. @@ -312,7 +318,7 @@ Local translation of this node. </member> <member name="visible" type="bool" setter="set_visible" getter="is_visible"> - Visibility of this node. Toggles if this node is rendered. + If [code]true[/code] this node is drawn. Default value: [code]true[/code]. </member> </members> <signals> diff --git a/doc/classes/SpatialMaterial.xml b/doc/classes/SpatialMaterial.xml index 4f48889531..8fd9b59bdc 100644 --- a/doc/classes/SpatialMaterial.xml +++ b/doc/classes/SpatialMaterial.xml @@ -1,11 +1,13 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="SpatialMaterial" inherits="Material" category="Core" version="3.1"> <brief_description> + Default 3D rendering material. </brief_description> <description> + This provides a default material with a wide variety of rendering features and properties without the need to write shader code. See the tutorial below for details. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/3d/spatial_material.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/3d/spatial_material.html</link> </tutorials> <demos> </demos> @@ -13,16 +15,20 @@ </methods> <members> <member name="albedo_color" type="Color" setter="set_albedo" getter="get_albedo"> + The material's base color. </member> <member name="albedo_texture" type="Texture" setter="set_texture" getter="get_texture"> </member> <member name="anisotropy" type="float" setter="set_anisotropy" getter="get_anisotropy"> + The strength of the anisotropy effect. </member> <member name="anisotropy_enabled" type="bool" setter="set_feature" getter="get_feature"> + If [code]true[/code] anisotropy is enabled. Changes the shape of the specular blob and aligns it to tangent space. Default value: [code]false[/code]. </member> <member name="anisotropy_flowmap" type="Texture" setter="set_texture" getter="get_texture"> </member> <member name="ao_enabled" type="bool" setter="set_feature" getter="get_feature"> + If [code]true[/code] ambient occlusion is enabled. </member> <member name="ao_light_affect" type="float" setter="set_ao_light_affect" getter="get_ao_light_affect"> </member> @@ -35,6 +41,7 @@ <member name="clearcoat" type="float" setter="set_clearcoat" getter="get_clearcoat"> </member> <member name="clearcoat_enabled" type="bool" setter="set_feature" getter="get_feature"> + If [code]true[/code] clearcoat rendering is enabled. Adds a secondary transparent pass to the material. Default value: [code]false[/code]. </member> <member name="clearcoat_gloss" type="float" setter="set_clearcoat_gloss" getter="get_clearcoat_gloss"> </member> @@ -43,6 +50,7 @@ <member name="depth_deep_parallax" type="bool" setter="set_depth_deep_parallax" getter="is_depth_deep_parallax_enabled"> </member> <member name="depth_enabled" type="bool" setter="set_feature" getter="get_feature"> + If [code]true[/code] Depth mapping is enabled. See also [member normal_enabled]. </member> <member name="depth_max_layers" type="int" setter="set_depth_deep_parallax_max_layers" getter="get_depth_deep_parallax_max_layers"> </member> @@ -64,17 +72,20 @@ </member> <member name="detail_uv_layer" type="int" setter="set_detail_uv" getter="get_detail_uv" enum="SpatialMaterial.DetailUV"> </member> - <member name="distance_fade_enable" type="bool" setter="set_distance_fade" getter="is_distance_fade_enabled"> - </member> <member name="distance_fade_max_distance" type="float" setter="set_distance_fade_max_distance" getter="get_distance_fade_max_distance"> </member> <member name="distance_fade_min_distance" type="float" setter="set_distance_fade_min_distance" getter="get_distance_fade_min_distance"> </member> + <member name="distance_fade_mode" type="int" setter="set_distance_fade" getter="get_distance_fade" enum="SpatialMaterial.DistanceFadeMode"> + </member> <member name="emission" type="Color" setter="set_emission" getter="get_emission"> + The emitted light's color. See [member emission_enabled]. </member> <member name="emission_enabled" type="bool" setter="set_feature" getter="get_feature"> + If [code]true[/code] the body emits light. </member> <member name="emission_energy" type="float" setter="set_emission_energy" getter="get_emission_energy"> + The emitted light's strength. See [member emission_enabled]. </member> <member name="emission_on_uv2" type="bool" setter="set_flag" getter="get_flag"> </member> @@ -85,36 +96,49 @@ <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"> + If [code]true[/code] the object receives no ambient light. Default value: [code]false[/code]. </member> <member name="flags_do_not_receive_shadows" type="bool" setter="set_flag" getter="get_flag"> + If [code]true[/code] the object receives no shadow that would otherwise be cast onto it. Default value: [code]false[/code]. </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"> + If [code]true[/code] the object is rendered at the same size regardless of distance. Default value: [code]false[/code]. </member> <member name="flags_no_depth_test" type="bool" setter="set_flag" getter="get_flag"> + If [code]true[/code] depth testing is disabled and the object will be drawn in render order. </member> <member name="flags_transparent" type="bool" setter="set_feature" getter="get_feature"> + If [code]true[/code] transparency is enabled on the body. Default value: [code]false[/code]. See also [member params_blend_mode]. </member> <member name="flags_unshaded" type="bool" setter="set_flag" getter="get_flag"> + If [code]true[/code] the object is unaffected by lighting. Default value: [code]false[/code]. </member> <member name="flags_use_point_size" type="bool" setter="set_flag" getter="get_flag"> + If [code]true[/code] render point size can be changed. Note: this is only effective for objects whose geometry is point-based rather than triangle-based. See also [member params_point_size]. </member> <member name="flags_vertex_lighting" type="bool" setter="set_flag" getter="get_flag"> + If [code]true[/code] lighting is calculated per vertex rather than per pixel. This may increase performance on low-end devices. Default value: [code]false[/code]. </member> <member name="flags_world_triplanar" type="bool" setter="set_flag" getter="get_flag"> + If [code]true[/code] triplanar mapping is calculated in world space rather than object local space. See also [member uv1_triplanar]. Default value: [code]false[/code]. </member> <member name="metallic" type="float" setter="set_metallic" getter="get_metallic"> + The reflectivity of the object's surface. The higher the value the more light is reflected. </member> <member name="metallic_specular" type="float" setter="set_specular" getter="get_specular"> + General reflectivity amount. Note: unlike [member metallic], this is not energy-conserving, so it should be left at [code]0.5[/code] in most cases. See also [member roughness]. </member> <member name="metallic_texture" type="Texture" setter="set_texture" getter="get_texture"> </member> <member name="metallic_texture_channel" type="int" setter="set_metallic_texture_channel" getter="get_metallic_texture_channel" enum="SpatialMaterial.TextureChannel"> </member> <member name="normal_enabled" type="bool" setter="set_feature" getter="get_feature"> + If [code]true[/code] normal mapping is enabled. </member> <member name="normal_scale" type="float" setter="set_normal_scale" getter="get_normal_scale"> + The strength of the normal map's effect. </member> <member name="normal_texture" type="Texture" setter="set_texture" getter="get_texture"> </member> @@ -123,40 +147,55 @@ <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"> + Controls how the object faces the camera. See [enum BillboardMode]. </member> <member name="params_blend_mode" type="int" setter="set_blend_mode" getter="get_blend_mode" enum="SpatialMaterial.BlendMode"> + The material's blend mode. Note that values other than [code]Mix[/code] force the object into the transparent pipeline. See [enum BlendMode]. </member> <member name="params_cull_mode" type="int" setter="set_cull_mode" getter="get_cull_mode" enum="SpatialMaterial.CullMode"> + Which side of the object is not drawn when backfaces are rendered. See [enum CullMode]. </member> <member name="params_depth_draw_mode" type="int" setter="set_depth_draw_mode" getter="get_depth_draw_mode" enum="SpatialMaterial.DepthDrawMode"> + Determines when depth rendering takes place. See [enum DepthDrawMode]. See also [member flags_transparent]. </member> <member name="params_diffuse_mode" type="int" setter="set_diffuse_mode" getter="get_diffuse_mode" enum="SpatialMaterial.DiffuseMode"> + The algorithm used for diffuse light scattering. See [enum DiffuseMode]. </member> <member name="params_grow" type="bool" setter="set_grow_enabled" getter="is_grow_enabled"> + If [code]true[/code] enables the vertex grow setting. See [member params_grow_amount]. </member> <member name="params_grow_amount" type="float" setter="set_grow" getter="get_grow"> + Grows object vertices in the direction of their normals. </member> <member name="params_line_width" type="float" setter="set_line_width" getter="get_line_width"> </member> <member name="params_point_size" type="float" setter="set_point_size" getter="get_point_size"> + The point size in pixels. See [member flags_use_point_size]. </member> <member name="params_specular_mode" type="int" setter="set_specular_mode" getter="get_specular_mode" enum="SpatialMaterial.SpecularMode"> + The method for rendering the specular blob. See [enum SpecularMode]. </member> <member name="params_use_alpha_scissor" type="bool" setter="set_flag" getter="get_flag"> </member> <member name="particles_anim_h_frames" type="int" setter="set_particles_anim_h_frames" getter="get_particles_anim_h_frames"> + The number of horizontal frames in the particle spritesheet. Only enabled when using [code]BillboardMode.BILLBOARD_PARTICLES[/code]. See [member params_billboard_mode]. </member> <member name="particles_anim_loop" type="int" setter="set_particles_anim_loop" getter="get_particles_anim_loop"> + If [code]true[/code] particle animations are looped. Only enabled when using [code]BillboardMode.BILLBOARD_PARTICLES[/code]. See [member params_billboard_mode]. </member> <member name="particles_anim_v_frames" type="int" setter="set_particles_anim_v_frames" getter="get_particles_anim_v_frames"> + The number of vertical frames in the particle spritesheet. Only enabled when using [code]BillboardMode.BILLBOARD_PARTICLES[/code]. See [member params_billboard_mode]. </member> <member name="proximity_fade_distance" type="float" setter="set_proximity_fade_distance" getter="get_proximity_fade_distance"> </member> <member name="proximity_fade_enable" type="bool" setter="set_proximity_fade" getter="is_proximity_fade_enabled"> + If [code]true[/code] the proximity and distance fade effect is enabled. Default value: [code]false[/code]. </member> <member name="refraction_enabled" type="bool" setter="set_feature" getter="get_feature"> + If [code]true[/code] the refraction effect is enabled. Distorts transparency based on light from behind the object. Default value: [code]false[/code]. </member> <member name="refraction_scale" type="float" setter="set_refraction" getter="get_refraction"> + The strength of the refraction effect. </member> <member name="refraction_texture" type="Texture" setter="set_texture" getter="get_texture"> </member> @@ -165,26 +204,33 @@ <member name="rim" type="float" setter="set_rim" getter="get_rim"> </member> <member name="rim_enabled" type="bool" setter="set_feature" getter="get_feature"> + If [code]true[/code] rim effect is enabled. Default value: [code]false[/code]. </member> <member name="rim_texture" type="Texture" setter="set_texture" getter="get_texture"> </member> <member name="rim_tint" type="float" setter="set_rim_tint" getter="get_rim_tint"> + The amount of to blend light and albedo color when rendering rim effect. If [code]0[/code] the light color is used, while [code]1[/code] means albedo color is used. An intermediate value generally works best. </member> <member name="roughness" type="float" setter="set_roughness" getter="get_roughness"> + Surface reflection. A value of [code]0[/code] represents a perfect mirror while a value of [code]1[/code] completely blurs the reflection. See also [member metallic]. </member> <member name="roughness_texture" type="Texture" setter="set_texture" getter="get_texture"> </member> <member name="roughness_texture_channel" type="int" setter="set_roughness_texture_channel" getter="get_roughness_texture_channel" enum="SpatialMaterial.TextureChannel"> </member> <member name="subsurf_scatter_enabled" type="bool" setter="set_feature" getter="get_feature"> + If [code]true[/code] subsurface scattering is enabled. Emulates light that penetrates an object's surface, is scattered, and then emerges. </member> <member name="subsurf_scatter_strength" type="float" setter="set_subsurface_scattering_strength" getter="get_subsurface_scattering_strength"> + The strength of the subsurface scattering effect. </member> <member name="subsurf_scatter_texture" type="Texture" setter="set_texture" getter="get_texture"> </member> <member name="transmission" type="Color" setter="set_transmission" getter="get_transmission"> + The color used by the transmission effect. Represents the light passing through an object. </member> <member name="transmission_enabled" type="bool" setter="set_feature" getter="get_feature"> + If [code]true[/code] the transmission effect is enabled. Default value: [code]false[/code]. </member> <member name="transmission_texture" type="Texture" setter="set_texture" getter="get_texture"> </member> @@ -205,8 +251,10 @@ <member name="uv2_triplanar_sharpness" type="float" setter="set_uv2_triplanar_blend_sharpness" getter="get_uv2_triplanar_blend_sharpness"> </member> <member name="vertex_color_is_srgb" type="bool" setter="set_flag" getter="get_flag"> + If [code]true[/code] the model's vertex colors are processed as sRGB mode. Default value: [code]false[/code]. </member> <member name="vertex_color_use_as_albedo" type="bool" setter="set_flag" getter="get_flag"> + If [code]true[/code] the vertex color is used as albedo color. Default value: [code]false[/code]. </member> </members> <constants> @@ -275,6 +323,7 @@ <constant name="FEATURE_MAX" value="12" enum="Feature"> </constant> <constant name="BLEND_MODE_MIX" value="0" enum="BlendMode"> + Default blend mode. </constant> <constant name="BLEND_MODE_ADD" value="1" enum="BlendMode"> </constant> @@ -283,18 +332,25 @@ <constant name="BLEND_MODE_MUL" value="3" enum="BlendMode"> </constant> <constant name="DEPTH_DRAW_OPAQUE_ONLY" value="0" enum="DepthDrawMode"> + Default depth draw mode. Depth is drawn only for opaque objects. </constant> <constant name="DEPTH_DRAW_ALWAYS" value="1" enum="DepthDrawMode"> + Depth draw is calculated for both opaque and transparent objects. </constant> <constant name="DEPTH_DRAW_DISABLED" value="2" enum="DepthDrawMode"> + No depth draw. </constant> <constant name="DEPTH_DRAW_ALPHA_OPAQUE_PREPASS" value="3" enum="DepthDrawMode"> + For transparent objects, an opaque pass is made first with the opaque parts, then transparency is drawn. </constant> <constant name="CULL_BACK" value="0" enum="CullMode"> + Default cull mode. The back of the object is culled when not visible. </constant> <constant name="CULL_FRONT" value="1" enum="CullMode"> + The front of the object is culled when not visible. </constant> <constant name="CULL_DISABLED" value="2" enum="CullMode"> + No culling is performed. </constant> <constant name="FLAG_UNSHADED" value="0" enum="Flags"> </constant> @@ -335,32 +391,46 @@ <constant name="FLAG_MAX" value="18" enum="Flags"> </constant> <constant name="DIFFUSE_BURLEY" value="0" enum="DiffuseMode"> + Default diffuse scattering algorithm. </constant> <constant name="DIFFUSE_LAMBERT" value="1" enum="DiffuseMode"> + Diffuse scattering ignores roughness. </constant> <constant name="DIFFUSE_LAMBERT_WRAP" value="2" enum="DiffuseMode"> + Extends Lambert to cover more than 90 degrees when roughness increases. </constant> <constant name="DIFFUSE_OREN_NAYAR" value="3" enum="DiffuseMode"> + Attempts to use roughness to emulate microsurfacing. </constant> <constant name="DIFFUSE_TOON" value="4" enum="DiffuseMode"> + Uses a hard cut for lighting, with smoothing affected by roughness. </constant> <constant name="SPECULAR_SCHLICK_GGX" value="0" enum="SpecularMode"> + Default specular blob. </constant> <constant name="SPECULAR_BLINN" value="1" enum="SpecularMode"> + Older specular algorithm, included for compatibility. </constant> <constant name="SPECULAR_PHONG" value="2" enum="SpecularMode"> + Older specular algorithm, included for compatibility. </constant> <constant name="SPECULAR_TOON" value="3" enum="SpecularMode"> + Toon blob which changes size based on roughness. </constant> <constant name="SPECULAR_DISABLED" value="4" enum="SpecularMode"> + No specular blob. </constant> <constant name="BILLBOARD_DISABLED" value="0" enum="BillboardMode"> + Default value. </constant> <constant name="BILLBOARD_ENABLED" value="1" enum="BillboardMode"> + The object's z-axis will always face the camera. </constant> <constant name="BILLBOARD_FIXED_Y" value="2" enum="BillboardMode"> + The object's x-axis will always face the camera. </constant> <constant name="BILLBOARD_PARTICLES" value="3" enum="BillboardMode"> + Used for particle systems. Enables particle animation options. </constant> <constant name="TEXTURE_CHANNEL_RED" value="0" enum="TextureChannel"> </constant> @@ -376,5 +446,13 @@ </constant> <constant name="EMISSION_OP_MULTIPLY" value="1" enum="EmissionOperator"> </constant> + <constant name="DISTANCE_FADE_DISABLED" value="0" enum="DistanceFadeMode"> + </constant> + <constant name="DISTANCE_FADE_PIXEL_ALPHA" value="1" enum="DistanceFadeMode"> + </constant> + <constant name="DISTANCE_FADE_PIXEL_DITHER" value="2" enum="DistanceFadeMode"> + </constant> + <constant name="DISTANCE_FADE_OBJECT_DITHER" value="3" enum="DistanceFadeMode"> + </constant> </constants> </class> diff --git a/doc/classes/SpinBox.xml b/doc/classes/SpinBox.xml index 0fcd5bbaf5..5087cf355a 100644 --- a/doc/classes/SpinBox.xml +++ b/doc/classes/SpinBox.xml @@ -19,6 +19,8 @@ </method> </methods> <members> + <member name="align" type="int" setter="set_align" getter="get_align" enum="LineEdit.Align"> + </member> <member name="editable" type="bool" setter="set_editable" getter="is_editable"> </member> <member name="prefix" type="String" setter="set_prefix" getter="get_prefix"> diff --git a/doc/classes/SpotLight.xml b/doc/classes/SpotLight.xml index 1f81e9e5c2..11f914aac2 100644 --- a/doc/classes/SpotLight.xml +++ b/doc/classes/SpotLight.xml @@ -7,7 +7,7 @@ A SpotLight light is a type of [Light] node that emits lights in a specific direction, in the shape of a cone. The light is attenuated through the distance and this attenuation can be configured by changing the energy, radius and attenuation parameters of [Light]. TODO: Image of a spotlight. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/3d/lights_and_shadows.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/3d/lights_and_shadows.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/Sprite.xml b/doc/classes/Sprite.xml index dd7fe010ba..52650c7300 100644 --- a/doc/classes/Sprite.xml +++ b/doc/classes/Sprite.xml @@ -18,6 +18,14 @@ Returns a Rect2 representing the Sprite's boundary relative to its local coordinates. </description> </method> + <method name="is_pixel_opaque" qualifiers="const"> + <return type="bool"> + </return> + <argument index="0" name="pos" type="Vector2"> + </argument> + <description> + </description> + </method> </methods> <members> <member name="centered" type="bool" setter="set_centered" getter="is_centered"> diff --git a/doc/classes/StreamPeer.xml b/doc/classes/StreamPeer.xml index ebe29c7e24..74ac8a79c0 100644 --- a/doc/classes/StreamPeer.xml +++ b/doc/classes/StreamPeer.xml @@ -81,10 +81,10 @@ <method name="get_string"> <return type="String"> </return> - <argument index="0" name="bytes" type="int"> + <argument index="0" name="bytes" type="int" default="-1"> </argument> <description> - Get a string with byte-length "bytes" from the stream. + Get a string with byte-length [code]bytes[/code] from the stream. If [code]bytes[/code] is negative (default) the length will be read from the stream using the reverse process of [method put_string]. </description> </method> <method name="get_u16"> @@ -118,10 +118,10 @@ <method name="get_utf8_string"> <return type="String"> </return> - <argument index="0" name="bytes" type="int"> + <argument index="0" name="bytes" type="int" default="-1"> </argument> <description> - Get a utf8 string with byte-length "bytes" from the stream (this decodes the string sent as utf8). + Get a utf8 string with byte-length [code]bytes[/code] from the stream (this decodes the string sent as utf8). If [code]bytes[/code] is negative (default) the length will be read from the stream using the reverse process of [method put_utf8_string]. </description> </method> <method name="get_var"> @@ -203,6 +203,15 @@ Send a chunk of data through the connection, if all the data could not be sent at once, only part of it will. This function returns two values, an Error code and an integer, describing how much data was actually sent. </description> </method> + <method name="put_string"> + <return type="void"> + </return> + <argument index="0" name="value" type="String"> + </argument> + <description> + Put a zero-terminated ascii string into the stream prepended by a 32 bits unsigned integer representing its size. + </description> + </method> <method name="put_u16"> <return type="void"> </return> @@ -245,7 +254,7 @@ <argument index="0" name="value" type="String"> </argument> <description> - Put a zero-terminated utf8 string into the stream. + Put a zero-terminated utf8 string into the stream prepended by a 32 bits unsigned integer representing its size. </description> </method> <method name="put_var"> diff --git a/doc/classes/StreamPeerSSL.xml b/doc/classes/StreamPeerSSL.xml index cf8769d22b..40a4e04a80 100644 --- a/doc/classes/StreamPeerSSL.xml +++ b/doc/classes/StreamPeerSSL.xml @@ -7,7 +7,7 @@ SSL Stream peer. This object can be used to connect to SSL servers. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/networking/ssl_certificates.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/networking/ssl_certificates.html</link> </tutorials> <demos> </demos> @@ -63,6 +63,8 @@ <constant name="STATUS_DISCONNECTED" value="0" enum="Status"> A status representing a [code]StreamPeerSSL[/code] that is disconnected. </constant> + <constant name="STATUS_HANDSHAKING" value="1" enum="Status"> + </constant> <constant name="STATUS_CONNECTED" value="2" enum="Status"> A status representing a [code]StreamPeerSSL[/code] that is connected to a host. </constant> diff --git a/doc/classes/StreamPeerTCP.xml b/doc/classes/StreamPeerTCP.xml index 5fc8e99ced..9a0fceddab 100644 --- a/doc/classes/StreamPeerTCP.xml +++ b/doc/classes/StreamPeerTCP.xml @@ -4,7 +4,7 @@ TCP Stream peer. </brief_description> <description> - TCP Stream peer. This object can be used to connect to TCP servers, or also is returned by a tcp server. + TCP Stream peer. This object can be used to connect to TCP servers, or also is returned by a TCP server. </description> <tutorials> </tutorials> @@ -43,7 +43,7 @@ Return the port of this peer. </description> </method> - <method name="get_status" qualifiers="const"> + <method name="get_status"> <return type="int" enum="StreamPeerTCP.Status"> </return> <description> @@ -54,6 +54,7 @@ <return type="bool"> </return> <description> + Returns [code]true[/code] if this peer is currently connected to a host, [code]false[code] otherwise. </description> </method> <method name="set_no_delay"> @@ -78,7 +79,7 @@ A status representing a [code]StreamPeerTCP[/code] that is connected to a host. </constant> <constant name="STATUS_ERROR" value="3" enum="Status"> - A staus representing a [code]StreamPeerTCP[/code] in error state. + A status representing a [code]StreamPeerTCP[/code] in error state. </constant> </constants> </class> diff --git a/doc/classes/String.xml b/doc/classes/String.xml index a42f508b59..536165487d 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -339,7 +339,7 @@ <method name="format"> <return type="String"> </return> - <argument index="0" name="values" type="var"> + <argument index="0" name="values" type="Variant"> </argument> <argument index="1" name="placeholder" type="String" default="{_}"> </argument> @@ -671,6 +671,7 @@ <return type="PoolByteArray"> </return> <description> + Returns the SHA-256 hash of the string as an array of bytes. </description> </method> <method name="sha256_text"> diff --git a/doc/classes/SurfaceTool.xml b/doc/classes/SurfaceTool.xml index deda7bc292..2e5d5d81c9 100644 --- a/doc/classes/SurfaceTool.xml +++ b/doc/classes/SurfaceTool.xml @@ -14,6 +14,7 @@ [/codeblock] The [code]SurfaceTool[/code] now contains one vertex of a triangle which has a UV coordinate and a specified [Color]. If another vertex were added without calls to [method add_uv] or [method add_color] then the last values would be used. It is very important that vertex attributes are passed [b]before[/b] the call to [method add_vertex], failure to do this will result in an error when committing the vertex information to a mesh. + Additionally, the attributes used before the first vertex is added determine the format of the mesh. For example if you only add UVs to the first vertex, you cannot add color to any of the subsequent vertices. </description> <tutorials> </tutorials> @@ -26,7 +27,7 @@ <argument index="0" name="bones" type="PoolIntArray"> </argument> <description> - Add an array of bones for the next Vertex to use. + Add an array of bones for the next Vertex to use. Array must contain 4 integers. </description> </method> <method name="add_color"> @@ -99,6 +100,7 @@ </argument> <description> Insert a triangle fan made of array data into [Mesh] being constructed. + Requires primitive type be set to [code]PRIMITIVE_TRIANGLES[/code]. </description> </method> <method name="add_uv"> @@ -134,7 +136,7 @@ <argument index="0" name="weights" type="PoolRealArray"> </argument> <description> - Specify weight value for next Vertex to use. + Specify weight values for next Vertex to use. Array must contain 4 values. </description> </method> <method name="append_from"> @@ -147,6 +149,7 @@ <argument index="2" name="transform" type="Transform"> </argument> <description> + Append vertices from a given [Mesh] surface onto the current vertex array with specified [Transform]. </description> </method> <method name="begin"> @@ -184,6 +187,7 @@ <argument index="1" name="surface" type="int"> </argument> <description> + Creates a vertex array from an existing [Mesh]. </description> </method> <method name="deindex"> @@ -201,12 +205,15 @@ <description> Generates normals from Vertices so you do not have to do it manually. Setting "flip" [code]true[/code] inverts resulting normals. + Requires primitive type to be set to [code]PRIMITIVE_TRIANGLES[/code]. </description> </method> <method name="generate_tangents"> <return type="void"> </return> <description> + Generates a tangent vector for each vertex. + Requires that each vertex have UVs and normals set already. </description> </method> <method name="index"> diff --git a/doc/classes/TabContainer.xml b/doc/classes/TabContainer.xml index 5acfd6194e..f17c10b31e 100644 --- a/doc/classes/TabContainer.xml +++ b/doc/classes/TabContainer.xml @@ -82,6 +82,7 @@ <return type="int"> </return> <description> + Returns the [code]TabContainer[/code] rearrange group id. </description> </method> <method name="set_popup"> @@ -132,6 +133,7 @@ <argument index="0" name="group_id" type="int"> </argument> <description> + Defines rearrange group id, choose for each [code]TabContainer[/code] the same value to enable tab drag between [code]TabContainer[/code]. Enable drag with [code]set_drag_to_rearrange_enabled(true)[/code]. </description> </method> </methods> @@ -140,6 +142,7 @@ The current tab index. When set, this index's [Control] node's [code]visible[/code] property is set to [code]true[/code] and all others are set to [code]false[/code]. </member> <member name="drag_to_rearrange_enabled" type="bool" setter="set_drag_to_rearrange_enabled" getter="get_drag_to_rearrange_enabled"> + If [code]true[/code], tabs can be rearranged with mouse drag. </member> <member name="tab_align" type="int" setter="set_tab_align" getter="get_tab_align" enum="TabContainer.TabAlign"> The alignment of all tabs in the tab container. See the [code]ALIGN_*[/code] constants for details. @@ -171,10 +174,13 @@ </signals> <constants> <constant name="ALIGN_LEFT" value="0" enum="TabAlign"> + Align the tabs to the left. </constant> <constant name="ALIGN_CENTER" value="1" enum="TabAlign"> + Align the tabs to the center. </constant> <constant name="ALIGN_RIGHT" value="2" enum="TabAlign"> + Align the tabs to the right. </constant> </constants> <theme_items> diff --git a/doc/classes/Tabs.xml b/doc/classes/Tabs.xml index fc1d0476ed..350b49513d 100644 --- a/doc/classes/Tabs.xml +++ b/doc/classes/Tabs.xml @@ -19,6 +19,7 @@ <argument index="1" name="icon" type="Texture" default="null"> </argument> <description> + Adds a new tab. </description> </method> <method name="ensure_tab_visible"> @@ -27,6 +28,7 @@ <argument index="0" name="idx" type="int"> </argument> <description> + Moves the Scroll view to make the tab visible. </description> </method> <method name="get_offset_buttons_visible" qualifiers="const"> @@ -39,12 +41,14 @@ <return type="bool"> </return> <description> + Returns [code]true[/code] if select with right mouse button is enabled. </description> </method> <method name="get_tab_count" qualifiers="const"> <return type="int"> </return> <description> + Returns the number of tabs. </description> </method> <method name="get_tab_disabled" qualifiers="const"> @@ -53,6 +57,7 @@ <argument index="0" name="tab_idx" type="int"> </argument> <description> + Returns [code]true[/code] if the tab at index [code]tab_idx[/code] is disabled. </description> </method> <method name="get_tab_icon" qualifiers="const"> @@ -61,6 +66,7 @@ <argument index="0" name="tab_idx" type="int"> </argument> <description> + Returns the [Texture] for the tab at index [code]tab_idx[/code] or null if the tab has no [Texture]. </description> </method> <method name="get_tab_offset" qualifiers="const"> @@ -84,12 +90,14 @@ <argument index="0" name="tab_idx" type="int"> </argument> <description> + Returns the title of the tab at index [code]tab_idx[/code]. Tab titles default to the name of the indexed child node, but this can be overridden with [method set_tab_title]. </description> </method> <method name="get_tabs_rearrange_group" qualifiers="const"> <return type="int"> </return> <description> + Returns the [code]Tabs[/code] rearrange group id. </description> </method> <method name="move_tab"> @@ -109,6 +117,7 @@ <argument index="0" name="tab_idx" type="int"> </argument> <description> + Removes tab at index [code]tab_idx[/code] </description> </method> <method name="set_select_with_rmb"> @@ -117,6 +126,7 @@ <argument index="0" name="enabled" type="bool"> </argument> <description> + If [code]true[/code] enables selecting a tab with right mouse button. </description> </method> <method name="set_tab_disabled"> @@ -127,6 +137,7 @@ <argument index="1" name="disabled" type="bool"> </argument> <description> + If [code]disabled[/code] is false, hides the tab at index [code]tab_idx[/code]. Note that its title text will remain, unless also removed with [method set_tab_title]. </description> </method> <method name="set_tab_icon"> @@ -137,6 +148,7 @@ <argument index="1" name="icon" type="Texture"> </argument> <description> + Sets an icon for the tab at index [code]tab_idx[/code]. </description> </method> <method name="set_tab_title"> @@ -147,6 +159,7 @@ <argument index="1" name="title" type="String"> </argument> <description> + Sets a title for the tab at index [code]tab_idx[/code]. </description> </method> <method name="set_tabs_rearrange_group"> @@ -155,17 +168,21 @@ <argument index="0" name="group_id" type="int"> </argument> <description> + Defines rearrange group id, choose for each [code]Tabs[/code] the same value to enable tab drag between [code]Tabs[/code]. Enable drag with [code]set_drag_to_rearrange_enabled(true)[/code]. </description> </method> </methods> <members> <member name="current_tab" type="int" setter="set_current_tab" getter="get_current_tab"> + Select tab at index [code]tab_idx[/code]. </member> <member name="drag_to_rearrange_enabled" type="bool" setter="set_drag_to_rearrange_enabled" getter="get_drag_to_rearrange_enabled"> + If [code]true[/code], tabs can be rearranged with mouse drag. </member> <member name="scrolling_enabled" type="bool" setter="set_scrolling_enabled" getter="get_scrolling_enabled"> </member> <member name="tab_align" type="int" setter="set_tab_align" getter="get_tab_align" enum="Tabs.TabAlign"> + The alignment of all tabs. See enum [code]TabAlign[/code] constants for details. </member> <member name="tab_close_display_policy" type="int" setter="set_tab_close_display_policy" getter="get_tab_close_display_policy" enum="Tabs.CloseButtonDisplayPolicy"> </member> @@ -210,10 +227,13 @@ </signals> <constants> <constant name="ALIGN_LEFT" value="0" enum="TabAlign"> + Align the tabs to the left. </constant> <constant name="ALIGN_CENTER" value="1" enum="TabAlign"> + Align the tabs to the center. </constant> <constant name="ALIGN_RIGHT" value="2" enum="TabAlign"> + Align the tabs to the right. </constant> <constant name="ALIGN_MAX" value="3" enum="TabAlign"> </constant> diff --git a/doc/classes/TextureProgress.xml b/doc/classes/TextureProgress.xml index f74420e8b1..3cbaf0429c 100644 --- a/doc/classes/TextureProgress.xml +++ b/doc/classes/TextureProgress.xml @@ -52,10 +52,13 @@ [Texture] that draws under the progress bar. The bar's background. </member> <member name="tint_over" type="Color" setter="set_tint_over" getter="get_tint_over"> + Multiplies the color of the bar's [code]texture_over[/code] texture. The effect is similar to [member CanvasItem.modulate], except it only affects this specific texture instead of the entire node. </member> <member name="tint_progress" type="Color" setter="set_tint_progress" getter="get_tint_progress"> + Multiplies the color of the bar's [code]texture_progress[/code] texture. </member> <member name="tint_under" type="Color" setter="set_tint_under" getter="get_tint_under"> + Multiplies the color of the bar's [code]texture_under[/code] texture. </member> </members> <constants> @@ -72,16 +75,19 @@ The [member texture_progress] fills from bottom to top. </constant> <constant name="FILL_CLOCKWISE" value="4" enum="FillMode"> - Turns the node into a radial bar. The [member texture_progress] fills clockwise. See [member radial_center_offset], [member radial_initial_angle] and [member radial_fill_degrees] to refine its behavior. + Turns the node into a radial bar. The [member texture_progress] fills clockwise. See [member radial_center_offset], [member radial_initial_angle] and [member radial_fill_degrees] to control the way the bar fills up. </constant> <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. + 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 control the way the bar fills up. </constant> <constant name="FILL_BILINEAR_LEFT_AND_RIGHT" value="6" enum="FillMode"> + The [member texture_progress] fills from the center, expanding both towards the left and the right. </constant> <constant name="FILL_BILINEAR_TOP_AND_BOTTOM" value="7" enum="FillMode"> + The [member texture_progress] fills from the center, expanding both towards the top and the bottom. </constant> <constant name="FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE" value="8" enum="FillMode"> + Turns the node into a radial bar. The [member texture_progress] fills radially from the center, expanding both clockwise and counter-clockwise. See [member radial_center_offset], [member radial_initial_angle] and [member radial_fill_degrees] to control the way the bar fills up. </constant> </constants> </class> diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml index 73d60e49b7..7034d75473 100644 --- a/doc/classes/TileMap.xml +++ b/doc/classes/TileMap.xml @@ -7,7 +7,7 @@ Node for 2D tile-based maps. Tilemaps use a [TileSet] which contain a list of tiles (textures plus optional collision, navigation, and/or occluder shapes) which are used to create grid-based maps. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/2d/using_tilemaps.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/2d/using_tilemaps.html</link> </tutorials> <demos> </demos> @@ -157,16 +157,6 @@ 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> @@ -258,7 +248,7 @@ Amount to offset alternating tiles. Uses HALF_OFFSET_* constants. Default value: HALF_OFFSET_DISABLED. </member> <member name="cell_quadrant_size" type="int" setter="set_quadrant_size" getter="get_quadrant_size"> - The TileMap's quadrant size. Optimizes drawing by batching, using chunks of this size. Default value: 16. + The TileMap's quadrant size. Optimizes drawing by batching, using chunks of this size. Default value: 16. </member> <member name="cell_size" type="Vector2" setter="set_cell_size" getter="get_cell_size"> The TileMap's cell size. @@ -288,7 +278,7 @@ The TileMap orientation mode. Uses MODE_* constants. Default value: MODE_SQUARE. </member> <member name="occluder_light_mask" type="int" setter="set_occluder_light_mask" getter="get_occluder_light_mask"> - The light mask assigned to all light occluders in the TileMap. The TileSet's light occluders will cast shadows only from Light2D(s) that have the same light mask(s). + The light mask assigned to all light occluders in the TileMap. The TileSet's light occluders will cast shadows only from Light2D(s) that have the same light mask(s). </member> <member name="tile_set" type="TileSet" setter="set_tileset" getter="get_tileset"> The assigned [TileSet]. diff --git a/doc/classes/TileSet.xml b/doc/classes/TileSet.xml index 56bb33c5e1..ffd15e8d8b 100644 --- a/doc/classes/TileSet.xml +++ b/doc/classes/TileSet.xml @@ -44,6 +44,14 @@ <description> </description> </method> + <method name="autotile_get_size" qualifiers="const"> + <return type="Vector2"> + </return> + <argument index="0" name="id" type="int"> + </argument> + <description> + </description> + </method> <method name="autotile_set_bitmask_mode"> <return type="void"> </return> @@ -54,6 +62,16 @@ <description> </description> </method> + <method name="autotile_set_size"> + <return type="void"> + </return> + <argument index="0" name="id" type="int"> + </argument> + <argument index="1" name="size" type="Vector2"> + </argument> + <description> + </description> + </method> <method name="clear"> <return type="void"> </return> @@ -67,7 +85,7 @@ <argument index="0" name="id" type="int"> </argument> <description> - Creates a new tile which will be referenced by the given ID. + Creates a new tile with the given ID. </description> </method> <method name="find_tile_by_name" qualifiers="const"> @@ -99,7 +117,7 @@ <argument index="0" name="id" type="int"> </argument> <description> - Removes the tile referenced by the given ID. + Removes the given tile ID. </description> </method> <method name="tile_add_shape"> @@ -116,6 +134,7 @@ <argument index="4" name="autotile_coord" type="Vector2" default="Vector2( 0, 0 )"> </argument> <description> + Adds a shape to the tile. </description> </method> <method name="tile_get_light_occluder" qualifiers="const"> @@ -124,7 +143,7 @@ <argument index="0" name="id" type="int"> </argument> <description> - Returns the light occluder of the tile. + Returns the tile's light occluder. </description> </method> <method name="tile_get_material" qualifiers="const"> @@ -133,7 +152,7 @@ <argument index="0" name="id" type="int"> </argument> <description> - Returns the material of the tile. + Returns the tile's material. </description> </method> <method name="tile_get_modulate" qualifiers="const"> @@ -142,6 +161,7 @@ <argument index="0" name="id" type="int"> </argument> <description> + Returns the tile's modulation color. </description> </method> <method name="tile_get_name" qualifiers="const"> @@ -150,7 +170,7 @@ <argument index="0" name="id" type="int"> </argument> <description> - Returns the name of the tile. + Returns the tile's name. </description> </method> <method name="tile_get_navigation_polygon" qualifiers="const"> @@ -177,6 +197,7 @@ <argument index="0" name="id" type="int"> </argument> <description> + Returns the tile's normal map texture. </description> </method> <method name="tile_get_occluder_offset" qualifiers="const"> @@ -205,6 +226,7 @@ <argument index="1" name="shape_id" type="int"> </argument> <description> + Returns a tile's given shape. </description> </method> <method name="tile_get_shape_count" qualifiers="const"> @@ -213,6 +235,18 @@ <argument index="0" name="id" type="int"> </argument> <description> + Returns the number of shapes assigned to a tile. + </description> + </method> + <method name="tile_get_shape_offset" qualifiers="const"> + <return type="Vector2"> + </return> + <argument index="0" name="id" type="int"> + </argument> + <argument index="1" name="shape_id" type="int"> + </argument> + <description> + Returns the offset of a tile's shape. </description> </method> <method name="tile_get_shape_one_way" qualifiers="const"> @@ -223,6 +257,7 @@ <argument index="1" name="shape_id" type="int"> </argument> <description> + Returns the one-way collision value of a tile's shape. </description> </method> <method name="tile_get_shape_transform" qualifiers="const"> @@ -233,6 +268,7 @@ <argument index="1" name="shape_id" type="int"> </argument> <description> + Returns the [Transform2D] of a tile's shape. </description> </method> <method name="tile_get_shapes" qualifiers="const"> @@ -241,7 +277,7 @@ <argument index="0" name="id" type="int"> </argument> <description> - Returns the array of shapes of the tile. + Returns an array of the tile's shapes. </description> </method> <method name="tile_get_texture" qualifiers="const"> @@ -250,7 +286,7 @@ <argument index="0" name="id" type="int"> </argument> <description> - Returns the texture of the tile. + Returns the tile's texture. </description> </method> <method name="tile_get_texture_offset" qualifiers="const"> @@ -268,6 +304,7 @@ <argument index="0" name="id" type="int"> </argument> <description> + Returns the tile's [enum TileMode]. </description> </method> <method name="tile_get_z_index" qualifiers="const"> @@ -276,6 +313,7 @@ <argument index="0" name="id" type="int"> </argument> <description> + Returns the tile's z-index (drawing layer). </description> </method> <method name="tile_set_light_occluder"> @@ -363,7 +401,7 @@ <argument index="1" name="occluder_offset" type="Vector2"> </argument> <description> - Set an offset for the tile's light occluder. + Sets an offset for the tile's light occluder. </description> </method> <method name="tile_set_region"> @@ -374,7 +412,7 @@ <argument index="1" name="region" type="Rect2"> </argument> <description> - Set the tile's sub-region in the texture. This is common in texture atlases. + Sets the tile's sub-region in the texture. This is common in texture atlases. </description> </method> <method name="tile_set_shape"> @@ -387,6 +425,20 @@ <argument index="2" name="shape" type="Shape2D"> </argument> <description> + Sets a shape for the tile, enabling collision. + </description> + </method> + <method name="tile_set_shape_offset"> + <return type="void"> + </return> + <argument index="0" name="id" type="int"> + </argument> + <argument index="1" name="shape_id" type="int"> + </argument> + <argument index="2" name="shape_offset" type="Vector2"> + </argument> + <description> + Sets the offset of a tile's shape. </description> </method> <method name="tile_set_shape_one_way"> @@ -399,6 +451,7 @@ <argument index="2" name="one_way" type="bool"> </argument> <description> + Enables one-way collision on a tile's shape. </description> </method> <method name="tile_set_shape_transform"> @@ -411,6 +464,7 @@ <argument index="2" name="shape_transform" type="Transform2D"> </argument> <description> + Sets a [Transform2D] on a tile's shape. </description> </method> <method name="tile_set_shapes"> diff --git a/doc/classes/Timer.xml b/doc/classes/Timer.xml index d1c8722901..65d638c4c0 100644 --- a/doc/classes/Timer.xml +++ b/doc/classes/Timer.xml @@ -32,25 +32,26 @@ <return type="void"> </return> <description> - Stop (cancel) the Timer. + Stops the timer. </description> </method> </methods> <members> <member name="autostart" type="bool" setter="set_autostart" getter="has_autostart"> - If [code]true[/code], Timer will automatically start when entering the scene tree. Default value: [code]false[/code]. + If [code]true[/code] the timer will automatically start when entering the scene tree. Default value: [code]false[/code]. </member> <member name="one_shot" type="bool" setter="set_one_shot" getter="is_one_shot"> - If [code]true[/code], Timer will stop when reaching 0. If [code]false[/code], it will restart. Default value: [code]false[/code]. + If [code]true[/code] the timer will stop when reaching 0. If [code]false[/code] it will restart. Default value: [code]false[/code]. </member> <member name="paused" type="bool" setter="set_paused" getter="is_paused"> - If [code]true[/code], the timer is paused and will not process until it is unpaused again, even if [method start] is called. + If [code]true[/code] the timer is paused and will not process until it is unpaused again, even if [method start] is called. </member> <member name="process_mode" type="int" setter="set_timer_process_mode" getter="get_timer_process_mode" enum="Timer.TimerProcessMode"> - Processing mode. Uses TIMER_PROCESS_* constants as value. + Processing mode. See [enum TimerProcessMode]. </member> <member name="time_left" type="float" setter="" getter="get_time_left"> The timer's remaining time in seconds. Returns 0 if the timer is inactive. + Note: You cannot set this value. To change the timer's remaining time, use [member wait_time]. </member> <member name="wait_time" type="float" setter="set_wait_time" getter="get_wait_time"> Wait time in seconds. @@ -59,16 +60,16 @@ <signals> <signal name="timeout"> <description> - Emitted when the Timer reaches 0. + Emitted when the timer reaches 0. </description> </signal> </signals> <constants> <constant name="TIMER_PROCESS_PHYSICS" value="0" enum="TimerProcessMode"> - Update the Timer during the physics step at each frame (fixed framerate processing). + Update the timer during the physics step at each frame (fixed framerate processing). </constant> <constant name="TIMER_PROCESS_IDLE" value="1" enum="TimerProcessMode"> - Update the Timer during the idle time at each frame. + Update the timer during the idle time at each frame. </constant> </constants> </class> diff --git a/doc/classes/Transform.xml b/doc/classes/Transform.xml index 24c009d922..b3168ef129 100644 --- a/doc/classes/Transform.xml +++ b/doc/classes/Transform.xml @@ -7,8 +7,8 @@ Represents one or many transformations in 3D space such as translation, rotation, or scaling. It consists of a [Basis] "basis" and an [Vector3] "origin". It is similar to a 3x4 matrix. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/math/index.html</link> - <link>http://docs.godotengine.org/en/latest/tutorials/3d/using_transforms.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/math/index.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/3d/using_transforms.html</link> </tutorials> <demos> </demos> @@ -141,18 +141,18 @@ </description> </method> <method name="xform"> - <return type="var"> + <return type="Variant"> </return> - <argument index="0" name="v" type="var"> + <argument index="0" name="v" type="Variant"> </argument> <description> Transforms the given [Vector3], [Plane], or [AABB] by this transform. </description> </method> <method name="xform_inv"> - <return type="var"> + <return type="Variant"> </return> - <argument index="0" name="v" type="var"> + <argument index="0" name="v" type="Variant"> </argument> <description> Inverse-transforms the given [Vector3], [Plane], or [AABB] by this transform. diff --git a/doc/classes/Transform2D.xml b/doc/classes/Transform2D.xml index b38d9a1a86..bf0a745aa8 100644 --- a/doc/classes/Transform2D.xml +++ b/doc/classes/Transform2D.xml @@ -143,18 +143,18 @@ </description> </method> <method name="xform"> - <return type="var"> + <return type="Variant"> </return> - <argument index="0" name="v" type="var"> + <argument index="0" name="v" type="Variant"> </argument> <description> Transforms the given [Vector2] or [Rect2] by this transform. </description> </method> <method name="xform_inv"> - <return type="var"> + <return type="Variant"> </return> - <argument index="0" name="v" type="var"> + <argument index="0" name="v" type="Variant"> </argument> <description> Inverse-transforms the given [Vector2] or [Rect2] by this transform. diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml index 533df57564..8051062fc4 100644 --- a/doc/classes/Tree.xml +++ b/doc/classes/Tree.xml @@ -37,7 +37,7 @@ </description> </method> <method name="create_item"> - <return type="Object"> + <return type="TreeItem"> </return> <argument index="0" name="parent" type="Object" default="null"> </argument> @@ -94,7 +94,7 @@ <argument index="0" name="position" type="Vector2"> </argument> <description> - If [member drop_mode_flags] includes [code]DROP_MODE_INBETWEEN[/code], returns -1 if [code]position[/code] is the upper part of a tree item at that position, 1 for the lower part, and additionally 0 for the middle part if [member drop_mode_flags] includes [code]DROP_MODE_ON_ITEM[/code]. + If [member drop_mode_flags] includes [code]DROP_MODE_INBETWEEN[/code], returns -1 if [code]position[/code] is the upper part of a tree item at that position, 1 for the lower part, and additionally 0 for the middle part if [member drop_mode_flags] includes [code]DROP_MODE_ON_ITEM[/code]. Otherwise, returns 0. If there are no tree item at [code]position[/code], returns -100. </description> </method> @@ -230,7 +230,7 @@ The amount of columns. </member> <member name="drop_mode_flags" type="int" setter="set_drop_mode_flags" getter="get_drop_mode_flags"> - The drop mode as an OR combination of flags. See [code]DROP_MODE_*[/code] constants. Once dropping is done, reverts to [code]DROP_MODE_DISABLED[/code]. Setting this during [method can_drop_data] is recommended. + The drop mode as an OR combination of flags. See [code]DROP_MODE_*[/code] constants. Once dropping is done, reverts to [code]DROP_MODE_DISABLED[/code]. Setting this during [method Control.can_drop_data] is recommended. </member> <member name="hide_folding" type="bool" setter="set_hide_folding" getter="is_folding_hidden"> If [code]true[/code] the folding arrow is hidden. @@ -244,7 +244,7 @@ </members> <signals> <signal name="button_pressed"> - <argument index="0" name="item" type="Object"> + <argument index="0" name="item" type="TreeItem"> </argument> <argument index="1" name="column" type="int"> </argument> @@ -286,7 +286,7 @@ </description> </signal> <signal name="item_collapsed"> - <argument index="0" name="item" type="Object"> + <argument index="0" name="item" type="TreeItem"> </argument> <description> Emitted when an item is collapsed by a click on the folding arrow. @@ -324,7 +324,7 @@ </description> </signal> <signal name="multi_selected"> - <argument index="0" name="item" type="Object"> + <argument index="0" name="item" type="TreeItem"> </argument> <argument index="1" name="column" type="int"> </argument> diff --git a/doc/classes/TreeItem.xml b/doc/classes/TreeItem.xml index 57e0f2825a..b4227b34be 100644 --- a/doc/classes/TreeItem.xml +++ b/doc/classes/TreeItem.xml @@ -25,7 +25,7 @@ <argument index="4" name="tooltip" type="String" default=""""> </argument> <description> - Adds a button with [Texture] [code]button[/code] at column [code]column[/code]. The [code]button_idx[/code] index is used to identify the button when calling other methods. If not specified, the next available index is used, which may be retrieved by calling [code]get_buton_count()[/code] immediately after this method. Optionally, the button can be [code]disabled[/code] and have a [code]tooltip[/code]. + Adds a button with [Texture] [code]button[/code] at column [code]column[/code]. The [code]button_idx[/code] index is used to identify the button when calling other methods. If not specified, the next available index is used, which may be retrieved by calling [method get_button_count] immediately after this method. Optionally, the button can be [code]disabled[/code] and have a [code]tooltip[/code]. </description> </method> <method name="clear_custom_bg_color"> @@ -400,6 +400,7 @@ </argument> <description> Sets the given column's custom draw callback to [code]callback[/code] method on [code]object[/code]. + The [code]callback[/code] should accept two arguments: the [TreeItem] that is drawn and its position and size as a [Rect2]. </description> </method> <method name="set_editable"> @@ -558,13 +559,10 @@ <constant name="CELL_MODE_RANGE" value="2" enum="TreeCellMode"> Cell contains a range. </constant> - <constant name="CELL_MODE_RANGE_EXPRESSION" value="3" enum="TreeCellMode"> - Cell contains a range expression. - </constant> - <constant name="CELL_MODE_ICON" value="4" enum="TreeCellMode"> + <constant name="CELL_MODE_ICON" value="3" enum="TreeCellMode"> Cell contains an icon. </constant> - <constant name="CELL_MODE_CUSTOM" value="5" enum="TreeCellMode"> + <constant name="CELL_MODE_CUSTOM" value="4" enum="TreeCellMode"> </constant> <constant name="ALIGN_LEFT" value="0" enum="TextAlign"> Align text to the left. See [code]set_text_align()[/code]. diff --git a/doc/classes/Tween.xml b/doc/classes/Tween.xml index 123226183a..e792835605 100644 --- a/doc/classes/Tween.xml +++ b/doc/classes/Tween.xml @@ -9,12 +9,12 @@ [codeblock] var tween = get_node("Tween") tween.interpolate_property($Node2D, "position", - Vector2(0, 0), Vector2(100, 100), 1, - Tween.TRANS_LINEAR, Tween.EASE_IN_OUT) + Vector2(0, 0), Vector2(100, 100), 1, + Tween.TRANS_LINEAR, Tween.EASE_IN_OUT) tween.start() [/codeblock] Many methods require a property name, such as "position" above. You can find the correct property name by hovering over the property in the Inspector. - Many of the methods accept [code]trans_type[/code] and [code]ease_type[/code]. The first accepts an [enum TransitionType] constant, and refers to the way the timing of the animation is handled (see [code]http://easings.net/[/code] for some examples). The second accepts an [enum EaseType] constant, and controls the where [code]trans_type[/code] is applied to the interpolation (in the beginning, the end, or both). If you don't know which transition and easing to pick, you can try different [enum TransitionType] constants with [enum EASE_IN_OUT], and use the one that looks best. + Many of the methods accept [code]trans_type[/code] and [code]ease_type[/code]. The first accepts an [enum TransitionType] constant, and refers to the way the timing of the animation is handled (see [code]http://easings.net/[/code] for some examples). The second accepts an [enum EaseType] constant, and controls the where [code]trans_type[/code] is applied to the interpolation (in the beginning, the end, or both). If you don't know which transition and easing to pick, you can try different [enum TransitionType] constants with [code]EASE_IN_OUT[/code], and use the one that looks best. </description> <tutorials> </tutorials> @@ -44,7 +44,7 @@ </argument> <description> Follows [code]method[/code] of [code]object[/code] and applies the returned value on [code]target_method[/code] of [code]target[/code], beginning from [code]initial_val[/code] for [code]duration[/code] seconds, [code]delay[/code] later. Methods are called with consecutive values. - Use [enum TransitionType] for [code]trans_type[/code] and [enum EaseType] for [code]ease_type[/code] parameters. These values control the timing and direction of the interpolation. See the class description for more information + Use [enum TransitionType] for [code]trans_type[/code] and [enum EaseType] for [code]ease_type[/code] parameters. These values control the timing and direction of the interpolation. See the class description for more information </description> </method> <method name="follow_property"> @@ -70,7 +70,7 @@ </argument> <description> Follows [code]property[/code] of [code]object[/code] and applies it on [code]target_property[/code] of [code]target[/code], beginning from [code]initial_val[/code] for [code]duration[/code] seconds, [code]delay[/code] seconds later. - Use [enum TransitionType] for [code]trans_type[/code] and [enum EaseType] for [code]ease_type[/code] parameters. These values control the timing and direction of the interpolation. See the class description for more information + Use [enum TransitionType] for [code]trans_type[/code] and [enum EaseType] for [code]ease_type[/code] parameters. These values control the timing and direction of the interpolation. See the class description for more information </description> </method> <method name="get_runtime" qualifiers="const"> @@ -147,7 +147,7 @@ </argument> <description> Animates [code]method[/code] of [code]object[/code] from [code]initial_val[/code] to [code]final_val[/code] for [code]duration[/code] seconds, [code]delay[/code] seconds later. Methods are called with consecutive values. - Use [enum TransitionType] for [code]trans_type[/code] and [enum EaseType] for [code]ease_type[/code] parameters. These values control the timing and direction of the interpolation. See the class description for more information + Use [enum TransitionType] for [code]trans_type[/code] and [enum EaseType] for [code]ease_type[/code] parameters. These values control the timing and direction of the interpolation. See the class description for more information </description> </method> <method name="interpolate_property"> @@ -171,7 +171,7 @@ </argument> <description> Animates [code]property[/code] of [code]object[/code] from [code]initial_val[/code] to [code]final_val[/code] for [code]duration[/code] seconds, [code]delay[/code] seconds later. Setting the initial value to [code]null[/code] uses the current value of the property. - Use [enum TransitionType] for [code]trans_type[/code] and [enum EaseType] for [code]ease_type[/code] parameters. These values control the timing and direction of the interpolation. See the class description for more information + Use [enum TransitionType] for [code]trans_type[/code] and [enum EaseType] for [code]ease_type[/code] parameters. These values control the timing and direction of the interpolation. See the class description for more information </description> </method> <method name="is_active" qualifiers="const"> @@ -301,7 +301,7 @@ </argument> <description> Animates [code]method[/code] of [code]object[/code] from the value returned by [code]initial_method[/code] to [code]final_val[/code] for [code]duration[/code] seconds, [code]delay[/code] seconds later. Methods are animated by calling them with consecutive values. - Use [enum TransitionType] for [code]trans_type[/code] and [enum EaseType] for [code]ease_type[/code] parameters. These values control the timing and direction of the interpolation. See the class description for more information + Use [enum TransitionType] for [code]trans_type[/code] and [enum EaseType] for [code]ease_type[/code] parameters. These values control the timing and direction of the interpolation. See the class description for more information </description> </method> <method name="targeting_property"> @@ -327,7 +327,7 @@ </argument> <description> Animates [code]property[/code] of [code]object[/code] from the current value of the [code]initial_val[/code] property of [code]initial[/code] to [code]final_val[/code] for [code]duration[/code] seconds, [code]delay[/code] seconds later. - Use [enum TransitionType] for [code]trans_type[/code] and [enum EaseType] for [code]ease_type[/code] parameters. These values control the timing and direction of the interpolation. See the class description for more information + Use [enum TransitionType] for [code]trans_type[/code] and [enum EaseType] for [code]ease_type[/code] parameters. These values control the timing and direction of the interpolation. See the class description for more information </description> </method> <method name="tell" qualifiers="const"> diff --git a/doc/classes/UndoRedo.xml b/doc/classes/UndoRedo.xml index b9550c17fb..0ea5c6e005 100644 --- a/doc/classes/UndoRedo.xml +++ b/doc/classes/UndoRedo.xml @@ -4,8 +4,29 @@ Helper to manage UndoRedo in the editor or custom tools. </brief_description> <description> - Helper to manage UndoRedo in the editor or custom tools. It works by storing calls to functions in both 'do' an 'undo' lists. + Helper to manage UndoRedo in the editor or custom tools. It works by registering methods and property changes inside 'actions'. Common behavior is to create an action, then add do/undo calls to functions or property changes, then committing the action. + Here's an example on how to add an action to Godot editor's own 'undoredo': + [codeblock] + var undoredo = get_undo_redo() # method of EditorPlugin + + func do_something(): + pass # put your code here + + func undo_something(): + pass # put here the code that reverts what's done by "do_something()" + + func _on_MyButton_pressed(): + var node = get_node("MyNode2D") + undoredo.create_action("Move the node") + undoredo.add_do_method(self, "do_something") + undoredo.add_undo_method(self, "undo_something") + undoredo.add_do_property(node, "position", Vector2(100,100)) + undoredo.add_undo_property(node, "position", node.position) + undoredo.commit_action() + [/codeblock] + [method create_action], [method add_do_method], [method add_undo_method], [method add_do_property], [method add_undo_property], and [method commit_action] should be called one after the other, like in the example. Not doing so could lead to crashes. + If you don't need to register a method you can leave [method add_do_method] and [method add_undo_method] out, and so it goes for properties. You can register more than one method/property. </description> <tutorials> </tutorials> @@ -20,6 +41,7 @@ <argument index="1" name="method" type="String"> </argument> <description> + Register a method that will be called when the action is committed. </description> </method> <method name="add_do_property"> @@ -32,7 +54,7 @@ <argument index="2" name="value" type="Variant"> </argument> <description> - Set a property with a custom value. + Register a property value change for 'do'. </description> </method> <method name="add_do_reference"> @@ -41,7 +63,7 @@ <argument index="0" name="object" type="Object"> </argument> <description> - Add a 'do' reference that will be erased if the 'do' history is lost. This is useful mostly for new nodes created for the 'do' call. Do not use for resources. + Register a reference for 'do' that will be erased if the 'do' history is lost. This is useful mostly for new nodes created for the 'do' call. Do not use for resources. </description> </method> <method name="add_undo_method" qualifiers="vararg"> @@ -52,6 +74,7 @@ <argument index="1" name="method" type="String"> </argument> <description> + Register a method that will be called when the action is undone. </description> </method> <method name="add_undo_property"> @@ -64,7 +87,7 @@ <argument index="2" name="value" type="Variant"> </argument> <description> - Undo setting of a property with a custom value. + Register a property value change for 'undo'. </description> </method> <method name="add_undo_reference"> @@ -73,7 +96,7 @@ <argument index="0" name="object" type="Object"> </argument> <description> - Add an 'undo' reference that will be erased if the 'undo' history is lost. This is useful mostly for nodes removed with the 'do' call (not the 'undo' call!). + Register a reference for 'undo' that will be erased if the 'undo' history is lost. This is useful mostly for nodes removed with the 'do' call (not the 'undo' call!). </description> </method> <method name="clear_history"> @@ -98,7 +121,7 @@ <argument index="1" name="merge_mode" type="int" enum="UndoRedo.MergeMode" default="0"> </argument> <description> - Create a new action. After this is called, do all your calls to [method add_do_method], [method add_undo_method], [method add_do_property] and [method add_undo_property]. + Create a new action. After this is called, do all your calls to [method add_do_method], [method add_undo_method], [method add_do_property], and [method add_undo_property], then commit the action with [method commit_action]. </description> </method> <method name="get_current_action_name" qualifiers="const"> @@ -120,12 +143,14 @@ <return type="bool"> </return> <description> + Redo last action. </description> </method> <method name="undo"> <return type="bool"> </return> <description> + Undo last action. </description> </method> </methods> diff --git a/doc/classes/Vector2.xml b/doc/classes/Vector2.xml index 4bc18b926e..006a843c32 100644 --- a/doc/classes/Vector2.xml +++ b/doc/classes/Vector2.xml @@ -7,7 +7,7 @@ 2-element structure that can be used to represent positions in 2d space or any other pair of numeric values. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/math/index.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/math/index.html</link> </tutorials> <demos> </demos> @@ -109,7 +109,7 @@ <argument index="3" name="t" type="float"> </argument> <description> - Cubicly interpolates between this vector and [code]b[/code] using [code]pre_a[/code] and [code]post_b[/code] as handles, and returns the result at position [code]t[/code]. [code]t[/code] is in the range of [code]0.0 - 1.0[/code], or a percentage of how far along the interpolation is. + Cubicly interpolates between this vector and [code]b[/code] using [code]pre_a[/code] and [code]post_b[/code] as handles, and returns the result at position [code]t[/code]. [code]t[/code] is in the range of [code]0.0 - 1.0[/code], representing the amount of interpolation. </description> </method> <method name="distance_squared_to"> @@ -175,7 +175,7 @@ <argument index="1" name="t" type="float"> </argument> <description> - Returns the result of the linear interpolation between this vector and [code]b[/code] by amount [code]t[/code]. [code]t[/code] is in the range of [code]0.0 - 1.0[/code], a percentage of how far along the interpolation is. + Returns the result of the linear interpolation between this vector and [code]b[/code] by amount [code]t[/code]. [code]t[/code] is in the range of [code]0.0 - 1.0[/code], representing the amount of interpolation. </description> </method> <method name="normalized"> @@ -227,7 +227,7 @@ <argument index="1" name="t" type="float"> </argument> <description> - Returns the result of SLERP between this vector and "b", by amount "t". "t" should be a float of 0.0-1.0, a percentage of how far along the interpolation is. + Returns the result of SLERP between this vector and [code]b[/code], by amount [code]t[/code]. [code]t[/code] is in the range of [code]0.0 - 1.0[/code], representing the amount of interpolation. Both vectors need to be normalized. </description> </method> @@ -259,24 +259,33 @@ </methods> <members> <member name="x" type="float" setter="" getter=""> - The vector's x component. + The vector's x component. Also accessible by using the index position [code][0][/code]. </member> <member name="y" type="float" setter="" getter=""> - The vector's y component. + The vector's y component. Also accessible by using the index position [code][1][/code]. </member> </members> <constants> <constant name="ZERO" value="Vector2( 0, 0 )"> + Zero vector. + </constant> + <constant name="ONE" value="Vector2( 1, 1 )"> + One vector. </constant> <constant name="INF" value="Vector2( inf, inf )"> + Infinite vector. </constant> <constant name="LEFT" value="Vector2( -1, 0 )"> + Left unit vector. </constant> <constant name="RIGHT" value="Vector2( 1, 0 )"> + Right unit vector. </constant> <constant name="UP" value="Vector2( 0, -1 )"> + Up unit vector. </constant> <constant name="DOWN" value="Vector2( 0, 1 )"> + Down unit vector. </constant> </constants> </class> diff --git a/doc/classes/Vector3.xml b/doc/classes/Vector3.xml index b4dcc6c6aa..48856875e7 100644 --- a/doc/classes/Vector3.xml +++ b/doc/classes/Vector3.xml @@ -7,7 +7,7 @@ Vector3 is one of the core classes of the engine, and includes several built-in helper functions to perform basic vector math operations. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/math/index.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/math/index.html</link> </tutorials> <demos> </demos> @@ -78,7 +78,7 @@ <argument index="3" name="t" type="float"> </argument> <description> - Performs a cubic interpolation between vectors [code]pre_a[/code], [code]a[/code], [code]b[/code], [code]post_b[/code] ([code]a[/code] is current), by the given amount (t). (t) should be a float of 0.0-1.0, a percentage of how far along the interpolation is. + Performs a cubic interpolation between vectors [code]pre_a[/code], [code]a[/code], [code]b[/code], [code]post_b[/code] ([code]a[/code] is current), by the given amount [code]t[/code]. [code]t[/code] is in the range of [code]0.0 - 1.0[/code], representing the amount of interpolation. </description> </method> <method name="distance_squared_to"> @@ -151,7 +151,7 @@ <argument index="1" name="t" type="float"> </argument> <description> - Returns the result of the linear interpolation between this vector and [code]b[/code] by amount [code]t[/code]. [code]t[/code] is in the range of [code]0.0 - 1.0[/code], a percentage of how far along the interpolation is. + Returns the result of the linear interpolation between this vector and [code]b[/code] by amount [code]t[/code]. [code]t[/code] is in the range of [code]0.0 - 1.0[/code], representing the amount of interpolation.. </description> </method> <method name="max_axis"> @@ -228,7 +228,7 @@ <argument index="1" name="t" type="float"> </argument> <description> - Returns the result of SLERP between this vector and "b", by amount "t". "t" should be a float of 0.0-1.0, a percentage of how far along the interpolation is. + Returns the result of SLERP between this vector and [code]b[/code], by amount [code]t[/code]. [code]t[/code] is in the range of [code]0.0 - 1.0[/code], representing the amount of interpolation. Both vectors need to be normalized. </description> </method> @@ -260,13 +260,13 @@ </methods> <members> <member name="x" type="float" setter="" getter=""> - The vector's x component. + The vector's x component. Also accessible by using the index position [code][0][/code]. </member> <member name="y" type="float" setter="" getter=""> - The vector's y component. + The vector's y component. Also accessible by using the index position [code][1][/code]. </member> <member name="z" type="float" setter="" getter=""> - The vector's z component. + The vector's z component. Also accessible by using the index position [code][2][/code]. </member> </members> <constants> @@ -280,20 +280,31 @@ Enumerated value for the Z axis. </constant> <constant name="ZERO" value="Vector3( 0, 0, 0 )"> + Zero vector. + </constant> + <constant name="ONE" value="Vector3( 1, 1, 1 )"> + One vector. </constant> <constant name="INF" value="Vector3( inf, inf, inf )"> + Infinite vector. </constant> <constant name="LEFT" value="Vector3( -1, 0, 0 )"> + Left unit vector. </constant> <constant name="RIGHT" value="Vector3( 1, 0, 0 )"> + Right unit vector. </constant> <constant name="UP" value="Vector3( 0, 1, 0 )"> + Up unit vector. </constant> <constant name="DOWN" value="Vector3( 0, -1, 0 )"> + Down unit vector. </constant> <constant name="FORWARD" value="Vector3( 0, 0, -1 )"> + Forward unit vector. </constant> <constant name="BACK" value="Vector3( 0, 0, 1 )"> + Back unit vector. </constant> </constants> </class> diff --git a/doc/classes/VideoStream.xml b/doc/classes/VideoStream.xml index 6bfa48511b..9f2655ed1b 100644 --- a/doc/classes/VideoStream.xml +++ b/doc/classes/VideoStream.xml @@ -1,6 +1,7 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="VideoStream" inherits="Resource" category="Core" version="3.1"> <brief_description> + Base resource for video streams. </brief_description> <description> </description> diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml index 05649193a6..2cdd8c407f 100644 --- a/doc/classes/Viewport.xml +++ b/doc/classes/Viewport.xml @@ -12,8 +12,8 @@ Finally, viewports can also behave as render targets, in which case they will not be visible unless the associated texture is used to draw. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/2d/2d_transforms.html</link> - <link>http://docs.godotengine.org/en/3.0/tutorials/viewports/index.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/2d/2d_transforms.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/viewports/index.html</link> </tutorials> <demos> </demos> @@ -46,6 +46,13 @@ Returns the total transform of the viewport. </description> </method> + <method name="get_modal_stack_top" qualifiers="const"> + <return type="Control"> + </return> + <description> + Returns the topmost modal in the stack. + </description> + </method> <method name="get_mouse_position" qualifiers="const"> <return type="Vector2"> </return> @@ -104,6 +111,12 @@ Returns [code]true[/code] if there are visible modals on-screen. </description> </method> + <method name="gui_is_dragging" qualifiers="const"> + <return type="bool"> + </return> + <description> + </description> + </method> <method name="input"> <return type="void"> </return> diff --git a/doc/classes/ViewportContainer.xml b/doc/classes/ViewportContainer.xml index 8c60fb473d..dde429440d 100644 --- a/doc/classes/ViewportContainer.xml +++ b/doc/classes/ViewportContainer.xml @@ -1,8 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="ViewportContainer" inherits="Container" category="Core" version="3.1"> <brief_description> + Control for holding [Viewport]s. </brief_description> <description> + A [Container] node that holds a [Viewport], automatically setting its size. </description> <tutorials> </tutorials> @@ -12,6 +14,7 @@ </methods> <members> <member name="stretch" type="bool" setter="set_stretch" getter="is_stretch_enabled"> + If [code]true[/code] the viewport will be scaled to the control's size. Default value:[code]false[/code]. </member> <member name="stretch_shrink" type="int" setter="set_stretch_shrink" getter="get_stretch_shrink"> </member> diff --git a/doc/classes/VisibilityEnabler.xml b/doc/classes/VisibilityEnabler.xml index 83ba9495e5..5f0a4ef0f4 100644 --- a/doc/classes/VisibilityEnabler.xml +++ b/doc/classes/VisibilityEnabler.xml @@ -14,8 +14,10 @@ </methods> <members> <member name="freeze_bodies" type="bool" setter="set_enabler" getter="is_enabler_enabled"> + If [code]true[/code] [RigidBody] nodes will be paused. </member> <member name="pause_animations" type="bool" setter="set_enabler" getter="is_enabler_enabled"> + If [code]true[/code] [AnimationPlayer] nodes will be paused. </member> </members> <constants> diff --git a/doc/classes/VisibilityEnabler2D.xml b/doc/classes/VisibilityEnabler2D.xml index 8dfbaec6e8..eab9bd1991 100644 --- a/doc/classes/VisibilityEnabler2D.xml +++ b/doc/classes/VisibilityEnabler2D.xml @@ -14,16 +14,22 @@ </methods> <members> <member name="freeze_bodies" type="bool" setter="set_enabler" getter="is_enabler_enabled"> + If [code]true[/code] [RigidBody2D] nodes will be paused. </member> <member name="pause_animated_sprites" type="bool" setter="set_enabler" getter="is_enabler_enabled"> + If [code]true[/code] [AnimatedSprite] nodes will be paused. </member> <member name="pause_animations" type="bool" setter="set_enabler" getter="is_enabler_enabled"> + If [code]true[/code] [AnimationPlayer] nodes will be paused. </member> <member name="pause_particles" type="bool" setter="set_enabler" getter="is_enabler_enabled"> + If [code]true[/code] [Particles2D] nodes will be paused. </member> <member name="physics_process_parent" type="bool" setter="set_enabler" getter="is_enabler_enabled"> + If [code]true[/code] the parent's [method Node._physics_process] will be stopped. </member> <member name="process_parent" type="bool" setter="set_enabler" getter="is_enabler_enabled"> + If [code]true[/code] the parent's [method Node._process] will be stopped. </member> </members> <constants> diff --git a/doc/classes/VisibilityNotifier.xml b/doc/classes/VisibilityNotifier.xml index 2f22dc99bf..95da708420 100644 --- a/doc/classes/VisibilityNotifier.xml +++ b/doc/classes/VisibilityNotifier.xml @@ -26,14 +26,14 @@ </members> <signals> <signal name="camera_entered"> - <argument index="0" name="camera" type="Object"> + <argument index="0" name="camera" type="Camera"> </argument> <description> Emitted when the VisibilityNotifier enters a [Camera]'s view. </description> </signal> <signal name="camera_exited"> - <argument index="0" name="camera" type="Object"> + <argument index="0" name="camera" type="Camera"> </argument> <description> Emitted when the VisibilityNotifier exits a [Camera]'s view. diff --git a/doc/classes/VisibilityNotifier2D.xml b/doc/classes/VisibilityNotifier2D.xml index b98f2794d2..aaaa9b63bb 100644 --- a/doc/classes/VisibilityNotifier2D.xml +++ b/doc/classes/VisibilityNotifier2D.xml @@ -36,14 +36,14 @@ </description> </signal> <signal name="viewport_entered"> - <argument index="0" name="viewport" type="Object"> + <argument index="0" name="viewport" type="Viewport"> </argument> <description> Emitted when the VisibilityNotifier2D enters a [Viewport]'s view. </description> </signal> <signal name="viewport_exited"> - <argument index="0" name="viewport" type="Object"> + <argument index="0" name="viewport" type="Viewport"> </argument> <description> Emitted when the VisibilityNotifier2D exits a [Viewport]'s view. diff --git a/doc/classes/VisualServer.xml b/doc/classes/VisualServer.xml index 395fb9e829..ed6fdacec7 100644 --- a/doc/classes/VisualServer.xml +++ b/doc/classes/VisualServer.xml @@ -1552,7 +1552,7 @@ <argument index="0" name="feature" type="String"> </argument> <description> - Returns true, if the OS supports a certain feature. Features might be s3tc, etc, etc2 and pvrtc, + Returns [code]true[/code] if the OS supports a certain feature. Features might be s3tc, etc, etc2 and pvrtc, </description> </method> <method name="immediate_begin"> @@ -2185,6 +2185,16 @@ Returns the value of a certain material's parameter. </description> </method> + <method name="material_get_param_default" qualifiers="const"> + <return type="Variant"> + </return> + <argument index="0" name="material" type="RID"> + </argument> + <argument index="1" name="parameter" type="String"> + </argument> + <description> + </description> + </method> <method name="material_get_shader" qualifiers="const"> <return type="RID"> </return> @@ -3110,7 +3120,7 @@ <argument index="2" name="scale" type="bool"> </argument> <description> - Sets a boot image. The color defines the background color and if scale is [code]true[/code], the image will be scaled to fit the screen size. + Sets a boot image. The color defines the background color and if scale is [code]true[/code] the image will be scaled to fit the screen size. </description> </method> <method name="set_debug_generate_wireframes"> @@ -3484,7 +3494,7 @@ <argument index="0" name="shrink" type="bool"> </argument> <description> - If [code]true[/code], sets internal processes to shrink all image data to half the size. + If [code]true[/code] sets internal processes to shrink all image data to half the size. </description> </method> <method name="texture_set_size_override"> @@ -3507,7 +3517,7 @@ <argument index="0" name="enable" type="bool"> </argument> <description> - If [code]true[/code], the image will be stored in the texture's images array if overwritten. + If [code]true[/code] the image will be stored in the texture's images array if overwritten. </description> </method> <method name="viewport_attach_camera"> @@ -3600,10 +3610,10 @@ <argument index="1" name="active" type="bool"> </argument> <description> - If [code]true[/code], sets the viewport active, else sets it inactive. + If [code]true[/code] sets the viewport active, else sets it inactive. </description> </method> - <method name="viewport_set_canvas_layer"> + <method name="viewport_set_canvas_stacking"> <return type="void"> </return> <argument index="0" name="viewport" type="RID"> @@ -3612,8 +3622,11 @@ </argument> <argument index="2" name="layer" type="int"> </argument> + <argument index="3" name="sublayer" type="int"> + </argument> <description> - Sets the renderlayer for a viewport's canvas. + Sets the stacking order for a viewport's canvas. + [code]layer[/code] is the actual canvas layer, while [code]sublayer[/code] specifies the stacking order of the canvas among those in the same layer. </description> </method> <method name="viewport_set_canvas_transform"> @@ -3659,7 +3672,7 @@ <argument index="1" name="disabled" type="bool"> </argument> <description> - If [code]true[/code] a viewport's 3D rendering should be disabled. + If [code]true[/code] a viewport's 3D rendering is disabled. </description> </method> <method name="viewport_set_disable_environment"> @@ -3670,7 +3683,7 @@ <argument index="1" name="disabled" type="bool"> </argument> <description> - If [code]true[/code] rendering of a viewport's environment should be disabled. + If [code]true[/code] rendering of a viewport's environment is disabled. </description> </method> <method name="viewport_set_global_canvas_transform"> @@ -3692,7 +3705,7 @@ <argument index="1" name="enabled" type="bool"> </argument> <description> - If [code]true[/code] the viewport should render to hdr. + If [code]true[/code] the viewport renders to hdr. </description> </method> <method name="viewport_set_hide_canvas"> @@ -3703,7 +3716,7 @@ <argument index="1" name="hidden" type="bool"> </argument> <description> - If [code]true[/code] the viewport's canvas should not be rendered. + If [code]true[/code] the viewport's canvas is not rendered. </description> </method> <method name="viewport_set_hide_scenario"> @@ -3795,7 +3808,7 @@ <argument index="1" name="enabled" type="bool"> </argument> <description> - If [code]true[/code] the viewport should render its background as transparent. + If [code]true[/code] the viewport renders its background as transparent. </description> </method> <method name="viewport_set_update_mode"> @@ -3806,7 +3819,7 @@ <argument index="1" name="update_mode" type="int" enum="VisualServer.ViewportUpdateMode"> </argument> <description> - Sets when the viewport should be updated. See VIEWPORT_UPDATE_MODE_* constants for options. + Sets when the viewport should be updated. See [enum ViewportUpdateMode] constants for options. </description> </method> <method name="viewport_set_usage"> @@ -3817,7 +3830,7 @@ <argument index="1" name="usage" type="int" enum="VisualServer.ViewportUsage"> </argument> <description> - Sets what should be rendered in the viewport. See VIEWPORT_USAGE_* constants for options. + Sets the viewport's 2D/3D mode. See [enum ViewportUsage] constants for options. </description> </method> <method name="viewport_set_use_arvr"> @@ -3828,7 +3841,7 @@ <argument index="1" name="use_arvr" type="bool"> </argument> <description> - If [code]true[/code] the viewport should use augmented or virtual reality technologies. See [ARVRInterface]. + If [code]true[/code] the viewport uses augmented or virtual reality technologies. See [ARVRInterface]. </description> </method> <method name="viewport_set_vflip"> @@ -3839,7 +3852,7 @@ <argument index="1" name="enabled" type="bool"> </argument> <description> - If [code]true[/code] the viewport's rendering should be flipped vertically. + If [code]true[/code] the viewport's rendering is flipped vertically. </description> </method> </methods> @@ -4370,7 +4383,7 @@ </constant> <constant name="ENV_TONE_MAPPER_LINEAR" value="0" enum="EnvironmentToneMapper"> </constant> - <constant name="ENV_TONE_MAPPER_REINHARDT" value="1" enum="EnvironmentToneMapper"> + <constant name="ENV_TONE_MAPPER_REINHARD" value="1" enum="EnvironmentToneMapper"> </constant> <constant name="ENV_TONE_MAPPER_FILMIC" value="2" enum="EnvironmentToneMapper"> </constant> diff --git a/doc/classes/WindowDialog.xml b/doc/classes/WindowDialog.xml index 39487f5020..f7f2d10fcd 100644 --- a/doc/classes/WindowDialog.xml +++ b/doc/classes/WindowDialog.xml @@ -24,7 +24,7 @@ If [code]true[/code] the user can resize the window. Default value: [code]false[/code]. </member> <member name="window_title" type="String" setter="set_title" getter="get_title"> - The text displayed in the window's title bar. Default value: "Save a File". + The text displayed in the window's title bar. </member> </members> <constants> diff --git a/doc/classes/World.xml b/doc/classes/World.xml index 540848e40a..4e954acd48 100644 --- a/doc/classes/World.xml +++ b/doc/classes/World.xml @@ -7,7 +7,7 @@ Class that has everything pertaining to a world. A physics space, a visual scenario and a sound space. Spatial nodes register their resources into the current world. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/physics/ray-casting.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/physics/ray-casting.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/World2D.xml b/doc/classes/World2D.xml index 780cdd181a..9c3d66dade 100644 --- a/doc/classes/World2D.xml +++ b/doc/classes/World2D.xml @@ -7,7 +7,7 @@ Class that has everything pertaining to a 2D world. A physics space, a visual scenario and a sound space. 2D nodes register their resources into the current 2D world. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/physics/ray-casting.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/physics/ray-casting.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/WorldEnvironment.xml b/doc/classes/WorldEnvironment.xml index e68ad2800e..45e1af4e42 100644 --- a/doc/classes/WorldEnvironment.xml +++ b/doc/classes/WorldEnvironment.xml @@ -9,7 +9,7 @@ The [code]WorldEnvironment[/code] allows the user to specify default lighting parameters (e.g. ambient lighting), various post-processing effects (e.g. SSAO, DOF, Tonemapping), and how to draw the background (e.g. solid color, skybox). Usually, these are added in order to improve the realism/color balance of the scene. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/3d/environment_and_post_processing.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/3d/environment_and_post_processing.html</link> </tutorials> <demos> </demos> diff --git a/doc/classes/YSort.xml b/doc/classes/YSort.xml index 12b9cb623f..45ae8645b1 100644 --- a/doc/classes/YSort.xml +++ b/doc/classes/YSort.xml @@ -14,6 +14,7 @@ </methods> <members> <member name="sort_enabled" type="bool" setter="set_sort_enabled" getter="is_sort_enabled"> + If [code]true[/code] child nodes are sorted, otherwise sorting is disabled. Default: [code]true[/code]. </member> </members> <constants> diff --git a/doc/classes/float.xml b/doc/classes/float.xml index ef3c3d72eb..0c5536b5fe 100644 --- a/doc/classes/float.xml +++ b/doc/classes/float.xml @@ -35,7 +35,7 @@ <argument index="0" name="from" type="String"> </argument> <description> - Cast a [String] value to a floating point value. This method accepts float value strings like [code] '1.23' [/code] and exponential notation strings for its parameter so calling [code] float('1e3') [/code] will return 1000.0 and calling [code] float('1e-3') [/code] will return -0.001. + Cast a [String] value to a floating point value. This method accepts float value strings like [code] '1.23' [/code] and exponential notation strings for its parameter so calling [code] float('1e3') [/code] will return 1000.0 and calling [code] float('1e-3') [/code] will return 0.001. </description> </method> </methods> diff --git a/doc/tools/doc_merge.py b/doc/tools/doc_merge.py index 57ac4bdcdd..496d5dcb74 100644 --- a/doc/tools/doc_merge.py +++ b/doc/tools/doc_merge.py @@ -82,7 +82,7 @@ def find_signal_descr(old_class, name): def find_constant_descr(old_class, name): - if (old_class == None): + if (old_class is None): return None constants = old_class.find("constants") if(constants != None and len(list(constants)) > 0): diff --git a/doc/tools/doc_status.py b/doc/tools/doc_status.py index ab74c0b9d6..4bb4342d5f 100644 --- a/doc/tools/doc_status.py +++ b/doc/tools/doc_status.py @@ -229,11 +229,11 @@ class ClassStatus: output['overall'] = (description_progress + items_progress).to_colored_string('{percent}%', '{pad_percent}{s}') if self.name.startswith('Total'): - output['url'] = color('url', 'http://docs.godotengine.org/en/latest/classes/') + output['url'] = color('url', 'https://docs.godotengine.org/en/latest/classes/') if flags['s']: output['comment'] = color('part_good', 'ALL OK') else: - output['url'] = color('url', 'http://docs.godotengine.org/en/latest/classes/class_{name}.html'.format(name=self.name.lower())) + output['url'] = color('url', 'https://docs.godotengine.org/en/latest/classes/class_{name}.html'.format(name=self.name.lower())) if flags['s'] and not flags['g'] and self.is_ok(): output['comment'] = color('part_good', 'ALL OK') diff --git a/doc/tools/makemd.py b/doc/tools/makemd.py deleted file mode 100644 index 056f1ca82d..0000000000 --- a/doc/tools/makemd.py +++ /dev/null @@ -1,360 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import sys -import os.path as path -import os -import xml.etree.ElementTree as ET - -input_list = [] - -for arg in sys.argv[1:]: - if not path.exists(arg): - exit("path {} doesn't exist".format(arg)) - elif path.isdir(arg): - input_list += filter(path.isfile, [path.join(arg, f) for f in os.listdir(arg)]) - else: # assuming is a file - input_list.append(arg) - -if len(input_list) < 1: - print 'usage: makemd.py <classes.xml>' - sys.exit(0) - - -def validate_tag(elem, tag): - if elem.tag != tag: - print "Tag mismatch, expected '" + tag + "', got " + elem.tag - sys.exit(255) - - -class_names = [] -classes = {} - - -def make_class_list(class_list, columns): - - f = open('class_list.md', 'wb') - prev = 0 - col_max = len(class_list) / columns + 1 - col_count = 0 - row_count = 0 - last_initial = '' - fit_columns = [] - - for n in range(0, columns): - fit_columns += [[]] - - indexers = [] - last_initial = '' - - idx = 0 - for n in class_list: - col = idx / col_max - if col >= columns: - col = columns - 1 - fit_columns[col] += [n] - idx += 1 - if n[:1] != last_initial: - indexers += [n] - last_initial = n[:1] - - row_max = 0 - f.write("\n") - - for n in range(0, columns): - if len(fit_columns[n]) > row_max: - row_max = len(fit_columns[n]) - - f.write("| ") - for n in range(0, columns): - f.write(" | |") - - f.write("\n") - f.write("| ") - for n in range(0, columns): - f.write(" --- | ------- |") - f.write("\n") - - for r in range(0, row_max): - s = '| ' - for c in range(0, columns): - if r >= len(fit_columns[c]): - continue - - classname = fit_columns[c][r] - initial = classname[0] - if classname in indexers: - s += '**' + initial + '** | ' - else: - s += ' | ' - - s += '[' + classname + '](class_' + classname.lower() + ') | ' - - s += '\n' - f.write(s) - - f.close() - - -def dokuize_text(txt): - - return txt - - -def dokuize_text(text): - pos = 0 - while True: - pos = text.find('[', pos) - if pos == -1: - break - - endq_pos = text.find(']', pos + 1) - if endq_pos == -1: - break - - pre_text = text[:pos] - post_text = text[endq_pos + 1:] - tag_text = text[pos + 1:endq_pos] - - if tag_text in class_names: - tag_text = make_type(tag_text) - else: - - # command - - cmd = tag_text - space_pos = tag_text.find(' ') - if cmd.find('html') == 0: - cmd = tag_text[:space_pos] - param = tag_text[space_pos + 1:] - tag_text = '<' + param + '>' - elif cmd.find('method') == 0: - cmd = tag_text[:space_pos] - param = tag_text[space_pos + 1:] - - if param.find('.') != -1: - (class_param, method_param) = param.split('.') - tag_text = '[' + class_param + '.' + method_param.replace("_", "_") + '](' + class_param.lower() + '#' \ - + method_param + ')' - else: - tag_text = '[' + param.replace("_", "_") + '](#' + param + ')' - elif cmd.find('image=') == 0: - tag_text = '' - elif cmd.find('url=') == 0: - tag_text = '[' + cmd[4:] + '](' + cmd[4:] - elif cmd == '/url': - tag_text = ')' - elif cmd == 'center': - tag_text = '' - elif cmd == '/center': - tag_text = '' - elif cmd == 'br': - tag_text = '\n' - elif cmd == 'i' or cmd == '/i': - tag_text = '_' - elif cmd == 'b' or cmd == '/b': - tag_text = '**' - elif cmd == 'u' or cmd == '/u': - tag_text = '__' - else: - tag_text = '[' + tag_text + ']' - - text = pre_text + tag_text + post_text - pos = len(pre_text) + len(tag_text) - - # tnode = ET.SubElement(parent,"div") - # tnode.text=text - - return text - - -def make_type(t): - global class_names - if t in class_names: - return '[' + t + '](class_' + t.lower() + ')' - return t - - -def make_method( - f, - name, - m, - declare, - event=False, -): - - s = ' * ' - ret_type = 'void' - args = list(m) - mdata = {} - mdata['argidx'] = [] - for a in args: - if a.tag == 'return': - idx = -1 - elif a.tag == 'argument': - idx = int(a.attrib['index']) - else: - continue - - mdata['argidx'].append(idx) - mdata[idx] = a - - if not event: - if -1 in mdata['argidx']: - s += make_type(mdata[-1].attrib['type']) - else: - s += 'void' - s += ' ' - - if declare: - - # span.attrib["class"]="funcdecl" - # a=ET.SubElement(span,"a") - # a.attrib["name"]=name+"_"+m.attrib["name"] - # a.text=name+"::"+m.attrib["name"] - - s += ' **' + m.attrib['name'].replace("_", "_") + '** ' - else: - s += ' **[' + m.attrib['name'].replace("_", "_") + '](#' + m.attrib['name'] + ')** ' - - s += ' **(**' - argfound = False - for a in mdata['argidx']: - arg = mdata[a] - if a < 0: - continue - if a > 0: - s += ', ' - else: - s += ' ' - - s += make_type(arg.attrib['type']) - if 'name' in arg.attrib: - s += ' ' + arg.attrib['name'] - else: - s += ' arg' + str(a) - - if 'default' in arg.attrib: - s += '=' + arg.attrib['default'] - - argfound = True - - if argfound: - s += ' ' - s += ' **)**' - - if 'qualifiers' in m.attrib: - s += ' ' + m.attrib['qualifiers'] - - f.write(s + '\n') - - -def make_doku_class(node): - - name = node.attrib['name'] - - f = open("class_" + name.lower() + '.md', 'wb') - - f.write('# ' + name + ' \n') - - if 'inherits' in node.attrib: - inh = node.attrib['inherits'].strip() - f.write('####**Inherits:** ' + make_type(inh) + '\n') - if 'category' in node.attrib: - f.write('####**Category:** ' + node.attrib['category'].strip() - + '\n') - - briefd = node.find('brief_description') - if briefd != None: - f.write('\n### Brief Description \n') - f.write(dokuize_text(briefd.text.strip()) + '\n') - - methods = node.find('methods') - - if methods != None and len(list(methods)) > 0: - f.write('\n### Member Functions \n') - for m in list(methods): - make_method(f, node.attrib['name'], m, False) - - events = node.find('signals') - if events != None and len(list(events)) > 0: - f.write('\n### Signals \n') - for m in list(events): - make_method(f, node.attrib['name'], m, True, True) - d = m.find('description') - if d == None or d.text.strip() == '': - continue - f.write('\n') - f.write(dokuize_text(d.text.strip())) - f.write('\n') - - members = node.find('members') - - if members != None and len(list(members)) > 0: - f.write('\n### Member Variables \n') - - for c in list(members): - s = ' * ' - s += make_type(c.attrib['type']) + ' ' - s += '**' + c.attrib['name'] + '**' - if c.text.strip() != '': - s += ' - ' + c.text.strip() - f.write(s + '\n') - - constants = node.find('constants') - if constants != None and len(list(constants)) > 0: - f.write('\n### Numeric Constants \n') - for c in list(constants): - s = ' * ' - s += '**' + c.attrib['name'] + '**' - if 'value' in c.attrib: - s += ' = **' + c.attrib['value'] + '**' - if c.text.strip() != '': - s += ' - ' + c.text.strip() - f.write(s + '\n') - - descr = node.find('description') - if descr != None and descr.text.strip() != '': - f.write('\n### Description \n') - f.write(dokuize_text(descr.text.strip()) + '\n') - - methods = node.find('methods') - - if methods != None and len(list(methods)) > 0: - f.write('\n### Member Function Description \n') - for m in list(methods): - - d = m.find('description') - if d == None or d.text.strip() == '': - continue - f.write('\n#### <a name="' + m.attrib['name'] + '">' + m.attrib['name'] + '</a>\n') - make_method(f, node.attrib['name'], m, True) - f.write('\n') - f.write(dokuize_text(d.text.strip())) - f.write('\n') - - f.close() - - -for file in input_list: - tree = ET.parse(file) - doc = tree.getroot() - - if 'version' not in doc.attrib: - print "Version missing from 'doc'" - sys.exit(255) - - version = doc.attrib['version'] - class_name = doc.attrib['name'] - if class_name in class_names: - continue - class_names.append(class_name) - classes[class_name] = doc - -class_names.sort() - -make_class_list(class_names, 2) - -for cn in class_names: - c = classes[cn] - make_doku_class(c) diff --git a/doc/tools/makerst.py b/doc/tools/makerst.py index 93ad823d42..7b7cc52547 100755 --- a/doc/tools/makerst.py +++ b/doc/tools/makerst.py @@ -309,10 +309,41 @@ def rstize_text(text, cclass): return text +def format_table(f, pp): + longest_t = 0 + longest_s = 0 + for s in pp: + sl = len(s[0]) + if (sl > longest_s): + longest_s = sl + tl = len(s[1]) + if (tl > longest_t): + longest_t = tl + + sep = "+" + for i in range(longest_s + 2): + sep += "-" + sep += "+" + for i in range(longest_t + 2): + sep += "-" + sep += "+\n" + f.write(sep) + for s in pp: + rt = s[0] + while (len(rt) < longest_s): + rt += " " + st = s[1] + while (len(st) < longest_t): + st += " " + f.write("| " + rt + " | " + st + " |\n") + f.write(sep) + f.write('\n') + + def make_type(t): global class_names if t in class_names: - return ':ref:`' + t + '<class_' + t.lower() + '>`' + return ':ref:`' + t + '<class_' + t + '>`' return t @@ -332,26 +363,25 @@ def make_enum(t): c = "@GlobalScope" e = t if c in class_names: - return ':ref:`' + e + '<enum_' + c.lower() + '_' + e.lower() + '>`' + return ':ref:`' + e + '<enum_' + c + '_' + e + '>`' return t def make_method( f, - name, - m, - declare, cname, + method_data, + declare, event=False, pp=None ): - if (declare or pp == None): + if (declare or pp is None): t = '- ' else: t = "" ret_type = 'void' - args = list(m) + args = list(method_data) mdata = {} mdata['argidx'] = [] for a in args: @@ -375,11 +405,11 @@ def make_method( t += 'void' t += ' ' - if declare or pp == None: + if declare or pp is None: - s = '**' + m.attrib['name'] + '** ' + s = '**' + method_data.attrib['name'] + '** ' else: - s = ':ref:`' + m.attrib['name'] + '<class_' + cname + "_" + m.attrib['name'] + '>` ' + s = ':ref:`' + method_data.attrib['name'] + '<class_' + cname + "_" + method_data.attrib['name'] + '>` ' s += '**(**' argfound = False @@ -406,8 +436,8 @@ def make_method( s += ' **)**' - if 'qualifiers' in m.attrib: - s += ' ' + m.attrib['qualifiers'] + if 'qualifiers' in method_data.attrib: + s += ' ' + method_data.attrib['qualifiers'] if (not declare): if (pp != None): @@ -418,6 +448,37 @@ def make_method( f.write(t + s + "\n") +def make_properties( + f, + cname, + prop_data, + description=False, + pp=None +): + t = "" + if 'enum' in prop_data.attrib: + t += make_enum(prop_data.attrib['enum']) + else: + t += make_type(prop_data.attrib['type']) + + if description: + s = '**' + prop_data.attrib['name'] + '**' + setget = [] + if 'setter' in prop_data.attrib and prop_data.attrib['setter'] != '' and not prop_data.attrib['setter'].startswith('_'): + setget.append(("*Setter*", prop_data.attrib['setter'] + '(value)')) + if 'getter' in prop_data.attrib and prop_data.attrib['getter'] != '' and not prop_data.attrib['getter'].startswith('_'): + setget.append(('*Getter*', prop_data.attrib['getter'] + '()')) + else: + s = ':ref:`' + prop_data.attrib['name'] + '<class_' + cname + "_" + prop_data.attrib['name'] + '>`' + + if (pp != None): + pp.append((t, s)) + elif description: + f.write('- ' + t + ' ' + s + '\n\n') + if len(setget) > 0: + format_table(f, setget) + + def make_heading(title, underline): return title + '\n' + underline * len(title) + "\n\n" @@ -435,6 +496,8 @@ def make_rst_class(node): f.write(".. _class_" + name + ":\n\n") f.write(make_heading(name, '=')) + # Inheritance tree + # Ascendents if 'inherits' in node.attrib: inh = node.attrib['inherits'].strip() f.write('**Inherits:** ') @@ -451,16 +514,15 @@ def make_rst_class(node): inh = inode.attrib['inherits'].strip() else: inh = None - f.write("\n\n") + # Descendents inherited = [] for cn in classes: c = classes[cn] if 'inherits' in c.attrib: if (c.attrib['inherits'].strip() == name): inherited.append(c.attrib['name']) - if (len(inherited)): f.write('**Inherited By:** ') for i in range(len(inherited)): @@ -468,83 +530,59 @@ def make_rst_class(node): f.write(", ") f.write(make_type(inherited[i])) f.write("\n\n") + + # Category if 'category' in node.attrib: f.write('**Category:** ' + node.attrib['category'].strip() + "\n\n") + # Brief description f.write(make_heading('Brief Description', '-')) briefd = node.find('brief_description') if briefd != None: f.write(rstize_text(briefd.text.strip(), name) + "\n\n") - methods = node.find('methods') + # Properties overview + members = node.find('members') + if members != None and len(list(members)) > 0: + f.write(make_heading('Properties', '-')) + ml = [] + for m in list(members): + make_properties(f, name, m, False, ml) + format_table(f, ml) + # Methods overview + methods = node.find('methods') if methods != None and len(list(methods)) > 0: - f.write(make_heading('Member Functions', '-')) + f.write(make_heading('Methods', '-')) ml = [] for m in list(methods): - make_method(f, node.attrib['name'], m, False, name, False, ml) - longest_t = 0 - longest_s = 0 - for s in ml: - sl = len(s[0]) - if (sl > longest_s): - longest_s = sl - tl = len(s[1]) - if (tl > longest_t): - longest_t = tl - - sep = "+" - for i in range(longest_s + 2): - sep += "-" - sep += "+" - for i in range(longest_t + 2): - sep += "-" - sep += "+\n" - f.write(sep) - for s in ml: - rt = s[0] - while (len(rt) < longest_s): - rt += " " - st = s[1] - while (len(st) < longest_t): - st += " " - f.write("| " + rt + " | " + st + " |\n") - f.write(sep) - f.write('\n') + make_method(f, name, m, False, False, ml) + format_table(f, ml) + # Theme properties + theme_items = node.find('theme_items') + if theme_items != None and len(list(theme_items)) > 0: + f.write(make_heading('Theme Properties', '-')) + ml = [] + for m in list(theme_items): + make_properties(f, name, m, False, ml) + format_table(f, ml) + + # Signals events = node.find('signals') if events != None and len(list(events)) > 0: f.write(make_heading('Signals', '-')) for m in list(events): f.write(".. _class_" + name + "_" + m.attrib['name'] + ":\n\n") - make_method(f, node.attrib['name'], m, True, name, True) + make_method(f, name, m, True, True) f.write('\n') d = m.find('description') - if d == None or d.text.strip() == '': + if d is None or d.text.strip() == '': continue f.write(rstize_text(d.text.strip(), name)) f.write("\n\n") - f.write('\n') - - members = node.find('members') - if members != None and len(list(members)) > 0: - f.write(make_heading('Member Variables', '-')) - - for c in list(members): - # Leading two spaces necessary to prevent breaking the <ul> - f.write(" .. _class_" + name + "_" + c.attrib['name'] + ":\n\n") - s = '- ' - if 'enum' in c.attrib: - s += make_enum(c.attrib['enum']) + ' ' - else: - s += make_type(c.attrib['type']) + ' ' - s += '**' + c.attrib['name'] + '**' - if c.text.strip() != '': - s += ' - ' + rstize_text(c.text.strip(), name) - f.write(s + '\n\n') - f.write('\n') - + # Constants and enums constants = node.find('constants') consts = [] enum_names = set() @@ -557,23 +595,12 @@ def make_rst_class(node): else: consts.append(c) - if len(consts) > 0: - f.write(make_heading('Numeric Constants', '-')) - for c in list(consts): - s = '- ' - s += '**' + c.attrib['name'] + '**' - if 'value' in c.attrib: - s += ' = **' + c.attrib['value'] + '**' - if c.text.strip() != '': - s += ' --- ' + rstize_text(c.text.strip(), name) - f.write(s + '\n') - f.write('\n') - + # Enums if len(enum_names) > 0: - f.write(make_heading('Enums', '-')) + f.write(make_heading('Enumerations', '-')) for e in enum_names: - f.write(" .. _enum_" + name + "_" + e + ":\n\n") - f.write("enum **" + e + "**\n\n") + f.write(".. _enum_" + name + "_" + e + ":\n\n") + f.write("enum **" + e + "**:\n\n") for c in enums: if c.attrib['enum'] != e: continue @@ -583,15 +610,27 @@ def make_rst_class(node): s += ' = **' + c.attrib['value'] + '**' if c.text.strip() != '': s += ' --- ' + rstize_text(c.text.strip(), name) - f.write(s + '\n') - f.write('\n') - f.write('\n') + f.write(s + '\n\n') + # Constants + if len(consts) > 0: + f.write(make_heading('Constants', '-')) + for c in list(consts): + s = '- ' + s += '**' + c.attrib['name'] + '**' + if 'value' in c.attrib: + s += ' = **' + c.attrib['value'] + '**' + if c.text.strip() != '': + s += ' --- ' + rstize_text(c.text.strip(), name) + f.write(s + '\n\n') + + # Class description descr = node.find('description') if descr != None and descr.text.strip() != '': f.write(make_heading('Description', '-')) f.write(rstize_text(descr.text.strip(), name) + "\n\n") + # Online tutorials global godot_docs_pattern tutorials = node.find('tutorials') if tutorials != None and len(tutorials) > 0: @@ -604,35 +643,43 @@ def make_rst_class(node): if match.lastindex == 2: # Doc reference with fragment identifier: emit direct link to section with reference to page, for example: # `#calling-javascript-from-script in Exporting For Web` - f.write("- `" + groups[1] + " <../" + groups[0] + ".html" + groups[1] + ">`_ in :doc:`../" + groups[0] + "`\n") + f.write("- `" + groups[1] + " <../" + groups[0] + ".html" + groups[1] + ">`_ in :doc:`../" + groups[0] + "`\n\n") # Commented out alternative: Instead just emit: # `Subsection in Exporting For Web` - # f.write("- `Subsection <../" + groups[0] + ".html" + groups[1] + ">`_ in :doc:`../" + groups[0] + "`\n") + # f.write("- `Subsection <../" + groups[0] + ".html" + groups[1] + ">`_ in :doc:`../" + groups[0] + "`\n\n") elif match.lastindex == 1: # Doc reference, for example: # `Math` - f.write("- :doc:`../" + groups[0] + "`\n") + f.write("- :doc:`../" + groups[0] + "`\n\n") else: # External link, for example: # `http://enet.bespin.org/usergroup0.html` - f.write("- `" + link + " <" + link + ">`_\n") - f.write("\n") + f.write("- `" + link + " <" + link + ">`_\n\n") + # Property descriptions + members = node.find('members') + if members != None and len(list(members)) > 0: + f.write(make_heading('Property Descriptions', '-')) + for m in list(members): + f.write(".. _class_" + name + "_" + m.attrib['name'] + ":\n\n") + make_properties(f, name, m, True) + if m.text.strip() != '': + f.write(rstize_text(m.text.strip(), name)) + f.write('\n\n') + + # Method descriptions methods = node.find('methods') if methods != None and len(list(methods)) > 0: - f.write(make_heading('Member Function Description', '-')) + f.write(make_heading('Method Descriptions', '-')) for m in list(methods): f.write(".. _class_" + name + "_" + m.attrib['name'] + ":\n\n") - make_method(f, node.attrib['name'], m, True, name) + make_method(f, name, m, True) f.write('\n') d = m.find('description') - if d == None or d.text.strip() == '': + if d is None or d.text.strip() == '': continue f.write(rstize_text(d.text.strip(), name)) f.write("\n\n") - f.write('\n') - - f.close() file_list = [] diff --git a/drivers/SCsub b/drivers/SCsub index f9cfa3fb05..320d4dc4bb 100644 --- a/drivers/SCsub +++ b/drivers/SCsub @@ -4,9 +4,6 @@ Import('env') env.drivers_sources = [] -if 'builtin_zlib' in env and env['builtin_zlib']: - SConscript("zlib/SCsub") - # OS drivers SConscript('unix/SCsub') SConscript('windows/SCsub') diff --git a/drivers/alsa/SCsub b/drivers/alsa/SCsub index ee39fd2631..28b315ae66 100644 --- a/drivers/alsa/SCsub +++ b/drivers/alsa/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.drivers_sources, "*.cpp") - -Export('env') diff --git a/drivers/alsa/audio_driver_alsa.cpp b/drivers/alsa/audio_driver_alsa.cpp index 1f53d52951..50697b8834 100644 --- a/drivers/alsa/audio_driver_alsa.cpp +++ b/drivers/alsa/audio_driver_alsa.cpp @@ -32,8 +32,8 @@ #ifdef ALSA_ENABLED -#include "os/os.h" -#include "project_settings.h" +#include "core/os/os.h" +#include "core/project_settings.h" #include <errno.h> diff --git a/drivers/alsamidi/SCsub b/drivers/alsamidi/SCsub index 233593b0f9..4c24925192 100644 --- a/drivers/alsamidi/SCsub +++ b/drivers/alsamidi/SCsub @@ -4,5 +4,3 @@ 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 index 599470d7e0..33ad7e3f17 100644 --- a/drivers/alsamidi/alsa_midi.cpp +++ b/drivers/alsamidi/alsa_midi.cpp @@ -30,11 +30,12 @@ #ifdef ALSAMIDI_ENABLED -#include <errno.h> - #include "alsa_midi.h" -#include "os/os.h" -#include "print_string.h" + +#include "core/os/os.h" +#include "core/print_string.h" + +#include <errno.h> static int get_message_size(uint8_t message) { switch (message & 0xF0) { diff --git a/drivers/alsamidi/alsa_midi.h b/drivers/alsamidi/alsa_midi.h index 90e458a365..5741036166 100644 --- a/drivers/alsamidi/alsa_midi.h +++ b/drivers/alsamidi/alsa_midi.h @@ -33,13 +33,13 @@ #ifndef ALSA_MIDI_H #define ALSA_MIDI_H -#include <alsa/asoundlib.h> -#include <stdio.h> - +#include "core/os/midi_driver.h" #include "core/os/mutex.h" #include "core/os/thread.h" #include "core/vector.h" -#include "os/midi_driver.h" + +#include <alsa/asoundlib.h> +#include <stdio.h> class MIDIDriverALSAMidi : public MIDIDriver { diff --git a/drivers/convex_decomp/SCsub b/drivers/convex_decomp/SCsub index f017e55120..65ba5332b7 100644 --- a/drivers/convex_decomp/SCsub +++ b/drivers/convex_decomp/SCsub @@ -11,6 +11,7 @@ thirdparty_sources = [ "b2Triangle.cpp", ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env.add_source_files(env.drivers_sources, thirdparty_sources) -Export('env') +env_thirdparty = env.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(env.drivers_sources, thirdparty_sources) diff --git a/drivers/convex_decomp/b2d_decompose.h b/drivers/convex_decomp/b2d_decompose.h index b21792047e..f6b08b957c 100644 --- a/drivers/convex_decomp/b2d_decompose.h +++ b/drivers/convex_decomp/b2d_decompose.h @@ -31,8 +31,9 @@ #ifndef B2D_DECOMPOSE_H #define B2D_DECOMPOSE_H -#include "vector.h" -#include "vector2.h" +#include "core/math/vector2.h" +#include "core/vector.h" + Vector<Vector<Vector2> > b2d_decompose(const Vector<Vector2> &p_polygon); #endif // B2D_DECOMPOSE_H diff --git a/drivers/coreaudio/SCsub b/drivers/coreaudio/SCsub index 233593b0f9..4c24925192 100644 --- a/drivers/coreaudio/SCsub +++ b/drivers/coreaudio/SCsub @@ -4,5 +4,3 @@ Import('env') # Driver source files env.add_source_files(env.drivers_sources, "*.cpp") - -Export('env') diff --git a/drivers/coreaudio/audio_driver_coreaudio.cpp b/drivers/coreaudio/audio_driver_coreaudio.cpp index 689f1f462d..850b90d59b 100644 --- a/drivers/coreaudio/audio_driver_coreaudio.cpp +++ b/drivers/coreaudio/audio_driver_coreaudio.cpp @@ -31,8 +31,9 @@ #ifdef COREAUDIO_ENABLED #include "audio_driver_coreaudio.h" + +#include "core/os/os.h" #include "core/project_settings.h" -#include "os/os.h" #define kOutputBus 0 #define kInputBus 1 @@ -94,11 +95,6 @@ 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; @@ -122,26 +118,6 @@ 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)); @@ -157,11 +133,6 @@ 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); @@ -174,9 +145,6 @@ Error AudioDriverCoreAudio::init() { 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; print_verbose("CoreAudio: detected " + itos(channels) + " channels"); print_verbose("CoreAudio: audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms"); @@ -188,16 +156,10 @@ 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); - return OK; + return capture_init(); } OSStatus AudioDriverCoreAudio::output_callback(void *inRefCon, @@ -264,7 +226,7 @@ OSStatus AudioDriverCoreAudio::input_callback(void *inRefCon, 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); + OSStatus result = AudioUnitRender(ad->input_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; @@ -276,7 +238,7 @@ OSStatus AudioDriverCoreAudio::input_callback(void *inRefCon, } } } else { - ERR_PRINT(("AudioUnitRender failed, code: " + itos(result)).utf8().get_data()); + ERR_PRINTS("AudioUnitRender failed, code: " + itos(result)); } ad->unlock(); @@ -288,7 +250,7 @@ void AudioDriverCoreAudio::start() { if (!active) { OSStatus result = AudioOutputUnitStart(audio_unit); if (result != noErr) { - ERR_PRINT(("AudioOutputUnitStart failed, code: " + itos(result)).utf8().get_data()); + ERR_PRINTS("AudioOutputUnitStart failed, code: " + itos(result)); } else { active = true; } @@ -299,7 +261,7 @@ void AudioDriverCoreAudio::stop() { if (active) { OSStatus result = AudioOutputUnitStop(audio_unit); if (result != noErr) { - ERR_PRINT(("AudioOutputUnitStop failed, code: " + itos(result)).utf8().get_data()); + ERR_PRINTS("AudioOutputUnitStop failed, code: " + itos(result)); } else { active = false; } @@ -331,70 +293,215 @@ bool AudioDriverCoreAudio::try_lock() { } void AudioDriverCoreAudio::finish() { - OSStatus result; + capture_finish(); - lock(); + if (audio_unit) { + OSStatus result; - 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"); - } + lock(); - if (active) { - result = AudioOutputUnitStop(audio_unit); + 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; + + 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("AudioOutputUnitStop failed"); + ERR_PRINT("AudioComponentInstanceDispose failed"); } - active = false; + audio_unit = NULL; + unlock(); } - result = AudioUnitUninitialize(audio_unit); - if (result != noErr) { - ERR_PRINT("AudioUnitUninitialize failed"); + if (mutex) { + memdelete(mutex); + mutex = NULL; } +} + +Error AudioDriverCoreAudio::capture_init() { + AudioComponentDescription desc; + zeromem(&desc, sizeof(desc)); + desc.componentType = kAudioUnitType_Output; +#ifdef OSX_ENABLED + desc.componentSubType = kAudioUnitSubType_HALOutput; +#else + desc.componentSubType = kAudioUnitSubType_RemoteIO; +#endif + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + + AudioComponent comp = AudioComponentFindNext(NULL, &desc); + ERR_FAIL_COND_V(comp == NULL, FAILED); + + OSStatus result = AudioComponentInstanceNew(comp, &input_unit); + ERR_FAIL_COND_V(result != noErr, FAILED); #ifdef OSX_ENABLED AudioObjectPropertyAddress prop; - prop.mSelector = kAudioHardwarePropertyDefaultOutputDevice; + prop.mSelector = kAudioHardwarePropertyDefaultInputDevice; prop.mScope = kAudioObjectPropertyScopeGlobal; prop.mElement = kAudioObjectPropertyElementMaster; - result = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &prop, &output_device_address_cb, this); - if (result != noErr) { - ERR_PRINT("AudioObjectRemovePropertyListener failed"); - } + result = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &prop, &input_device_address_cb, this); + ERR_FAIL_COND_V(result != noErr, FAILED); #endif - result = AudioComponentInstanceDispose(audio_unit); - if (result != noErr) { - ERR_PRINT("AudioComponentInstanceDispose failed"); + UInt32 flag = 1; + result = AudioUnitSetProperty(input_unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &flag, sizeof(flag)); + ERR_FAIL_COND_V(result != noErr, FAILED); + flag = 0; + result = AudioUnitSetProperty(input_unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &flag, sizeof(flag)); + ERR_FAIL_COND_V(result != noErr, FAILED); + + UInt32 size; +#ifdef OSX_ENABLED + AudioDeviceID deviceId; + size = sizeof(AudioDeviceID); + AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; + + result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property, 0, NULL, &size, &deviceId); + ERR_FAIL_COND_V(result != noErr, FAILED); + + result = AudioUnitSetProperty(input_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceId, sizeof(AudioDeviceID)); + ERR_FAIL_COND_V(result != noErr, FAILED); +#endif + + AudioStreamBasicDescription strdesc; + zeromem(&strdesc, sizeof(strdesc)); + size = sizeof(strdesc); + result = AudioUnitGetProperty(input_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; } - unlock(); + mix_rate = GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE); - if (mutex) { - memdelete(mutex); - mutex = NULL; + zeromem(&strdesc, sizeof(strdesc)); + strdesc.mFormatID = kAudioFormatLinearPCM; + strdesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; + strdesc.mChannelsPerFrame = capture_channels; + strdesc.mSampleRate = mix_rate; + strdesc.mFramesPerPacket = 1; + strdesc.mBitsPerChannel = 16; + strdesc.mBytesPerFrame = strdesc.mBitsPerChannel * strdesc.mChannelsPerFrame / 8; + strdesc.mBytesPerPacket = strdesc.mBytesPerFrame * strdesc.mFramesPerPacket; + + result = AudioUnitSetProperty(input_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &strdesc, sizeof(strdesc)); + ERR_FAIL_COND_V(result != noErr, FAILED); + + AURenderCallbackStruct callback; + zeromem(&callback, sizeof(AURenderCallbackStruct)); + callback.inputProc = &AudioDriverCoreAudio::input_callback; + callback.inputProcRefCon = this; + result = AudioUnitSetProperty(input_unit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, kInputBus, &callback, sizeof(callback)); + ERR_FAIL_COND_V(result != noErr, FAILED); + + result = AudioUnitInitialize(input_unit); + ERR_FAIL_COND_V(result != noErr, FAILED); + + return OK; +} + +void AudioDriverCoreAudio::capture_finish() { + if (input_unit) { + lock(); + + AURenderCallbackStruct callback; + zeromem(&callback, sizeof(AURenderCallbackStruct)); + OSStatus result = AudioUnitSetProperty(input_unit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callback, sizeof(callback)); + if (result != noErr) { + ERR_PRINT("AudioUnitSetProperty failed"); + } + + result = AudioUnitUninitialize(input_unit); + if (result != noErr) { + ERR_PRINT("AudioUnitUninitialize failed"); + } + +#ifdef OSX_ENABLED + AudioObjectPropertyAddress prop; + prop.mSelector = kAudioHardwarePropertyDefaultInputDevice; + prop.mScope = kAudioObjectPropertyScopeGlobal; + prop.mElement = kAudioObjectPropertyElementMaster; + + result = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &prop, &input_device_address_cb, this); + if (result != noErr) { + ERR_PRINT("AudioObjectRemovePropertyListener failed"); + } +#endif + + result = AudioComponentInstanceDispose(input_unit); + if (result != noErr) { + ERR_PRINT("AudioComponentInstanceDispose failed"); + } + + input_unit = NULL; + unlock(); } -}; +} 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); + input_buffer_init(buffer_frames); + + OSStatus result = AudioOutputUnitStart(input_unit); + if (result != noErr) { + ERR_PRINTS("AudioOutputUnitStart failed, code: " + itos(result)); + } 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); + if (input_unit) { + OSStatus result = AudioOutputUnitStop(input_unit); + if (result != noErr) { + ERR_PRINTS("AudioOutputUnitStop failed, code: " + itos(result)); + } + } return OK; } @@ -528,12 +635,14 @@ void AudioDriverCoreAudio::_set_device(const String &device, bool capture) { } if (found) { - OSStatus result = AudioUnitSetProperty(audio_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, capture ? kInputBus : kOutputBus, &deviceId, sizeof(AudioDeviceID)); + OSStatus result = AudioUnitSetProperty(capture ? input_unit : audio_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceId, sizeof(AudioDeviceID)); ERR_FAIL_COND(result != noErr); - // Reset audio input to keep synchronisation. - input_position = 0; - input_size = 0; + if (capture) { + // Reset audio input to keep synchronisation. + input_position = 0; + input_size = 0; + } } } @@ -576,6 +685,8 @@ String AudioDriverCoreAudio::capture_get_device() { #endif AudioDriverCoreAudio::AudioDriverCoreAudio() { + audio_unit = NULL; + input_unit = NULL; active = false; mutex = NULL; diff --git a/drivers/coreaudio/audio_driver_coreaudio.h b/drivers/coreaudio/audio_driver_coreaudio.h index d3f7c8d596..474a9e43ae 100644 --- a/drivers/coreaudio/audio_driver_coreaudio.h +++ b/drivers/coreaudio/audio_driver_coreaudio.h @@ -43,6 +43,7 @@ class AudioDriverCoreAudio : public AudioDriver { AudioComponentInstance audio_unit; + AudioComponentInstance input_unit; bool active; Mutex *mutex; @@ -83,6 +84,9 @@ class AudioDriverCoreAudio : public AudioDriver { UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData); + Error capture_init(); + void capture_finish(); + public: const char *get_name() const { return "CoreAudio"; diff --git a/drivers/coremidi/SCsub b/drivers/coremidi/SCsub index 233593b0f9..4c24925192 100644 --- a/drivers/coremidi/SCsub +++ b/drivers/coremidi/SCsub @@ -4,5 +4,3 @@ 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 index 6d4624e05b..2ebbabaa38 100644 --- a/drivers/coremidi/core_midi.cpp +++ b/drivers/coremidi/core_midi.cpp @@ -31,7 +31,8 @@ #ifdef COREMIDI_ENABLED #include "core_midi.h" -#include "print_string.h" + +#include "core/print_string.h" #include <CoreAudio/HostTime.h> #include <CoreServices/CoreServices.h> @@ -50,13 +51,13 @@ Error MIDIDriverCoreMidi::open() { OSStatus result = MIDIClientCreate(name, NULL, NULL, &client); CFRelease(name); if (result != noErr) { - ERR_PRINTS("MIDIClientCreate failed: " + String(GetMacOSStatusErrorString(result))); + ERR_PRINTS("MIDIClientCreate failed, code: " + itos(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))); + ERR_PRINTS("MIDIInputPortCreate failed, code: " + itos(result)); return ERR_CANT_OPEN; } @@ -64,7 +65,7 @@ Error MIDIDriverCoreMidi::open() { for (int i = 0; i < sources; i++) { MIDIEndpointRef source = MIDIGetSource(i); - if (source != NULL) { + if (source) { MIDIPortConnectSource(port_in, source, (void *)this); connected_sources.insert(i, source); } diff --git a/drivers/coremidi/core_midi.h b/drivers/coremidi/core_midi.h index c6b443764f..ea6b0fcb06 100644 --- a/drivers/coremidi/core_midi.h +++ b/drivers/coremidi/core_midi.h @@ -33,12 +33,11 @@ #ifndef CORE_MIDI_H #define CORE_MIDI_H -#include <stdio.h> +#include "core/os/midi_driver.h" +#include "core/vector.h" #include <CoreMIDI/CoreMIDI.h> - -#include "core/vector.h" -#include "os/midi_driver.h" +#include <stdio.h> class MIDIDriverCoreMidi : public MIDIDriver { diff --git a/drivers/dummy/rasterizer_dummy.h b/drivers/dummy/rasterizer_dummy.h index e39ec915fc..d109ef7b91 100644 --- a/drivers/dummy/rasterizer_dummy.h +++ b/drivers/dummy/rasterizer_dummy.h @@ -27,16 +27,16 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef RASTERIZER_DUMMY_H #define RASTERIZER_DUMMY_H -#include "camera_matrix.h" +#include "core/math/camera_matrix.h" +#include "core/self_list.h" #include "scene/resources/mesh.h" #include "servers/visual/rasterizer.h" #include "servers/visual_server.h" -#include "self_list.h" - class RasterizerSceneDummy : public RasterizerScene { public: /* SHADOW ATLAS API */ @@ -267,6 +267,7 @@ public: void material_set_param(RID p_material, const StringName &p_param, const Variant &p_value) {} Variant material_get_param(RID p_material, const StringName &p_param) const { return Variant(); } + Variant material_get_param_default(RID p_material, const StringName &p_param) const { return Variant(); } void material_set_line_width(RID p_material, float p_width) {} @@ -516,6 +517,7 @@ public: void reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable) {} void reflection_probe_set_enable_shadows(RID p_probe, bool p_enable) {} void reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) {} + void reflection_probe_set_resolution(RID p_probe, int p_resolution) {} AABB reflection_probe_get_aabb(RID p_probe) const { return AABB(); } VS::ReflectionProbeUpdateMode reflection_probe_get_update_mode(RID p_probe) const { return VisualServer::REFLECTION_PROBE_UPDATE_ONCE; } @@ -786,9 +788,14 @@ public: void restore_render_target() {} void clear_render_target(const Color &p_color) {} void blit_render_target_to_screen(RID p_render_target, const Rect2 &p_screen_rect, int p_screen = 0) {} + void output_lens_distorted_to_screen(RID p_render_target, const Rect2 &p_screen_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample) {} void end_frame(bool p_swap_buffers) {} void finalize() {} + static Error is_viable() { + return OK; + } + static Rasterizer *_create_current() { return memnew(RasterizerDummy); } @@ -797,6 +804,8 @@ public: _create_func = _create_current; } + virtual bool is_low_end() const { return true; } + RasterizerDummy() {} ~RasterizerDummy() {} }; diff --git a/drivers/dummy/texture_loader_dummy.cpp b/drivers/dummy/texture_loader_dummy.cpp index b099019d17..8153fbd10b 100644 --- a/drivers/dummy/texture_loader_dummy.cpp +++ b/drivers/dummy/texture_loader_dummy.cpp @@ -29,8 +29,10 @@ /*************************************************************************/ #include "texture_loader_dummy.h" + #include "core/os/file_access.h" -#include "print_string.h" +#include "core/print_string.h" + #include <string.h> RES ResourceFormatDummyTexture::load(const String &p_path, const String &p_original_path, Error *r_error) { diff --git a/drivers/gl_context/SCsub b/drivers/gl_context/SCsub index 4d66a9f9f1..efb26a7908 100644 --- a/drivers/gl_context/SCsub +++ b/drivers/gl_context/SCsub @@ -10,13 +10,14 @@ if (env["platform"] in ["haiku", "osx", "windows", "x11"]): ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env.add_source_files(env.drivers_sources, thirdparty_sources) env.Append(CPPPATH=[thirdparty_dir]) env.Append(CPPFLAGS=['-DGLAD_ENABLED']) env.Append(CPPFLAGS=['-DGLES_OVER_GL']) + env_thirdparty = env.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.drivers_sources, thirdparty_sources) + # Godot source files env.add_source_files(env.drivers_sources, "*.cpp") - -Export('env') diff --git a/drivers/gl_context/context_gl.h b/drivers/gl_context/context_gl.h index 60781a3453..37f334454b 100644 --- a/drivers/gl_context/context_gl.h +++ b/drivers/gl_context/context_gl.h @@ -33,7 +33,7 @@ #if defined(OPENGL_ENABLED) || defined(GLES_ENABLED) -#include "typedefs.h" +#include "core/typedefs.h" /** @author Juan Linietsky <reduzio@gmail.com> diff --git a/drivers/gles2/SCsub b/drivers/gles2/SCsub index 2471dd3739..9923e52c73 100644 --- a/drivers/gles2/SCsub +++ b/drivers/gles2/SCsub @@ -2,6 +2,6 @@ Import('env') -env.add_source_files(env.drivers_sources,"*.cpp") +env.add_source_files(env.drivers_sources, "*.cpp") SConscript("shaders/SCsub") diff --git a/drivers/gles2/rasterizer_canvas_gles2.cpp b/drivers/gles2/rasterizer_canvas_gles2.cpp index 9a9ede761a..9227c04e71 100644 --- a/drivers/gles2/rasterizer_canvas_gles2.cpp +++ b/drivers/gles2/rasterizer_canvas_gles2.cpp @@ -27,11 +27,14 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "rasterizer_canvas_gles2.h" -#include "os/os.h" -#include "project_settings.h" + +#include "core/os/os.h" +#include "core/project_settings.h" #include "rasterizer_scene_gles2.h" #include "servers/visual/visual_server_raster.h" + #ifndef GLES_OVER_GL #define glClearDepth glClearDepthf #endif @@ -562,7 +565,7 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur 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 + // third 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]; @@ -808,8 +811,6 @@ void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, cons bool rebind_shader = true; - Size2 rt_size = Size2(storage->frame.current_rt->width, storage->frame.current_rt->height); - state.current_tex = RID(); state.current_tex_ptr = NULL; state.current_normal = RID(); @@ -1048,6 +1049,43 @@ void RasterizerCanvasGLES2::draw_generic_textured_rect(const Rect2 &p_rect, cons glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } +void RasterizerCanvasGLES2::draw_lens_distortion_rect(const Rect2 &p_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample) { + Vector2 half_size; + if (storage->frame.current_rt) { + half_size = Vector2(storage->frame.current_rt->width, storage->frame.current_rt->height); + } else { + half_size = OS::get_singleton()->get_window_size(); + } + half_size *= 0.5; + Vector2 offset((p_rect.position.x - half_size.x) / half_size.x, (p_rect.position.y - half_size.y) / half_size.y); + Vector2 scale(p_rect.size.x / half_size.x, p_rect.size.y / half_size.y); + + float aspect_ratio = p_rect.size.x / p_rect.size.y; + + // setup our lens shader + state.lens_shader.bind(); + state.lens_shader.set_uniform(LensDistortedShaderGLES2::OFFSET, offset); + state.lens_shader.set_uniform(LensDistortedShaderGLES2::SCALE, scale); + state.lens_shader.set_uniform(LensDistortedShaderGLES2::K1, p_k1); + state.lens_shader.set_uniform(LensDistortedShaderGLES2::K2, p_k2); + state.lens_shader.set_uniform(LensDistortedShaderGLES2::EYE_CENTER, p_eye_center); + state.lens_shader.set_uniform(LensDistortedShaderGLES2::UPSCALE, p_oversample); + state.lens_shader.set_uniform(LensDistortedShaderGLES2::ASPECT_RATIO, aspect_ratio); + + // bind our quad buffer + _bind_quad_buffer(); + + // and draw + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + // and cleanup + glBindBuffer(GL_ARRAY_BUFFER, 0); + + for (int i = 0; i < VS::ARRAY_MAX; i++) { + glDisableVertexAttribArray(i); + } +} + void RasterizerCanvasGLES2::draw_window_margins(int *black_margin, RID *black_image) { } @@ -1073,6 +1111,7 @@ void RasterizerCanvasGLES2::initialize() { // polygon buffer { uint32_t poly_size = GLOBAL_DEF("rendering/limits/buffers/canvas_polygon_buffer_size_kb", 128); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/buffers/canvas_polygon_buffer_size_kb", PropertyInfo(Variant::INT, "rendering/limits/buffers/canvas_polygon_buffer_size_kb", PROPERTY_HINT_RANGE, "0,256,1,or_greater")); poly_size *= 1024; poly_size = MAX(poly_size, (2 + 2 + 4) * 4 * sizeof(float)); glGenBuffers(1, &data.polygon_buffer); @@ -1084,6 +1123,7 @@ void RasterizerCanvasGLES2::initialize() { glBindBuffer(GL_ARRAY_BUFFER, 0); uint32_t index_size = GLOBAL_DEF("rendering/limits/buffers/canvas_polygon_index_size_kb", 128); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/buffers/canvas_polygon_index_size_kb", PropertyInfo(Variant::INT, "rendering/limits/buffers/canvas_polygon_index_size_kb", PROPERTY_HINT_RANGE, "0,256,1,or_greater")); index_size *= 1024; // kb glGenBuffers(1, &data.polygon_index_buffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.polygon_index_buffer); @@ -1147,7 +1187,6 @@ void RasterizerCanvasGLES2::initialize() { _EIDX(1, 1), _EIDX(1, 2), _EIDX(2, 2), _EIDX(2, 2), _EIDX(2, 1), _EIDX(1, 1) }; - ; #undef _EIDX glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elems), elems, GL_STATIC_DRAW); @@ -1160,6 +1199,10 @@ void RasterizerCanvasGLES2::initialize() { state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, true); state.canvas_shader.bind(); + + state.lens_shader.init(); + + state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_PIXEL_SNAP, GLOBAL_DEF("rendering/quality/2d/use_pixel_snap", false)); } void RasterizerCanvasGLES2::finalize() { diff --git a/drivers/gles2/rasterizer_canvas_gles2.h b/drivers/gles2/rasterizer_canvas_gles2.h index cda3ec79e7..cf1c239b6e 100644 --- a/drivers/gles2/rasterizer_canvas_gles2.h +++ b/drivers/gles2/rasterizer_canvas_gles2.h @@ -34,6 +34,7 @@ #include "servers/visual/rasterizer.h" #include "shaders/canvas.glsl.gen.h" +#include "shaders/lens_distorted.glsl.gen.h" // #include "shaders/canvas_shadow.glsl.gen.h" @@ -70,6 +71,7 @@ public: bool canvas_texscreen_used; CanvasShaderGLES2 canvas_shader; // CanvasShadowShaderGLES3 canvas_shadow_shader; + LensDistortedShaderGLES2 lens_shader; bool using_texture_rect; bool using_ninepatch; @@ -117,6 +119,7 @@ public: void _bind_quad_buffer(); void draw_generic_textured_rect(const Rect2 &p_rect, const Rect2 &p_src); + void draw_lens_distortion_rect(const Rect2 &p_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample); void initialize(); void finalize(); diff --git a/drivers/gles2/rasterizer_gles2.cpp b/drivers/gles2/rasterizer_gles2.cpp index c926f2bcc4..175587b1bb 100644 --- a/drivers/gles2/rasterizer_gles2.cpp +++ b/drivers/gles2/rasterizer_gles2.cpp @@ -27,12 +27,12 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "rasterizer_gles2.h" -#include "gl_context/context_gl.h" -#include "os/os.h" -#include "project_settings.h" -#include <string.h> +#include "core/os/os.h" +#include "core/project_settings.h" +#include "drivers/gl_context/context_gl.h" #define _EXT_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242 #define _EXT_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB 0x8243 @@ -64,6 +64,21 @@ #define GLAPIENTRY #endif +#if !defined(GLES_OVER_GL) && !defined(IPHONE_ENABLED) +// Used for debugging on mobile, but not iOS as EGL is not available +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <GLES2/gl2platform.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#endif + +#if defined(MINGW_ENABLED) || defined(_MSC_VER) +#define strcpy strcpy_s +#endif + +#ifndef IPHONE_ENABLED static void GLAPIENTRY _gl_debug_print(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const GLvoid *userParam) { if (type == _EXT_DEBUG_TYPE_OTHER_ARB) @@ -73,6 +88,7 @@ static void GLAPIENTRY _gl_debug_print(GLenum source, GLenum type, GLuint id, GL return; //these are ultimately annoying, so removing for now char debSource[256], debType[256], debSev[256]; + if (source == _EXT_DEBUG_SOURCE_API_ARB) strcpy(debSource, "OpenGL"); else if (source == _EXT_DEBUG_SOURCE_WINDOW_SYSTEM_ARB) @@ -110,6 +126,7 @@ static void GLAPIENTRY _gl_debug_print(GLenum source, GLenum type, GLuint id, GL ERR_PRINTS(output); } +#endif // IPHONE_ENABLED typedef void (*DEBUGPROCARB)(GLenum source, GLenum type, @@ -136,26 +153,21 @@ RasterizerScene *RasterizerGLES2::get_scene() { return scene; } -void RasterizerGLES2::initialize() { - - print_verbose("Using GLES2 video driver"); +Error RasterizerGLES2::is_viable() { #ifdef GLAD_ENABLED if (!gladLoadGL()) { ERR_PRINT("Error initializing GLAD"); + return ERR_UNAVAILABLE; } // GLVersion seems to be used for both GL and GL ES, so we need different version checks for them #ifdef OPENGL_ENABLED // OpenGL 2.1 Profile required - if (GLVersion.major < 2) { -#else // OpenGL ES 3.0 + if (GLVersion.major < 2 || (GLVersion.major == 2 && GLVersion.minor < 1)) { +#else // OpenGL ES 2.0 if (GLVersion.major < 2) { #endif - ERR_PRINT("Your system's graphic drivers seem not to support OpenGL 2.1 / OpenGL ES 2.0, sorry :(\n" - "Try a drivers update, buy a new GPU or try software rendering on Linux; Godot will now crash with a segmentation fault."); - OS::get_singleton()->alert("Your system's graphic drivers seem not to support OpenGL 2.1 / OpenGL ES 2.0, sorry :(\n" - "Godot Engine will self-destruct as soon as you acknowledge this error message.", - "Fatal error: Insufficient OpenGL / GLES driver support"); + return ERR_UNAVAILABLE; } #ifdef GLES_OVER_GL @@ -181,15 +193,22 @@ void RasterizerGLES2::initialize() { glGetFramebufferAttachmentParameteriv = glGetFramebufferAttachmentParameterivEXT; glGenerateMipmap = glGenerateMipmapEXT; } else { - ERR_PRINT("Your system's graphic drivers seem not to support GL_ARB(EXT)_framebuffer_object OpenGL extension, sorry :(\n" - "Try a drivers update, buy a new GPU or try software rendering on Linux; Godot will now crash with a segmentation fault."); - OS::get_singleton()->alert("Your system's graphic drivers seem not to support GL_ARB(EXT)_framebuffer_object OpenGL extension, sorry :(\n" - "Godot Engine will self-destruct as soon as you acknowledge this error message.", - "Fatal error: Insufficient OpenGL / GLES driver support"); + return ERR_UNAVAILABLE; } } -#endif - if (true || OS::get_singleton()->is_stdout_verbose()) { +#endif // GLES_OVER_GL + +#endif // GLAD_ENABLED + + return OK; +} + +void RasterizerGLES2::initialize() { + + print_verbose("Using GLES2 video driver"); + +#ifdef GLAD_ENABLED + if (OS::get_singleton()->is_stdout_verbose()) { if (GLAD_GL_ARB_debug_output) { glEnable(_EXT_DEBUG_OUTPUT_SYNCHRONOUS_ARB); glDebugMessageCallbackARB(_gl_debug_print, NULL); @@ -198,12 +217,11 @@ void RasterizerGLES2::initialize() { print_line("OpenGL debugging not supported!"); } } - #endif // GLAD_ENABLED // For debugging #ifdef GLES_OVER_GL - if (GLAD_GL_ARB_debug_output) { + if (OS::get_singleton()->is_stdout_verbose() && GLAD_GL_ARB_debug_output) { glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_ERROR_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, NULL, GL_TRUE); glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, NULL, GL_TRUE); glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, NULL, GL_TRUE); @@ -216,7 +234,24 @@ void RasterizerGLES2::initialize() { GL_DEBUG_SEVERITY_HIGH_ARB, 5, "hello"); */ } -#endif +#else +#ifndef IPHONE_ENABLED + if (OS::get_singleton()->is_stdout_verbose()) { + DebugMessageCallbackARB callback = (DebugMessageCallbackARB)eglGetProcAddress("glDebugMessageCallback"); + if (!callback) { + callback = (DebugMessageCallbackARB)eglGetProcAddress("glDebugMessageCallbackKHR"); + } + + if (callback) { + + print_line("godot: ENABLING GL DEBUG"); + glEnable(_EXT_DEBUG_OUTPUT_SYNCHRONOUS_ARB); + callback(_gl_debug_print, NULL); + glEnable(_EXT_DEBUG_OUTPUT); + } + } +#endif // !IPHONE_ENABLED +#endif // GLES_OVER_GL const GLubyte *renderer = glGetString(GL_RENDERER); print_line("OpenGL ES 2.0 Renderer: " + String((const char *)renderer)); @@ -320,9 +355,25 @@ void RasterizerGLES2::set_boot_image(const Ref<Image> &p_image, const Color &p_c Rect2 imgrect(0, 0, p_image->get_width(), p_image->get_height()); Rect2 screenrect; + if (p_scale) { - screenrect = imgrect; - screenrect.position += ((Size2(window_w, window_h) - screenrect.size) / 2.0).floor(); + if (window_w > window_h) { + //scale horizontally + screenrect.size.y = window_h; + screenrect.size.x = imgrect.size.x * window_h / imgrect.size.y; + screenrect.position.x = (window_w - screenrect.size.x) / 2; + + } else { + //scale vertically + screenrect.size.x = window_w; + screenrect.size.y = imgrect.size.y * window_w / imgrect.size.x; + screenrect.position.y = (window_h - screenrect.size.y) / 2; + } + } else { + + screenrect = imgrect; + screenrect.position += ((Size2(window_w, window_h) - screenrect.size) / 2.0).floor(); + } RasterizerStorageGLES2::Texture *t = storage->texture_owner.get(texture); glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 1); @@ -363,6 +414,26 @@ void RasterizerGLES2::blit_render_target_to_screen(RID p_render_target, const Re canvas->canvas_end(); } +void RasterizerGLES2::output_lens_distorted_to_screen(RID p_render_target, const Rect2 &p_screen_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample) { + ERR_FAIL_COND(storage->frame.current_rt); + + RasterizerStorageGLES2::RenderTarget *rt = storage->render_target_owner.getornull(p_render_target); + ERR_FAIL_COND(!rt); + + glDisable(GL_BLEND); + + // render to our framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, RasterizerStorageGLES2::system_fbo); + + // output our texture + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, rt->color); + + canvas->draw_lens_distortion_rect(p_screen_rect, p_k1, p_k2, p_eye_center, p_oversample); + + glBindTexture(GL_TEXTURE_2D, 0); +} + void RasterizerGLES2::end_frame(bool p_swap_buffers) { if (OS::get_singleton()->is_layered_allowed()) { diff --git a/drivers/gles2/rasterizer_gles2.h b/drivers/gles2/rasterizer_gles2.h index f727af39dd..97f8ee7c1c 100644 --- a/drivers/gles2/rasterizer_gles2.h +++ b/drivers/gles2/rasterizer_gles2.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef RASTERIZERGLES2_H #define RASTERIZERGLES2_H @@ -58,12 +59,16 @@ public: virtual void restore_render_target(); virtual void clear_render_target(const Color &p_color); virtual void blit_render_target_to_screen(RID p_render_target, const Rect2 &p_screen_rect, int p_screen = 0); + virtual void output_lens_distorted_to_screen(RID p_render_target, const Rect2 &p_screen_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample); virtual void end_frame(bool p_swap_buffers); virtual void finalize(); + static Error is_viable(); static void make_current(); - static void register_config(); + + virtual bool is_low_end() const { return true; } + RasterizerGLES2(); ~RasterizerGLES2(); }; diff --git a/drivers/gles2/rasterizer_scene_gles2.cpp b/drivers/gles2/rasterizer_scene_gles2.cpp index e21998d55e..c4b6c607a2 100644 --- a/drivers/gles2/rasterizer_scene_gles2.cpp +++ b/drivers/gles2/rasterizer_scene_gles2.cpp @@ -27,20 +27,23 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* 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 "core/math/math_funcs.h" +#include "core/math/transform.h" +#include "core/os/os.h" +#include "core/project_settings.h" +#include "core/vmap.h" #include "rasterizer_canvas_gles2.h" #include "servers/visual/visual_server_raster.h" -#include "vmap.h" - #ifndef GLES_OVER_GL #define glClearDepth glClearDepthf #endif +#define _DEPTH_COMPONENT24_OES 0x81A6 + static const GLenum _cube_side_enum[6] = { GL_TEXTURE_CUBE_MAP_NEGATIVE_X, @@ -109,8 +112,8 @@ void RasterizerSceneGLES2::shadow_atlas_set_size(RID p_atlas, int p_size) { glBindTexture(GL_TEXTURE_2D, shadow_atlas->depth); glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 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_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); @@ -436,29 +439,206 @@ void RasterizerSceneGLES2::reflection_atlas_set_subdivision(RID p_ref_atlas, int //////////////////////////////////////////////////// RID RasterizerSceneGLES2::reflection_probe_instance_create(RID p_probe) { - return RID(); + + RasterizerStorageGLES2::ReflectionProbe *probe = storage->reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!probe, RID()); + + ReflectionProbeInstance *rpi = memnew(ReflectionProbeInstance); + + rpi->probe_ptr = probe; + rpi->self = reflection_probe_instance_owner.make_rid(rpi); + rpi->probe = p_probe; + rpi->reflection_atlas_index = -1; + rpi->render_step = -1; + rpi->last_pass = 0; + rpi->current_resolution = 0; + rpi->dirty = true; + + rpi->last_pass = 0; + rpi->index = 0; + + for (int i = 0; i < 6; i++) { + glGenFramebuffers(1, &rpi->fbo[i]); + } + + glGenFramebuffers(1, &rpi->fbo_blur); + glGenRenderbuffers(1, &rpi->depth); + rpi->cubemap = 0; + //glGenTextures(1, &rpi->cubemap); + + return rpi->self; } void RasterizerSceneGLES2::reflection_probe_instance_set_transform(RID p_instance, const Transform &p_transform) { + + ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_instance); + ERR_FAIL_COND(!rpi); + rpi->transform = p_transform; } void RasterizerSceneGLES2::reflection_probe_release_atlas_index(RID p_instance) { } bool RasterizerSceneGLES2::reflection_probe_instance_needs_redraw(RID p_instance) { - return false; + const ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_instance); + ERR_FAIL_COND_V(!rpi, false); + + bool need_redraw = rpi->probe_ptr->resolution != rpi->current_resolution || rpi->dirty || rpi->probe_ptr->update_mode == VS::REFLECTION_PROBE_UPDATE_ALWAYS; + rpi->dirty = false; + return need_redraw; } bool RasterizerSceneGLES2::reflection_probe_instance_has_reflection(RID p_instance) { - return false; + return true; } bool RasterizerSceneGLES2::reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) { - return false; + + ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_instance); + ERR_FAIL_COND_V(!rpi, false); + + rpi->render_step = 0; + + if (rpi->probe_ptr->resolution != rpi->current_resolution) { + + //update cubemap if resolution changed + int size = rpi->probe_ptr->resolution; + rpi->current_resolution = size; + + GLenum internal_format = GL_RGB; + GLenum format = GL_RGB; + GLenum type = GL_UNSIGNED_BYTE; + + glActiveTexture(GL_TEXTURE0); + if (rpi->cubemap != 0) { + glDeleteTextures(1, &rpi->cubemap); + } + glGenTextures(1, &rpi->cubemap); + glBindTexture(GL_TEXTURE_CUBE_MAP, rpi->cubemap); +#if 1 + //Mobile hardware (PowerVR specially) prefers this approach, the other one kills the game + for (int i = 0; i < 6; i++) { + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, internal_format, size, size, 0, format, type, NULL); + } + + glGenerateMipmap(GL_TEXTURE_CUBE_MAP); + + glBindRenderbuffer(GL_RENDERBUFFER, rpi->depth); //resize depth buffer + glRenderbufferStorage(GL_RENDERBUFFER, _DEPTH_COMPONENT24_OES, size, size); + + for (int i = 0; i < 6; i++) { + glBindFramebuffer(GL_FRAMEBUFFER, rpi->fbo[i]); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, _cube_side_enum[i], rpi->cubemap, 0); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rpi->depth); + } + +#else + int lod = 0; + + //the approach below is fatal for powervr + + // Set the initial (empty) mipmaps, all need to be set for this to work in GLES2, even if later wont be used. + while (size >= 1) { + + for (int i = 0; i < 6; i++) { + glTexImage2D(_cube_side_enum[i], lod, internal_format, size, size, 0, format, type, NULL); + if (size == rpi->current_resolution) { + //adjust framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, rpi->fbo[i]); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, _cube_side_enum[i], rpi->cubemap, 0); + glBindRenderbuffer(GL_RENDERBUFFER, rpi->depth); + glRenderbufferStorage(GL_RENDERBUFFER, _DEPTH_COMPONENT24_OES, size, size); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rpi->depth); + +#ifdef DEBUG_ENABLED + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + ERR_CONTINUE(status != GL_FRAMEBUFFER_COMPLETE); +#endif + } + } + + lod++; + + size >>= 1; + } +#endif + 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); + } + + return true; } bool RasterizerSceneGLES2::reflection_probe_instance_postprocess_step(RID p_instance) { - return false; + + ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_instance); + ERR_FAIL_COND_V(!rpi, false); + + int size = rpi->probe_ptr->resolution; + + { + 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); + glDepthMask(GL_FALSE); + + for (int i = 0; i < VS::ARRAY_MAX - 1; i++) { + glDisableVertexAttribArray(i); + } + } + + //vdc cache + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, storage->resources.radical_inverse_vdc_cache_tex); + + glBindFramebuffer(GL_FRAMEBUFFER, rpi->fbo_blur); + // now render to the framebuffer, mipmap level for mipmap level + int lod = 1; + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_CUBE_MAP, rpi->cubemap); + glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //use linear, no mipmaps so it does not read from what is being written to + + size >>= 1; + int mipmaps = 6; + int mm_level = mipmaps - 1; + + storage->shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES2::USE_SOURCE_PANORAMA, false); + storage->shaders.cubemap_filter.bind(); + + //blur + while (size >= 1) { + + for (int i = 0; i < 6; i++) { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, _cube_side_enum[i], rpi->cubemap, lod); + + glViewport(0, 0, size, size); + storage->bind_quad_array(); + storage->shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES2::FACE_ID, i); + float roughness = CLAMP(lod / (float)(mipmaps - 1), 0, 1); + storage->shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES2::ROUGHNESS, roughness); + storage->shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES2::Z_FLIP, false); + + 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); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + return true; } /* ENVIRONMENT API */ @@ -562,20 +742,38 @@ void RasterizerSceneGLES2::environment_set_adjustment(RID p_env, bool p_enable, } 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); + + env->fog_enabled = p_enable; + env->fog_color = p_color; + env->fog_sun_color = p_sun_color; + env->fog_sun_amount = p_sun_amount; } 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); + + env->fog_depth_enabled = p_enable; + env->fog_depth_begin = p_depth_begin; + env->fog_depth_curve = p_depth_curve; + env->fog_transmit_enabled = p_transmit; + env->fog_transmit_curve = p_transmit_curve; } 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); -} + env->fog_height_enabled = p_enable; + env->fog_height_min = p_min_height; + env->fog_height_max = p_max_height; + env->fog_height_curve = p_height_curve; +} bool RasterizerSceneGLES2::is_environment(RID p_env) { return environment_owner.owns(p_env); } @@ -603,6 +801,8 @@ RID RasterizerSceneGLES2::light_instance_create(RID p_light) { light_instance->light = p_light; light_instance->light_ptr = storage->light_owner.getornull(p_light); + light_instance->light_index = 0xFFFF; + ERR_FAIL_COND_V(!light_instance->light_ptr, RID()); light_instance->self = light_instance_owner.make_rid(light_instance); @@ -708,9 +908,39 @@ void RasterizerSceneGLES2::_add_geometry_with_material(RasterizerStorageGLES2::G 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; + bool mirror = p_instance->mirror; + + if (p_material->shader->spatial.cull_mode == RasterizerStorageGLES2::Shader::Spatial::CULL_MODE_DISABLED) { + mirror = false; + } else if (p_material->shader->spatial.cull_mode == RasterizerStorageGLES2::Shader::Spatial::CULL_MODE_FRONT) { + mirror = !mirror; + } + + //if (p_material->shader->spatial.uses_sss) { + // state.used_sss = true; + //} + + if (p_material->shader->spatial.uses_screen_texture) { + state.used_screen_texture = true; + } + + if (p_depth_pass) { + + 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; //bye + + 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 does not use discard and does not write a vertex position, use generic material + if (p_instance->cast_shadows == VS::SHADOW_CASTING_SETTING_DOUBLE_SIDED) { + p_material = storage->material_owner.getptr(!p_shadow_pass && p_material->shader->spatial.uses_world_coordinates ? default_worldcoord_material_twosided : default_material_twosided); + mirror = false; + } else { + p_material = storage->material_owner.getptr(!p_shadow_pass && p_material->shader->spatial.uses_world_coordinates ? default_worldcoord_material : default_material); + } + } + + has_alpha = false; + } RenderList::Element *e = has_alpha ? render_list.add_alpha_element() : render_list.add_element(); @@ -723,46 +953,130 @@ void RasterizerSceneGLES2::_add_geometry_with_material(RasterizerStorageGLES2::G e->instance = p_instance; e->owner = p_owner; e->sort_key = 0; + e->depth_key = 0; + e->use_accum = false; + e->light_index = RenderList::MAX_LIGHTS; + e->use_accum_ptr = &e->use_accum; + e->instancing = (e->instance->base_type == VS::INSTANCE_MULTIMESH) ? 1 : 0; + + if (e->geometry->last_pass != render_pass) { + e->geometry->last_pass = render_pass; + e->geometry->index = current_geometry_index++; + } - // TODO check render pass of geometry - - // TODO check directional light flag + e->geometry_index = e->geometry->index; - if (p_depth_pass) { - // if we are in the depth pass we can sort out a few things to improve performance + if (e->material->last_pass != render_pass) { + e->material->last_pass = render_pass; + e->material->index = current_material_index++; - 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 (e->material->shader->last_pass != render_pass) { + e->material->shader->index = current_shader_index++; } + } + + e->material_index = e->material->index; - 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) { + e->refprobe_0_index = RenderList::MAX_REFLECTION_PROBES; //refprobe disabled by default + e->refprobe_1_index = RenderList::MAX_REFLECTION_PROBES; //refprobe disabled by default - // shader doesn't use discard or writes a custom vertex position, - // so we can use a stripped down shader instead + if (!p_depth_pass) { - // TODO twosided and worldcoord stuff + e->depth_layer = e->instance->depth_layer; + e->priority = p_material->render_priority; - p_material = storage->material_owner.getptr(default_material_twosided); + int rpsize = e->instance->reflection_probe_instances.size(); + if (rpsize > 0) { + bool first = true; + rpsize = MIN(rpsize, 2); //more than 2 per object are not supported, this keeps it stable + + for (int i = 0; i < rpsize; i++) { + ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(e->instance->reflection_probe_instances[i]); + if (rpi->last_pass != render_pass) { + continue; + } + if (first) { + e->refprobe_0_index = rpi->index; + first = false; + } else { + e->refprobe_1_index = rpi->index; + break; + } + } + + /* if (e->refprobe_0_index > e->refprobe_1_index) { //if both are valid, swap them to keep order as best as possible + uint64_t tmp = e->refprobe_0_index; + e->refprobe_0_index = e->refprobe_1_index; + e->refprobe_1_index = tmp; + }*/ } - has_alpha = false; - } + //add directional lights - 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->light_mode = LIGHTMODE_UNSHADED; + } else { - if (p_material->shader->spatial.unshaded) { - e->sort_key |= SORT_KEY_UNSHADED_FLAG; - } + bool copy = false; - if (!p_depth_pass) { - e->sort_key |= uint64_t(e->material->index) << RenderList::SORT_KEY_MATERIAL_INDEX_SHIFT; + for (int i = 0; i < render_directional_lights; i++) { - e->sort_key |= uint64_t(p_material->render_priority + 128) << RenderList::SORT_KEY_PRIORITY_SHIFT; - } else { - // TODO + if (copy) { + RenderList::Element *e2 = has_alpha ? render_list.add_alpha_element() : render_list.add_element(); + if (!e2) { + break; + } + *e2 = *e; //this includes accum ptr :) + e = e2; + } + + //directional sort key + e->light_type1 = 0; + e->light_type2 = 1; + e->light_index = i; + + copy = true; + } + + //add omni / spots + + for (int i = 0; i < e->instance->light_instances.size(); i++) { + + LightInstance *li = light_instance_owner.getornull(e->instance->light_instances[i]); + + if (li->light_index >= render_light_instance_count) { + continue; // too many + } + + if (copy) { + RenderList::Element *e2 = has_alpha ? render_list.add_alpha_element() : render_list.add_element(); + if (!e2) { + break; + } + *e2 = *e; //this includes accum ptr :) + e = e2; + } + + //directional sort key + e->light_type1 = 1; + e->light_type2 = li->light_ptr->type == VisualServer::LIGHT_OMNI ? 0 : 1; + e->light_index = li->light_index; + + copy = true; + } + + if (e->instance->lightmap.is_valid()) { + e->light_mode = LIGHTMODE_LIGHTMAP; + } else if (!e->instance->lightmap_capture_data.empty()) { + e->light_mode = LIGHTMODE_LIGHTMAP_CAPTURE; + } else { + e->light_mode = LIGHTMODE_NORMAL; + } + } } + // do not add anything here, as lights are duplicated elements.. + if (p_material->shader->spatial.uses_time) { VisualServerRaster::redraw_request(); } @@ -770,6 +1084,13 @@ void RasterizerSceneGLES2::_add_geometry_with_material(RasterizerStorageGLES2::G void RasterizerSceneGLES2::_fill_render_list(InstanceBase **p_cull_result, int p_cull_count, bool p_depth_pass, bool p_shadow_pass) { + render_pass++; + current_material_index = 0; + current_geometry_index = 0; + current_light_index = 0; + current_refprobe_index = 0; + current_shader_index = 0; + for (int i = 0; i < p_cull_count; i++) { InstanceBase *instance = p_cull_result[i]; @@ -820,9 +1141,7 @@ void RasterizerSceneGLES2::_fill_render_list(InstanceBase **p_cull_result, int p } break; - default: { - - } break; + default: {} } } } @@ -837,13 +1156,13 @@ static const GLenum gl_primitive[] = { GL_TRIANGLE_FAN }; -void RasterizerSceneGLES2::_setup_material(RasterizerStorageGLES2::Material *p_material, bool p_reverse_cull, bool p_alpha_pass, Size2i p_skeleton_tex_size) { +bool RasterizerSceneGLES2::_setup_material(RasterizerStorageGLES2::Material *p_material, bool p_reverse_cull, bool p_alpha_pass, Size2i p_skeleton_tex_size) { // material parameters state.scene_shader.set_custom_shader(p_material->shader->custom_code_id); - state.scene_shader.bind(); + bool shader_rebind = state.scene_shader.bind(); if (p_material->shader->spatial.no_depth_test) { glDisable(GL_DEPTH_TEST); @@ -922,203 +1241,178 @@ void RasterizerSceneGLES2::_setup_material(RasterizerStorageGLES2::Material *p_m glBindTexture(t->target, t->tex_id); } state.scene_shader.use_material((void *)p_material); + + return shader_rebind; } 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; - - case VS::INSTANCE_IMMEDIATE: { - state.scene_shader.set_conditional(SceneShaderGLES2::USE_INSTANCING, false); - state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_COLOR_INTERP, true); - state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_UV_INTERP, true); - state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_UV2_INTERP, true); - } break; - - default: { - - } break; - } - - if (storage->config.float_texture_supported) { - if (p_skeleton) { - glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 1); - 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); + glBindBuffer(GL_ARRAY_BUFFER, s->vertex_id); - 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. - } + if (s->index_array_len > 0) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s->index_id); + } - // 3 * vec4 per vertex - if (transform_buffer.size() < s->array_len * 12) { - transform_buffer.resize(s->array_len * 12); + 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); + switch (i) { + case VS::ARRAY_NORMAL: { + glVertexAttrib4f(VS::ARRAY_NORMAL, 0.0, 0.0, 1, 1); + } break; + case VS::ARRAY_COLOR: { + glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1); + + } break; + default: {} + } } + } - 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; + bool clear_skeleton_buffer = !storage->config.float_texture_supported; - { - PoolVector<float>::Write write = transform_buffer.write(); - float *buffer = write.ptr(); + if (p_skeleton) { - PoolVector<uint8_t>::Read vertex_array_read = s->data.read(); - const uint8_t *vertex_data = vertex_array_read.ptr(); + if (storage->config.float_texture_supported) { + //use float texture workflow + glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 1); + glBindTexture(GL_TEXTURE_2D, p_skeleton->tex_id); + } else { + //use transform buffer workflow + ERR_FAIL_COND(p_skeleton->use_2d); - for (int i = 0; i < s->array_len; i++) { + PoolVector<float> &transform_buffer = storage->resources.skeleton_transform_cpu_buffer; - // do magic + 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. + } - size_t bones[4]; - float bone_weight[4]; + // 3 * vec4 per vertex + if (transform_buffer.size() < s->array_len * 12) { + transform_buffer.resize(s->array_len * 12); + } - 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]; + 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); + } + + 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)); } + } - 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; + storage->_update_skeleton_transform_buffer(transform_buffer, s->array_len * 12); - 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]; + //enable transform buffer and bind it + glBindBuffer(GL_ARRAY_BUFFER, storage->resources.skeleton_transform_buffer); - 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] }, - }; + glEnableVertexAttribArray(INSTANCE_BONE_BASE + 0); + glEnableVertexAttribArray(INSTANCE_BONE_BASE + 1); + glEnableVertexAttribArray(INSTANCE_BONE_BASE + 2); - size_t transform_buffer_offset = i * 12; + glVertexAttribPointer(INSTANCE_BONE_BASE + 0, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 0)); + glVertexAttribPointer(INSTANCE_BONE_BASE + 1, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 1)); + glVertexAttribPointer(INSTANCE_BONE_BASE + 2, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 2)); - copymem(&buffer[transform_buffer_offset], row, sizeof(row)); - } + clear_skeleton_buffer = false; } + } - storage->_update_skeleton_transform_buffer(transform_buffer, s->array_len * 12); - } break; - - default: { - - } break; - } - } -} - -void RasterizerSceneGLES2::_render_geometry(RenderList::Element *p_element) { + if (clear_skeleton_buffer) { - switch (p_element->instance->base_type) { + glDisableVertexAttribArray(INSTANCE_BONE_BASE + 0); + glDisableVertexAttribArray(INSTANCE_BONE_BASE + 1); + glDisableVertexAttribArray(INSTANCE_BONE_BASE + 2); + } - case VS::INSTANCE_MESH: { + } break; + case VS::INSTANCE_MULTIMESH: { 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) { @@ -1131,61 +1425,58 @@ void RasterizerSceneGLES2::_render_geometry(RenderList::Element *p_element) { 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); + switch (i) { + case VS::ARRAY_NORMAL: { + glVertexAttrib4f(VS::ARRAY_NORMAL, 0.0, 0.0, 1, 1); + } break; + case VS::ARRAY_COLOR: { + glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1); + + } break; + default: {} + } } } - // 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 + // prepare multimesh (disable) + glDisableVertexAttribArray(INSTANCE_ATTRIB_BASE + 0); + glDisableVertexAttribArray(INSTANCE_ATTRIB_BASE + 1); + glDisableVertexAttribArray(INSTANCE_ATTRIB_BASE + 2); + glDisableVertexAttribArray(INSTANCE_ATTRIB_BASE + 3); + glDisableVertexAttribArray(INSTANCE_ATTRIB_BASE + 4); + glDisableVertexAttribArray(INSTANCE_BONE_BASE + 0); + glDisableVertexAttribArray(INSTANCE_BONE_BASE + 1); + glDisableVertexAttribArray(INSTANCE_BONE_BASE + 2); - for (int i = 0; i < VS::ARRAY_MAX - 1; i++) { - glDisableVertexAttribArray(i); - } - - if (s->index_array_len > 0) { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - } + } break; - 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); + case VS::INSTANCE_IMMEDIATE: { + } break; - glDisableVertexAttribArray(VS::ARRAY_MAX + 0); - glDisableVertexAttribArray(VS::ARRAY_MAX + 1); - glDisableVertexAttribArray(VS::ARRAY_MAX + 2); - } + default: {} + } +} - glBindBuffer(GL_ARRAY_BUFFER, 0); +void RasterizerSceneGLES2::_render_geometry(RenderList::Element *p_element) { - } break; + switch (p_element->instance->base_type) { - case VS::INSTANCE_MULTIMESH: { + case VS::INSTANCE_MESH: { - 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; - } + // 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); + } + /* if (p_element->instance->skeleton.is_valid() && s->attribs[VS::ARRAY_BONES].enabled && s->attribs[VS::ARRAY_WEIGHTS].enabled) { + //clean up after skeleton 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); @@ -1194,37 +1485,20 @@ void RasterizerSceneGLES2::_render_geometry(RenderList::Element *p_element) { glVertexAttrib4f(VS::ARRAY_MAX + 1, 0, 1, 0, 0); glVertexAttrib4f(VS::ARRAY_MAX + 2, 0, 0, 1, 0); } +*/ + } break; - 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); - } - } + case VS::INSTANCE_MULTIMESH: { - glDisableVertexAttribArray(12); // transform 0 - glDisableVertexAttribArray(13); // transform 1 - glDisableVertexAttribArray(14); // transform 2 - glDisableVertexAttribArray(15); // color - glDisableVertexAttribArray(8); // custom data + RasterizerStorageGLES2::MultiMesh *multi_mesh = static_cast<RasterizerStorageGLES2::MultiMesh *>(p_element->owner); + RasterizerStorageGLES2::Surface *s = static_cast<RasterizerStorageGLES2::Surface *>(p_element->geometry); - if (!s->attribs[VS::ARRAY_COLOR].enabled) { - glDisableVertexAttribArray(VS::ARRAY_COLOR); + int amount = MIN(multi_mesh->size, multi_mesh->visible_instances); - glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1); + if (amount == -1) { + amount = multi_mesh->size; } - 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; @@ -1232,49 +1506,34 @@ void RasterizerSceneGLES2::_render_geometry(RenderList::Element *p_element) { // drawing + const float *base_buffer = multi_mesh->data.ptr(); + for (int i = 0; i < amount; i++) { - float *buffer = &multi_mesh->data.write[i * stride]; + const float *buffer = base_buffer + 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]); + + glVertexAttrib4fv(INSTANCE_ATTRIB_BASE + 0, &buffer[0]); + glVertexAttrib4fv(INSTANCE_ATTRIB_BASE + 1, &buffer[4]); + glVertexAttrib4fv(INSTANCE_ATTRIB_BASE + 2, &buffer[8]); } if (multi_mesh->color_floats) { if (multi_mesh->color_format == VS::MULTIMESH_COLOR_8BIT) { uint8_t *color_data = (uint8_t *)(buffer + color_ofs); - glVertexAttrib4f(15, color_data[0] / 255.0, color_data[1] / 255.0, color_data[2] / 255.0, color_data[3] / 255.0); + glVertexAttrib4f(INSTANCE_ATTRIB_BASE + 3, color_data[0] / 255.0, color_data[1] / 255.0, color_data[2] / 255.0, color_data[3] / 255.0); } else { - glVertexAttrib4fv(15, buffer + color_ofs); + glVertexAttrib4fv(INSTANCE_ATTRIB_BASE + 3, buffer + color_ofs); } } if (multi_mesh->custom_data_floats) { - glVertexAttrib4fv(8, buffer + custom_data_ofs); + if (multi_mesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_8BIT) { + uint8_t *custom_data = (uint8_t *)(buffer + custom_data_ofs); + glVertexAttrib4f(INSTANCE_ATTRIB_BASE + 4, custom_data[0] / 255.0, custom_data[1] / 255.0, custom_data[2] / 255.0, custom_data[3] / 255.0); + } else { + glVertexAttrib4fv(INSTANCE_ATTRIB_BASE + 4, buffer + custom_data_ofs); + } } if (s->index_array_len > 0) { @@ -1284,25 +1543,6 @@ void RasterizerSceneGLES2::_render_geometry(RenderList::Element *p_element) { } } - // 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_IMMEDIATE: { @@ -1416,508 +1656,731 @@ void RasterizerSceneGLES2::_render_geometry(RenderList::Element *p_element) { } } break; + default: {} } } -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); +void RasterizerSceneGLES2::_setup_light_type(LightInstance *p_light, ShadowAtlas *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; + //turn off all by default + state.scene_shader.set_conditional(SceneShaderGLES2::USE_LIGHTING, false); + state.scene_shader.set_conditional(SceneShaderGLES2::USE_SHADOW, false); + state.scene_shader.set_conditional(SceneShaderGLES2::SHADOW_MODE_PCF_5, false); + state.scene_shader.set_conditional(SceneShaderGLES2::SHADOW_MODE_PCF_13, false); + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_MODE_DIRECTIONAL, false); + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_MODE_OMNI, false); + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_MODE_SPOT, false); + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM2, false); + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM4, false); + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM_BLEND, false); + state.scene_shader.set_conditional(SceneShaderGLES2::USE_SHADOW, false); - bool use_radiance_map = false; + if (!p_light) { //no light, return off + return; + } - VMap<RID, Vector<RenderList::Element *> > lit_objects; + //turn on lighting + state.scene_shader.set_conditional(SceneShaderGLES2::USE_LIGHTING, true); - for (int i = 0; i < p_element_count; i++) { - RenderList::Element *e = p_elements[i]; + switch (p_light->light_ptr->type) { + case VS::LIGHT_DIRECTIONAL: { - RasterizerStorageGLES2::Material *material = e->material; + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_MODE_DIRECTIONAL, true); + switch (p_light->light_ptr->directional_shadow_mode) { + case VS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL: { + //no need + } break; + case VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS: { + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM2, true); - RasterizerStorageGLES2::Skeleton *skeleton = storage->skeleton_owner.getornull(e->instance->skeleton); + } break; + case VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS: { + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM4, true); + } break; + } - 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); + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM_BLEND, p_light->light_ptr->directional_blend_splits); + if (!state.render_no_shadows && p_light->light_ptr->shadow) { + state.scene_shader.set_conditional(SceneShaderGLES2::USE_SHADOW, true); + glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 3); + glBindTexture(GL_TEXTURE_2D, directional_shadow.depth); + state.scene_shader.set_conditional(SceneShaderGLES2::SHADOW_MODE_PCF_5, shadow_filter_mode == SHADOW_FILTER_PCF5); + state.scene_shader.set_conditional(SceneShaderGLES2::SHADOW_MODE_PCF_13, shadow_filter_mode == SHADOW_FILTER_PCF13); + } - 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); - } + } break; + case VS::LIGHT_OMNI: { + + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_MODE_OMNI, true); + if (!state.render_no_shadows && shadow_atlas && p_light->light_ptr->shadow) { + state.scene_shader.set_conditional(SceneShaderGLES2::USE_SHADOW, true); + glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 3); + glBindTexture(GL_TEXTURE_2D, shadow_atlas->depth); + state.scene_shader.set_conditional(SceneShaderGLES2::SHADOW_MODE_PCF_5, shadow_filter_mode == SHADOW_FILTER_PCF5); + state.scene_shader.set_conditional(SceneShaderGLES2::SHADOW_MODE_PCF_13, shadow_filter_mode == SHADOW_FILTER_PCF13); + } + } break; + case VS::LIGHT_SPOT: { + + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_MODE_SPOT, true); + if (!state.render_no_shadows && shadow_atlas && p_light->light_ptr->shadow) { + state.scene_shader.set_conditional(SceneShaderGLES2::USE_SHADOW, true); + glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 3); + glBindTexture(GL_TEXTURE_2D, shadow_atlas->depth); + state.scene_shader.set_conditional(SceneShaderGLES2::SHADOW_MODE_PCF_5, shadow_filter_mode == SHADOW_FILTER_PCF5); + state.scene_shader.set_conditional(SceneShaderGLES2::SHADOW_MODE_PCF_13, shadow_filter_mode == SHADOW_FILTER_PCF13); + } + } break; + } +} - // opaque pass +void RasterizerSceneGLES2::_setup_light(LightInstance *light, ShadowAtlas *shadow_atlas, const Transform &p_view_transform) { - state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_PASS, false); + RasterizerStorageGLES2::Light *light_ptr = light->light_ptr; - _setup_geometry(e, skeleton); + //common parameters + float energy = light_ptr->param[VS::LIGHT_PARAM_ENERGY]; + float specular = light_ptr->param[VS::LIGHT_PARAM_SPECULAR]; + float sign = light_ptr->negative ? -1 : 1; - _setup_material(material, p_reverse_cull, p_alpha_pass, Size2i(skeleton ? skeleton->size * 3 : 0, 0)); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SPECULAR, specular); + Color color = light_ptr->color * sign * energy * Math_PI; + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_COLOR, color); - if (use_radiance_map) { - state.scene_shader.set_uniform(SceneShaderGLES2::RADIANCE_INVERSE_XFORM, p_view_transform); - } + //specific parameters - 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); - } + switch (light_ptr->type) { + case VS::LIGHT_DIRECTIONAL: { + //not using inverse for performance, view should be normalized anyway + Vector3 direction = p_view_transform.basis.xform_inv(light->transform.basis.xform(Vector3(0, 0, -1))).normalized(); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_DIRECTION, direction); - 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); + CameraMatrix matrices[4]; - } 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); - } + if (!state.render_no_shadows && light_ptr->shadow && directional_shadow.depth) { - glEnable(GL_BLEND); + int shadow_count = 0; + Color split_offsets; - 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 (light_ptr->directional_shadow_mode) { + case VS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL: { + shadow_count = 1; + } break; - switch (desired_blend_mode) { + case VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS: { + shadow_count = 2; + } break; - 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); - } + case VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS: { + shadow_count = 4; + } break; + } - } break; - case RasterizerStorageGLES2::Shader::Spatial::BLEND_MODE_ADD: { + for (int k = 0; k < shadow_count; k++) { - glBlendEquation(GL_FUNC_ADD); - glBlendFunc(p_alpha_pass ? GL_SRC_ALPHA : GL_ONE, GL_ONE); + 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; - } break; - case RasterizerStorageGLES2::Shader::Spatial::BLEND_MODE_SUB: { + if (light_ptr->directional_shadow_mode == VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS) { - 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); - } + width /= 2; + height /= 2; - } 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); - } - } + if (k == 0) { - 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()); + } else if (k == 1) { + x += width; + } else if (k == 2) { + y += height; + } else if (k == 3) { + x += width; + y += height; + } - state.scene_shader.set_uniform(SceneShaderGLES2::TIME, storage->frame.time[0]); + } else if (light_ptr->directional_shadow_mode == VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS) { - 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); + height /= 2; - _render_geometry(e); + if (k == 0) { - if (material->shader->spatial.unshaded) - continue; + } else { + y += height; + } + } - if (p_shadow) - continue; + split_offsets[k] = light->shadow_transform[k].split; - for (int light = 0; light < e->instance->light_instances.size(); light++) { + Transform modelview = (p_view_transform.inverse() * light->shadow_transform[k].transform).affine_inverse(); - RID light_instance = e->instance->light_instances[light]; + 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); - lit_objects[light_instance].push_back(e); - } - } + CameraMatrix shadow_mtx = rectm * bias * light->shadow_transform[k].camera * modelview; + matrices[k] = shadow_mtx; - 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; - } + /*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_conditional(SceneShaderGLES2::LIGHT_PASS, true); + // state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_CLAMP, light_clamp); + state.scene_shader.set_uniform(SceneShaderGLES2::SHADOW_PIXEL_SIZE, Size2(1.0 / directional_shadow.size, 1.0 / directional_shadow.size)); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SPLIT_OFFSETS, split_offsets); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SHADOW_MATRIX, 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]); + } + } break; + case VS::LIGHT_OMNI: { - glEnable(GL_BLEND); - glBlendEquation(GL_FUNC_ADD); - glBlendFunc(GL_SRC_ALPHA, GL_ONE); + Vector3 position = p_view_transform.xform_inv(light->transform.origin); - for (int lo = 0; lo < lit_objects.size(); lo++) { + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_POSITION, position); - RID key = lit_objects.getk(lo); + float range = light_ptr->param[VS::LIGHT_PARAM_RANGE]; + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_RANGE, range); - LightInstance *light = light_instance_owner.getornull(key); - RasterizerStorageGLES2::Light *light_ptr = light->light_ptr; + float attenuation = light_ptr->param[VS::LIGHT_PARAM_ATTENUATION]; + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_ATTENUATION, attenuation); - const Vector<RenderList::Element *> &list = lit_objects.getv(lo); + if (!state.render_no_shadows && light_ptr->shadow && shadow_atlas && shadow_atlas->shadow_owners.has(light->self)) { - for (int i = 0; i < list.size(); i++) { + uint32_t key = shadow_atlas->shadow_owners[light->self]; - RenderList::Element *e = list[i]; - RasterizerStorageGLES2::Material *material = e->material; + uint32_t quadrant = (key >> ShadowAtlas::QUADRANT_SHIFT) & 0x03; + uint32_t shadow = key & ShadowAtlas::SHADOW_INDEX_MASK; - RasterizerStorageGLES2::Skeleton *skeleton = storage->skeleton_owner.getornull(e->instance->skeleton); + ERR_BREAK(shadow >= (uint32_t)shadow_atlas->quadrants[quadrant].shadows.size()); - { - _setup_geometry(e, skeleton); + uint32_t atlas_size = shadow_atlas->size; + uint32_t quadrant_size = atlas_size >> 1; - _setup_material(material, p_reverse_cull, p_alpha_pass, 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); - } + uint32_t x = (quadrant & 1) * quadrant_size; + uint32_t y = (quadrant >> 1) * quadrant_size; - 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()); + 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; - state.scene_shader.set_uniform(SceneShaderGLES2::TIME, storage->frame.time[0]); + uint32_t width = shadow_size; + uint32_t height = shadow_size; - 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); - } + if (light->light_ptr->omni_shadow_detail == VS::LIGHT_OMNI_SHADOW_DETAIL_HORIZONTAL) { + height /= 2; + } else { + width /= 2; + } - switch (light_ptr->type) { - case VS::LIGHT_OMNI: { + Transform proj = (p_view_transform.inverse() * light->transform).inverse(); - state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_TYPE, (int)1); + 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; - Vector3 position = p_view_transform.inverse().xform(light->transform.origin); + state.scene_shader.set_uniform(SceneShaderGLES2::SHADOW_PIXEL_SIZE, Size2(1.0 / shadow_atlas->size, 1.0 / shadow_atlas->size)); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SHADOW_MATRIX, proj); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_CLAMP, light_clamp); + } + } break; - state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_POSITION, position); + case VS::LIGHT_SPOT: { - float range = light_ptr->param[VS::LIGHT_PARAM_RANGE]; - state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_RANGE, range); + Vector3 position = p_view_transform.xform_inv(light->transform.origin); - 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); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_POSITION, position); - if (light_ptr->shadow && shadow_atlas->shadow_owners.has(light->self)) { + 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 attenuation = 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); - uint32_t key = shadow_atlas->shadow_owners[light->self]; + if (!state.render_no_shadows && 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; + 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()); + ERR_BREAK(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 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 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 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; + 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; - } + Rect2 rect(float(x) / atlas_size, float(y) / atlas_size, float(width) / atlas_size, float(height) / atlas_size); - Transform proj = (p_view_transform.inverse() * light->transform).inverse(); + 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; - 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; + Transform modelview = (p_view_transform.inverse() * light->transform).inverse(); - state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SHADOW_MATRIX, proj); - state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_CLAMP, light_clamp); + CameraMatrix bias; + bias.set_light_bias(); - state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_HAS_SHADOW, 1.0); - } else { - state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_HAS_SHADOW, 0.0); - } - } break; + CameraMatrix rectm; + rectm.set_light_atlas_rect(rect); - 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); + CameraMatrix shadow_matrix = rectm * bias * light->shadow_transform[0].camera * modelview; - 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); + state.scene_shader.set_uniform(SceneShaderGLES2::SHADOW_PIXEL_SIZE, Size2(1.0 / shadow_atlas->size, 1.0 / shadow_atlas->size)); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SHADOW_MATRIX, shadow_matrix); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_CLAMP, light_clamp); + } - if (light->light_ptr->shadow && shadow_atlas && shadow_atlas->shadow_owners.has(light->self)) { - uint32_t key = shadow_atlas->shadow_owners[light->self]; + } break; + default: {} + } +} - uint32_t quadrant = (key >> ShadowAtlas::QUADRANT_SHIFT) & 0x03; - uint32_t shadow = key & ShadowAtlas::SHADOW_INDEX_MASK; +void RasterizerSceneGLES2::_setup_refprobes(ReflectionProbeInstance *p_refprobe1, ReflectionProbeInstance *p_refprobe2, const Transform &p_view_transform, Environment *p_env) { - ERR_CONTINUE(shadow >= (uint32_t)shadow_atlas->quadrants[quadrant].shadows.size()); + if (p_refprobe1) { + state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE1_USE_BOX_PROJECT, p_refprobe1->probe_ptr->box_projection); + state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE1_BOX_EXTENTS, p_refprobe1->probe_ptr->extents); + state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE1_BOX_OFFSET, p_refprobe1->probe_ptr->origin_offset); + state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE1_EXTERIOR, !p_refprobe1->probe_ptr->interior); + state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE1_INTENSITY, p_refprobe1->probe_ptr->intensity); - uint32_t atlas_size = shadow_atlas->size; - uint32_t quadrant_size = atlas_size >> 1; + Color ambient; + if (p_refprobe1->probe_ptr->interior) { + ambient = p_refprobe1->probe_ptr->interior_ambient * p_refprobe1->probe_ptr->interior_ambient_energy; + ambient.a = p_refprobe1->probe_ptr->interior_ambient_probe_contrib; + } else if (p_env) { + ambient = p_env->ambient_color * p_env->ambient_energy; + ambient.a = p_env->ambient_sky_contribution; + } - uint32_t x = (quadrant & 1) * quadrant_size; - uint32_t y = (quadrant >> 1) * quadrant_size; + state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE1_AMBIENT, ambient); - 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; + Transform proj = (p_view_transform.inverse() * p_refprobe1->transform).affine_inverse(); - uint32_t width = shadow_size; - uint32_t height = shadow_size; + state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE1_LOCAL_MATRIX, proj); + } - Rect2 rect(float(x) / atlas_size, float(y) / atlas_size, float(width) / atlas_size, float(height) / atlas_size); + if (p_refprobe2) { + state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE2_USE_BOX_PROJECT, p_refprobe2->probe_ptr->box_projection); + state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE2_BOX_EXTENTS, p_refprobe2->probe_ptr->extents); + state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE2_BOX_OFFSET, p_refprobe2->probe_ptr->origin_offset); + state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE2_EXTERIOR, !p_refprobe2->probe_ptr->interior); + state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE2_INTENSITY, p_refprobe2->probe_ptr->intensity); + + Color ambient; + if (p_refprobe2->probe_ptr->interior) { + ambient = p_refprobe2->probe_ptr->interior_ambient * p_refprobe2->probe_ptr->interior_ambient_energy; + ambient.a = p_refprobe2->probe_ptr->interior_ambient_probe_contrib; + } else if (p_env) { + ambient = p_env->ambient_color * p_env->ambient_energy; + ambient.a = p_env->ambient_sky_contribution; + } - 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; + state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE2_AMBIENT, ambient); - Transform modelview = (p_view_transform.inverse() * light->transform).inverse(); + Transform proj = (p_view_transform.inverse() * p_refprobe2->transform).affine_inverse(); - CameraMatrix bias; - bias.set_light_bias(); + state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE2_LOCAL_MATRIX, proj); + } +} - CameraMatrix rectm; - rectm.set_light_atlas_rect(rect); +void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements, int p_element_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) { - CameraMatrix shadow_matrix = rectm * bias * light->shadow_transform[0].camera * modelview; + ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_shadow_atlas); - 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); + Vector2 screen_pixel_size = state.screen_pixel_size; - } else { - state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_HAS_SHADOW, 0.0); - } + bool use_radiance_map = false; + if (!p_shadow && 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, true); //since prev unshaded is false, this needs to be true if exists + } - } break; + bool prev_unshaded = false; + bool prev_instancing = false; + state.scene_shader.set_conditional(SceneShaderGLES2::SHADELESS, false); + RasterizerStorageGLES2::Material *prev_material = NULL; + RasterizerStorageGLES2::Geometry *prev_geometry = NULL; + RasterizerStorageGLES2::Skeleton *prev_skeleton = NULL; + RasterizerStorageGLES2::GeometryOwner *prev_owner = NULL; - default: break; - } + Transform view_transform_inverse = p_view_transform.inverse(); + CameraMatrix projection_inverse = p_projection.inverse(); - float energy = light->light_ptr->param[VS::LIGHT_PARAM_ENERGY]; - float specular = light->light_ptr->param[VS::LIGHT_PARAM_SPECULAR]; + bool prev_base_pass = false; + LightInstance *prev_light = NULL; + bool prev_vertex_lit = false; + ReflectionProbeInstance *prev_refprobe_1 = NULL; + ReflectionProbeInstance *prev_refprobe_2 = NULL; - 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); + int prev_blend_mode = -2; //will always catch the first go - _render_geometry(e); - } + if (p_alpha_pass) { + glEnable(GL_BLEND); + } else { + glDisable(GL_BLEND); } - 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; - } + float fog_max_distance = 0; + bool using_fog = false; + if (p_env && !p_shadow && p_env->fog_enabled && (p_env->fog_depth_enabled || p_env->fog_height_enabled)) { + state.scene_shader.set_conditional(SceneShaderGLES2::FOG_DEPTH_ENABLED, p_env->fog_depth_enabled); + state.scene_shader.set_conditional(SceneShaderGLES2::FOG_HEIGHT_ENABLED, p_env->fog_height_enabled); + fog_max_distance = p_projection.get_z_far(); + using_fog = true; + } - for (int i = 0; i < p_element_count; i++) { + RasterizerStorageGLES2::Texture *prev_lightmap = NULL; + float lightmap_energy = 1.0; + bool prev_use_lightmap_capture = false; - RenderList::Element *e = p_elements[i]; - RasterizerStorageGLES2::Material *material = e->material; - RasterizerStorageGLES2::Skeleton *skeleton = storage->skeleton_owner.getornull(e->instance->skeleton); + for (int i = 0; i < p_element_count; i++) { + RenderList::Element *e = p_elements[i]; - { - _setup_material(material, p_reverse_cull, false, Size2i(skeleton ? skeleton->size * 3 : 0, 0)); + RasterizerStorageGLES2::Material *material = e->material; - 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); + bool rebind = false; + bool accum_pass = *e->use_accum_ptr; + *e->use_accum_ptr = true; //set to accum for next time this is found + LightInstance *light = NULL; + ReflectionProbeInstance *refprobe_1 = NULL; + ReflectionProbeInstance *refprobe_2 = NULL; + RasterizerStorageGLES2::Texture *lightmap = NULL; + bool use_lightmap_capture = false; + bool rebind_light = false; + bool rebind_reflection = false; + bool rebind_lightmap = false; + + if (!p_shadow) { + + bool unshaded = material->shader->spatial.unshaded; + + if (unshaded != prev_unshaded) { + rebind = true; + if (unshaded) { + state.scene_shader.set_conditional(SceneShaderGLES2::SHADELESS, true); + state.scene_shader.set_conditional(SceneShaderGLES2::USE_RADIANCE_MAP, false); + state.scene_shader.set_conditional(SceneShaderGLES2::USE_LIGHTING, false); + } else { + state.scene_shader.set_conditional(SceneShaderGLES2::SHADELESS, false); + state.scene_shader.set_conditional(SceneShaderGLES2::USE_RADIANCE_MAP, use_radiance_map); } - 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()); + prev_unshaded = unshaded; + } - state.scene_shader.set_uniform(SceneShaderGLES2::TIME, storage->frame.time[0]); + bool base_pass = !accum_pass && !unshaded; //conditions for a base pass - 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); + if (base_pass != prev_base_pass) { + state.scene_shader.set_conditional(SceneShaderGLES2::BASE_PASS, base_pass); + rebind = true; + prev_base_pass = base_pass; } - 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]; + if (!unshaded && e->light_index < RenderList::MAX_LIGHTS) { + light = render_light_instances[e->light_index]; + } - state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_ENERGY, energy); - state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SPECULAR, specular); + if (light != prev_light) { - float sign = light_ptr->negative ? -1 : 1; + _setup_light_type(light, shadow_atlas); + rebind = true; + rebind_light = true; + } - 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; + int blend_mode = p_alpha_pass ? material->shader->spatial.blend_mode : -1; // -1 no blend, no mix - color[3] = 0; + if (accum_pass) { //accum pass force pass + blend_mode = RasterizerStorageGLES2::Shader::Spatial::BLEND_MODE_ADD; + } - state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_COLOR, color); + if (prev_blend_mode != blend_mode) { - CameraMatrix matrices[4]; + if (prev_blend_mode == -1 && blend_mode != -1) { + //does blend + glEnable(GL_BLEND); + } else if (blend_mode == -1 && prev_blend_mode != -1) { + //do not blend + glDisable(GL_BLEND); + } - if (light_ptr->shadow && directional_shadow.depth) { + switch (blend_mode) { + //-1 not handled because not blend is enabled anyway + 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); + } - int shadow_count = 0; - Color split_offsets; + } break; + case RasterizerStorageGLES2::Shader::Spatial::BLEND_MODE_ADD: { + + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(p_alpha_pass ? GL_SRC_ALPHA : GL_ONE, GL_ONE); - switch (light_ptr->directional_shadow_mode) { - case VS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL: { - shadow_count = 1; } break; + case RasterizerStorageGLES2::Shader::Spatial::BLEND_MODE_SUB: { - case VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS: { - shadow_count = 2; + 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); + } - case VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS: { - shadow_count = 4; } break; } - for (int k = 0; k < shadow_count; k++) { + prev_blend_mode = blend_mode; + } - 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; + //condition to enable vertex lighting on this object + bool vertex_lit = (material->shader->spatial.uses_vertex_lighting || storage->config.force_vertex_shading) && ((!unshaded && light) || using_fog); //fog forces vertex lighting because it still applies even if unshaded or no fog - if (light_ptr->directional_shadow_mode == VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS) { + if (vertex_lit != prev_vertex_lit) { + state.scene_shader.set_conditional(SceneShaderGLES2::USE_VERTEX_LIGHTING, vertex_lit); + prev_vertex_lit = vertex_lit; + } - width /= 2; - height /= 2; + if (!unshaded && !accum_pass && e->refprobe_0_index != RenderList::MAX_REFLECTION_PROBES) { + ERR_FAIL_INDEX(e->refprobe_0_index, reflection_probe_count); + refprobe_1 = reflection_probe_instances[e->refprobe_0_index]; + } + if (!unshaded && !accum_pass && e->refprobe_1_index != RenderList::MAX_REFLECTION_PROBES) { + ERR_FAIL_INDEX(e->refprobe_1_index, reflection_probe_count); + refprobe_2 = reflection_probe_instances[e->refprobe_1_index]; + } - if (k == 0) { + if (refprobe_1 != prev_refprobe_1 || refprobe_2 != prev_refprobe_2) { + state.scene_shader.set_conditional(SceneShaderGLES2::USE_REFLECTION_PROBE1, refprobe_1 != NULL); + state.scene_shader.set_conditional(SceneShaderGLES2::USE_REFLECTION_PROBE2, refprobe_2 != NULL); + if (refprobe_1 != NULL && refprobe_1 != prev_refprobe_1) { + glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 5); + glBindTexture(GL_TEXTURE_CUBE_MAP, refprobe_1->cubemap); + } + if (refprobe_2 != NULL && refprobe_2 != prev_refprobe_2) { + glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 6); + glBindTexture(GL_TEXTURE_CUBE_MAP, refprobe_2->cubemap); + } + rebind = true; + rebind_reflection = true; + } - } else if (k == 1) { - x += width; - } else if (k == 2) { - y += height; - } else if (k == 3) { - x += width; - y += height; - } + use_lightmap_capture = !unshaded && !accum_pass && !e->instance->lightmap_capture_data.empty(); - } else if (light_ptr->directional_shadow_mode == VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS) { + if (use_lightmap_capture != prev_use_lightmap_capture) { - height /= 2; + state.scene_shader.set_conditional(SceneShaderGLES2::USE_LIGHTMAP_CAPTURE, use_lightmap_capture); + rebind = true; + } - if (k == 0) { + if (!unshaded && !accum_pass && e->instance->lightmap.is_valid()) { - } else { - y += height; - } + lightmap = storage->texture_owner.getornull(e->instance->lightmap); + lightmap_energy = 1.0; + if (lightmap) { + RasterizerStorageGLES2::LightmapCapture *capture = storage->lightmap_capture_data_owner.getornull(e->instance->lightmap_capture->base); + if (capture) { + lightmap_energy = capture->energy; } + } + } - split_offsets[k] = light->shadow_transform[k].split; + if (lightmap != prev_lightmap) { + state.scene_shader.set_conditional(SceneShaderGLES2::USE_LIGHTMAP, lightmap != NULL); + if (lightmap != NULL) { + glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 4); + glBindTexture(GL_TEXTURE_2D, lightmap->tex_id); + } + rebind = true; + rebind_lightmap = true; + } + } - Transform modelview = (p_view_transform * light->shadow_transform[k].transform).inverse(); + bool instancing = e->instance->base_type == VS::INSTANCE_MULTIMESH; - 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); + if (instancing != prev_instancing) { - CameraMatrix shadow_mtx = rectm * bias * light->shadow_transform[k].camera * modelview; - matrices[k] = shadow_mtx.inverse(); + state.scene_shader.set_conditional(SceneShaderGLES2::USE_INSTANCING, instancing); + rebind = true; + } - 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; + RasterizerStorageGLES2::Skeleton *skeleton = storage->skeleton_owner.getornull(e->instance->skeleton); - 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); - } + if (skeleton != prev_skeleton) { - 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]); + if (skeleton) { + state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON, true); + state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON_SOFTWARE, !storage->config.float_texture_supported); } else { - state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_HAS_SHADOW, 0.0); + state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON, false); + state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON_SOFTWARE, false); } - _render_geometry(e); + rebind = true; + } + + if (e->owner != prev_owner || e->geometry != prev_geometry || skeleton != prev_skeleton) { + _setup_geometry(e, skeleton); } - } - state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_PASS, false); + bool shader_rebind = false; + if (rebind || material != prev_material) { + shader_rebind = _setup_material(material, p_reverse_cull, p_alpha_pass, Size2i(skeleton ? skeleton->size * 3 : 0, 0)); + } + + if (i == 0 || shader_rebind) { //first time must rebind + 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 (state.shadow_is_dual_parabolloid) { + state.scene_shader.set_uniform(SceneShaderGLES2::SHADOW_DUAL_PARABOLOID_RENDER_SIDE, state.dual_parbolloid_direction); + state.scene_shader.set_uniform(SceneShaderGLES2::SHADOW_DUAL_PARABOLOID_RENDER_ZFAR, state.dual_parbolloid_zfar); + } + } else { + if (use_radiance_map) { + state.scene_shader.set_uniform(SceneShaderGLES2::RADIANCE_INVERSE_XFORM, p_view_transform); + } + + 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); + } + + //rebind all these + rebind_light = true; + rebind_reflection = true; + rebind_lightmap = true; + + if (using_fog) { + + state.scene_shader.set_uniform(SceneShaderGLES2::FOG_COLOR_BASE, p_env->fog_color); + Color sun_color_amount = p_env->fog_sun_color; + sun_color_amount.a = p_env->fog_sun_amount; + + state.scene_shader.set_uniform(SceneShaderGLES2::FOG_SUN_COLOR_AMOUNT, sun_color_amount); + state.scene_shader.set_uniform(SceneShaderGLES2::FOG_TRANSMIT_ENABLED, p_env->fog_transmit_enabled); + state.scene_shader.set_uniform(SceneShaderGLES2::FOG_TRANSMIT_CURVE, p_env->fog_transmit_curve); + + if (p_env->fog_depth_enabled) { + state.scene_shader.set_uniform(SceneShaderGLES2::FOG_DEPTH_BEGIN, p_env->fog_depth_begin); + state.scene_shader.set_uniform(SceneShaderGLES2::FOG_DEPTH_CURVE, p_env->fog_depth_curve); + state.scene_shader.set_uniform(SceneShaderGLES2::FOG_MAX_DISTANCE, fog_max_distance); + } + + if (p_env->fog_height_enabled) { + state.scene_shader.set_uniform(SceneShaderGLES2::FOG_HEIGHT_MIN, p_env->fog_height_min); + state.scene_shader.set_uniform(SceneShaderGLES2::FOG_HEIGHT_MAX, p_env->fog_height_max); + state.scene_shader.set_uniform(SceneShaderGLES2::FOG_HEIGHT_MAX, p_env->fog_height_max); + state.scene_shader.set_uniform(SceneShaderGLES2::FOG_HEIGHT_CURVE, p_env->fog_height_curve); + } + } + } + + state.scene_shader.set_uniform(SceneShaderGLES2::CAMERA_MATRIX, p_view_transform); + state.scene_shader.set_uniform(SceneShaderGLES2::CAMERA_INVERSE_MATRIX, view_transform_inverse); + state.scene_shader.set_uniform(SceneShaderGLES2::PROJECTION_MATRIX, p_projection); + state.scene_shader.set_uniform(SceneShaderGLES2::PROJECTION_INVERSE_MATRIX, 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? + } + + if (rebind_light && light) { + _setup_light(light, shadow_atlas, p_view_transform); + } + + if (rebind_reflection && (refprobe_1 || refprobe_2)) { + _setup_refprobes(refprobe_1, refprobe_2, p_view_transform, p_env); + } + + if (rebind_lightmap && lightmap) { + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHTMAP_ENERGY, lightmap_energy); + } + + state.scene_shader.set_uniform(SceneShaderGLES2::WORLD_TRANSFORM, e->instance->transform); + + if (use_lightmap_capture) { //this is per instance, must be set always if present + glUniform4fv(state.scene_shader.get_uniform_location(SceneShaderGLES2::LIGHTMAP_CAPTURES), 12, (const GLfloat *)e->instance->lightmap_capture_data.ptr()); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHTMAP_CAPTURE_SKY, false); + } + + _render_geometry(e); + + prev_geometry = e->geometry; + prev_owner = e->owner; + prev_material = material; + prev_skeleton = skeleton; + prev_instancing = instancing; + prev_light = light; + prev_refprobe_1 = refprobe_1; + prev_refprobe_2 = refprobe_2; + prev_lightmap = lightmap; + prev_use_lightmap_capture = use_lightmap_capture; + } + + _setup_light_type(NULL, NULL); //clear light stuff + state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON, false); + state.scene_shader.set_conditional(SceneShaderGLES2::SHADELESS, false); + state.scene_shader.set_conditional(SceneShaderGLES2::BASE_PASS, false); + state.scene_shader.set_conditional(SceneShaderGLES2::USE_INSTANCING, 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); + state.scene_shader.set_conditional(SceneShaderGLES2::USE_VERTEX_LIGHTING, false); + state.scene_shader.set_conditional(SceneShaderGLES2::USE_REFLECTION_PROBE1, false); + state.scene_shader.set_conditional(SceneShaderGLES2::USE_REFLECTION_PROBE2, false); + state.scene_shader.set_conditional(SceneShaderGLES2::USE_LIGHTMAP, false); + state.scene_shader.set_conditional(SceneShaderGLES2::USE_LIGHTMAP_CAPTURE, false); + state.scene_shader.set_conditional(SceneShaderGLES2::FOG_DEPTH_ENABLED, false); + state.scene_shader.set_conditional(SceneShaderGLES2::FOG_HEIGHT_ENABLED, 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) { @@ -1992,13 +2455,20 @@ void RasterizerSceneGLES2::_draw_sky(RasterizerStorageGLES2::Sky *p_sky, const C glEnableVertexAttribArray(VS::ARRAY_VERTEX); glEnableVertexAttribArray(VS::ARRAY_TEX_UV); + storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_ASYM_PANO, asymmetrical); + storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_PANORAMA, !asymmetrical); 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); + if (asymmetrical) { + // pack the bits we need from our projection matrix + storage->shaders.copy.set_uniform(CopyShaderGLES2::ASYM_PROJ, camera.matrix[2][0], camera.matrix[0][0], camera.matrix[2][1], camera.matrix[1][1]); + ///@TODO I couldn't get mat3 + p_transform.basis to work, that would be better here. + storage->shaders.copy.set_uniform(CopyShaderGLES2::PANO_TRANSFORM, p_transform); + } glDrawArrays(GL_TRIANGLE_FAN, 0, 4); @@ -2006,16 +2476,92 @@ void RasterizerSceneGLES2::_draw_sky(RasterizerStorageGLES2::Sky *p_sky, const C glDisableVertexAttribArray(VS::ARRAY_TEX_UV); glBindBuffer(GL_ARRAY_BUFFER, 0); + storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_ASYM_PANO, false); + storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_PANORAMA, false); 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 = 0; + Environment *env = NULL; + + int viewport_width, viewport_height; + + if (p_reflection_probe.is_valid()) { + ReflectionProbeInstance *probe = reflection_probe_instance_owner.getornull(p_reflection_probe); + ERR_FAIL_COND(!probe); + state.render_no_shadows = !probe->probe_ptr->enable_shadows; + + if (!probe->probe_ptr->interior) { //use env only if not interior + env = environment_owner.getornull(p_environment); + } + + current_fb = probe->fbo[p_reflection_probe_pass]; + state.screen_pixel_size.x = 1.0 / probe->probe_ptr->resolution; + state.screen_pixel_size.y = 1.0 / probe->probe_ptr->resolution; + + viewport_width = probe->probe_ptr->resolution; + viewport_height = probe->probe_ptr->resolution; + + } else { + state.render_no_shadows = false; + current_fb = storage->frame.current_rt->fbo; + env = environment_owner.getornull(p_environment); + state.screen_pixel_size.x = 1.0 / storage->frame.current_rt->width; + state.screen_pixel_size.y = 1.0 / storage->frame.current_rt->height; + viewport_width = storage->frame.current_rt->width; + viewport_height = storage->frame.current_rt->height; + } + //push back the directional lights + + if (p_light_cull_count) { + //harcoded limit of 256 lights + render_light_instance_count = MIN(RenderList::MAX_LIGHTS, p_light_cull_count); + render_light_instances = (LightInstance **)alloca(sizeof(LightInstance *) * render_light_instance_count); + render_directional_lights = 0; + + //doing this because directional lights are at the end, put them at the beginning + int index = 0; + for (int i = render_light_instance_count - 1; i >= 0; 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) { + render_directional_lights++; + //as goin in reverse, directional lights are always first anyway + } + + light->light_index = index; + render_light_instances[index] = light; + + index++; + } + + } else { + render_light_instances = NULL; + render_directional_lights = 0; + render_light_instance_count = 0; + } + + if (p_reflection_probe_cull_count) { - GLuint current_fb = storage->frame.current_rt->fbo; - Environment *env = environment_owner.getornull(p_environment); + reflection_probe_instances = (ReflectionProbeInstance **)alloca(sizeof(ReflectionProbeInstance *) * p_reflection_probe_cull_count); + reflection_probe_count = p_reflection_probe_cull_count; + for (int i = 0; i < p_reflection_probe_cull_count; i++) { + ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_reflection_probe_cull_result[i]); + ERR_CONTINUE(!rpi); + rpi->last_pass = render_pass + 1; //will be incremented later + rpi->index = i; + reflection_probe_instances[i] = rpi; + } + + } else { + reflection_probe_instances = NULL; + reflection_probe_count = 0; + } // render list stuff @@ -2025,6 +2571,7 @@ void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const // other stuff glBindFramebuffer(GL_FRAMEBUFFER, current_fb); + glViewport(0, 0, viewport_width, viewport_height); glDepthFunc(GL_LEQUAL); glDepthMask(GL_TRUE); @@ -2068,34 +2615,22 @@ void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const } } - 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); + _render_render_list(render_list.elements, render_list.element_count, p_cam_transform, p_cam_projection, p_shadow_atlas, env, env_radiance_tex, 0.0, 0.0, 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); + render_list.sort_by_depth(true); + + _render_render_list(&render_list.elements[render_list.max_elements - render_list.alpha_element_count], render_list.alpha_element_count, p_cam_transform, p_cam_projection, p_shadow_atlas, env, env_radiance_tex, 0.0, 0.0, false, true, false); - glDepthMask(GL_FALSE); glDisable(GL_DEPTH_TEST); - // #define GLES2_SHADOW_ATLAS_DEBUG_VIEW + //#define GLES2_SHADOW_ATLAS_DEBUG_VIEW #ifdef GLES2_SHADOW_ATLAS_DEBUG_VIEW ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_shadow_atlas); @@ -2114,10 +2649,31 @@ void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const storage->_copy_screen(); } #endif + + //#define GLES2_SHADOW_DIRECTIONAL_DEBUG_VIEW + +#ifdef GLES2_SHADOW_DIRECTIONAL_DEBUG_VIEW + if (true) { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, directional_shadow.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) { + state.render_no_shadows = false; + LightInstance *light_instance = light_instance_owner.getornull(p_light); ERR_FAIL_COND(!light_instance); @@ -2128,13 +2684,13 @@ void RasterizerSceneGLES2::render_shadow(RID p_light, RID p_shadow_atlas, int p_ 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; + state.shadow_is_dual_parabolloid = false; + state.dual_parbolloid_direction = 0.0; int current_cubemap = -1; float bias = 0; @@ -2213,14 +2769,12 @@ void RasterizerSceneGLES2::render_shadow(RID p_light, RID p_shadow_atlas, int p_ 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]; @@ -2263,8 +2817,32 @@ void RasterizerSceneGLES2::render_shadow(RID p_light, RID p_shadow_atlas, int p_ zfar = light->param[VS::LIGHT_PARAM_RANGE]; current_cubemap = cubemap_index; + } else { + //dual parabolloid + state.shadow_is_dual_parabolloid = true; + light_projection = light_instance->shadow_transform[0].camera; + light_transform = light_instance->shadow_transform[0].transform; + + if (light->omni_shadow_detail == VS::LIGHT_OMNI_SHADOW_DETAIL_HORIZONTAL) { + + height /= 2; + y += p_pass * height; + } else { + width /= 2; + x += p_pass * width; + } + + state.dual_parbolloid_direction = p_pass == 0 ? 1.0 : -1.0; + flip_facing = (p_pass == 1); + zfar = light->param[VS::LIGHT_PARAM_RANGE]; + bias = light->param[VS::LIGHT_PARAM_SHADOW_BIAS]; + + state.dual_parbolloid_zfar = zfar; + + state.scene_shader.set_conditional(SceneShaderGLES2::RENDER_DEPTH_DUAL_PARABOLOID, true); } - } else { + + } else if (light->type == VS::LIGHT_SPOT) { light_projection = light_instance->shadow_transform[0].camera; light_transform = light_instance->shadow_transform[0].transform; @@ -2303,11 +2881,16 @@ void RasterizerSceneGLES2::render_shadow(RID p_light, RID p_shadow_atlas, int p_ glClear(GL_DEPTH_BUFFER_BIT); glDisable(GL_SCISSOR_TEST); + if (light->reverse_cull) { + flip_facing = !flip_facing; + } + 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); + _render_render_list(render_list.elements, render_list.element_count, light_transform, light_projection, RID(), NULL, 0, bias, normal_bias, flip_facing, false, true); state.scene_shader.set_conditional(SceneShaderGLES2::RENDER_DEPTH, false); + state.scene_shader.set_conditional(SceneShaderGLES2::RENDER_DEPTH_DUAL_PARABOLOID, 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) { @@ -2357,6 +2940,7 @@ void RasterizerSceneGLES2::render_shadow(RID p_light, RID p_shadow_atlas, int p_ } glViewport(0, 0, storage->frame.current_rt->width, storage->frame.current_rt->height); + glColorMask(1, 1, 1, 1); } void RasterizerSceneGLES2::set_scene_pass(uint64_t p_pass) { @@ -2364,6 +2948,44 @@ void RasterizerSceneGLES2::set_scene_pass(uint64_t p_pass) { } bool RasterizerSceneGLES2::free(RID p_rid) { + + if (light_instance_owner.owns(p_rid)) { + + LightInstance *light_instance = light_instance_owner.getptr(p_rid); + + //remove from shadow atlases.. + for (Set<RID>::Element *E = light_instance->shadow_atlases.front(); E; E = E->next()) { + ShadowAtlas *shadow_atlas = shadow_atlas_owner.get(E->get()); + ERR_CONTINUE(!shadow_atlas->shadow_owners.has(p_rid)); + uint32_t key = shadow_atlas->shadow_owners[p_rid]; + uint32_t q = (key >> ShadowAtlas::QUADRANT_SHIFT) & 0x3; + uint32_t s = key & ShadowAtlas::SHADOW_INDEX_MASK; + + shadow_atlas->quadrants[q].shadows.write[s].owner = RID(); + shadow_atlas->shadow_owners.erase(p_rid); + } + + light_instance_owner.free(p_rid); + memdelete(light_instance); + + } else if (shadow_atlas_owner.owns(p_rid)) { + + ShadowAtlas *shadow_atlas = shadow_atlas_owner.get(p_rid); + shadow_atlas_set_size(p_rid, 0); + shadow_atlas_owner.free(p_rid); + memdelete(shadow_atlas); + } else if (reflection_probe_instance_owner.owns(p_rid)) { + + ReflectionProbeInstance *reflection_instance = reflection_probe_instance_owner.get(p_rid); + + reflection_probe_release_atlas_index(p_rid); + reflection_probe_instance_owner.free(p_rid); + memdelete(reflection_instance); + + } else { + return false; + } + return true; } @@ -2376,6 +2998,8 @@ void RasterizerSceneGLES2::initialize() { render_list.init(); + render_pass = 1; + shadow_atlas_realloc_tolerance_msec = 500; { @@ -2393,6 +3017,27 @@ void RasterizerSceneGLES2::initialize() { } { + default_worldcoord_shader = storage->shader_create(); + storage->shader_set_code(default_worldcoord_shader, "shader_type spatial; render_mode world_vertex_coords;\n"); + default_worldcoord_material = storage->material_create(); + storage->material_set_shader(default_worldcoord_material, default_worldcoord_shader); + + default_worldcoord_shader_twosided = storage->shader_create(); + default_worldcoord_material_twosided = storage->material_create(); + storage->shader_set_code(default_worldcoord_shader_twosided, "shader_type spatial; render_mode cull_disabled,world_vertex_coords;\n"); + storage->material_set_shader(default_worldcoord_material_twosided, default_worldcoord_shader_twosided); + } + + { + //default material and shader + + default_overdraw_shader = storage->shader_create(); + storage->shader_set_code(default_overdraw_shader, "shader_type spatial;\nrender_mode blend_add,unshaded;\n void fragment() { ALBEDO=vec3(0.4,0.8,0.8); ALPHA=0.2; }"); + default_overdraw_material = storage->material_create(); + storage->material_set_shader(default_overdraw_material, default_overdraw_shader); + } + + { glGenBuffers(1, &state.sky_verts); glBindBuffer(GL_ARRAY_BUFFER, state.sky_verts); glBufferData(GL_ARRAY_BUFFER, sizeof(Vector3) * 8, NULL, GL_DYNAMIC_DRAW); @@ -2401,6 +3046,7 @@ void RasterizerSceneGLES2::initialize() { { uint32_t immediate_buffer_size = GLOBAL_DEF("rendering/limits/buffers/immediate_buffer_size_kb", 2048); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/buffers/immediate_buffer_size_kb", PropertyInfo(Variant::INT, "rendering/limits/buffers/immediate_buffer_size_kb", PROPERTY_HINT_RANGE, "0,8192,1,or_greater")); glGenBuffers(1, &state.immediate_buffer); glBindBuffer(GL_ARRAY_BUFFER, state.immediate_buffer); @@ -2426,7 +3072,7 @@ void RasterizerSceneGLES2::initialize() { glBindTexture(GL_TEXTURE_CUBE_MAP, cube.cubemap); for (int i = 0; i < 6; i++) { - glTexImage2D(_cube_side_enum[i], 0, GL_DEPTH_COMPONENT, cube_size, cube_size, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, NULL); + glTexImage2D(_cube_side_enum[i], 0, GL_DEPTH_COMPONENT, cube_size, cube_size, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL); } glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); @@ -2462,8 +3108,8 @@ void RasterizerSceneGLES2::initialize() { glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 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_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); @@ -2474,9 +3120,12 @@ void RasterizerSceneGLES2::initialize() { ERR_PRINT("Directional shadow framebuffer status invalid"); } } + + shadow_filter_mode = SHADOW_FILTER_NEAREST; } void RasterizerSceneGLES2::iteration() { + shadow_filter_mode = ShadowFilterMode(int(GLOBAL_GET("rendering/quality/shadows/filter_mode"))); } void RasterizerSceneGLES2::finalize() { diff --git a/drivers/gles2/rasterizer_scene_gles2.h b/drivers/gles2/rasterizer_scene_gles2.h index 72dbe14387..33ac99366d 100644 --- a/drivers/gles2/rasterizer_scene_gles2.h +++ b/drivers/gles2/rasterizer_scene_gles2.h @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef RASTERIZERSCENEGLES2_H #define RASTERIZERSCENEGLES2_H @@ -52,12 +53,39 @@ class RasterizerSceneGLES2 : public RasterizerScene { public: + enum ShadowFilterMode { + SHADOW_FILTER_NEAREST, + SHADOW_FILTER_PCF5, + SHADOW_FILTER_PCF13, + }; + + enum { + INSTANCE_ATTRIB_BASE = 8, + INSTANCE_BONE_BASE = 13, + }; + + ShadowFilterMode shadow_filter_mode; + RID default_material; RID default_material_twosided; RID default_shader; RID default_shader_twosided; + RID default_worldcoord_material; + RID default_worldcoord_material_twosided; + RID default_worldcoord_shader; + RID default_worldcoord_shader_twosided; + + RID default_overdraw_material; + RID default_overdraw_shader; + + uint64_t render_pass; uint64_t scene_pass; + uint32_t current_material_index; + uint32_t current_geometry_index; + uint32_t current_light_index; + uint32_t current_refprobe_index; + uint32_t current_shader_index; RasterizerStorageGLES2 *storage; struct State { @@ -171,11 +199,19 @@ public: bool cull_front; bool cull_disabled; bool used_sss; - bool used_screen_texture; bool using_contact_shadows; VS::ViewportDebugDraw debug_draw; */ + + bool used_screen_texture; + bool shadow_is_dual_parabolloid; + float dual_parbolloid_direction; + float dual_parbolloid_zfar; + + bool render_no_shadows; + + Vector2 screen_pixel_size; } state; /* SHADOW ATLAS API */ @@ -259,6 +295,38 @@ public: /* REFLECTION PROBE INSTANCE */ + struct ReflectionProbeInstance : public RID_Data { + + RasterizerStorageGLES2::ReflectionProbe *probe_ptr; + RID probe; + RID self; + RID atlas; + + int reflection_atlas_index; + + int render_step; + int reflection_index; + + GLuint fbo[6]; + GLuint cubemap; + GLuint depth; + + GLuint fbo_blur; + + int current_resolution; + mutable bool dirty; + + uint64_t last_pass; + uint32_t index; + + Transform transform; + }; + + mutable RID_Owner<ReflectionProbeInstance> reflection_probe_instance_owner; + + ReflectionProbeInstance **reflection_probe_instances; + int reflection_probe_count; + virtual RID reflection_probe_instance_create(RID p_probe); virtual void reflection_probe_instance_set_transform(RID p_instance, const Transform &p_transform); virtual void reflection_probe_release_atlas_index(RID p_instance); @@ -285,6 +353,21 @@ public: int canvas_max_layer; + bool fog_enabled; + Color fog_color; + Color fog_sun_color; + float fog_sun_amount; + + bool fog_depth_enabled; + float fog_depth_begin; + float fog_depth_curve; + bool fog_transmit_enabled; + float fog_transmit_curve; + bool fog_height_enabled; + float fog_height_min; + float fog_height_max; + float fog_height_curve; + Environment() { bg_mode = VS::ENV_BG_CLEAR_COLOR; sky_custom_fov = 0.0; @@ -293,6 +376,24 @@ public: ambient_energy = 1.0; ambient_sky_contribution = 0.0; canvas_max_layer = 0; + + fog_enabled = false; + fog_color = Color(0.5, 0.5, 0.5); + fog_sun_color = Color(0.8, 0.8, 0.0); + fog_sun_amount = 0; + + fog_depth_enabled = true; + + fog_depth_begin = 10; + fog_depth_curve = 1; + + fog_transmit_enabled = true; + fog_transmit_curve = 1; + + fog_height_enabled = false; + fog_height_min = 0; + fog_height_max = 100; + fog_height_curve = 1; } }; @@ -372,6 +473,10 @@ public: 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); virtual void light_instance_mark_visible(RID p_light_instance); + LightInstance **render_light_instances; + int render_directional_lights; + int render_light_instance_count; + /* REFLECTION INSTANCE */ virtual RID gi_probe_instance_create(); @@ -381,40 +486,19 @@ public: /* RENDER LIST */ + enum LightMode { + LIGHTMODE_NORMAL, + LIGHTMODE_UNSHADED, + LIGHTMODE_LIGHTMAP, + LIGHTMODE_LIGHTMAP_CAPTURE, + }; + 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 + MAX_LIGHTS = 255, + MAX_REFLECTION_PROBES = 255, + DEFAULT_MAX_ELEMENTS = 65536 }; int max_elements; @@ -426,7 +510,38 @@ public: RasterizerStorageGLES2::Material *material; RasterizerStorageGLES2::GeometryOwner *owner; - uint64_t sort_key; + bool use_accum; //is this an add pass for multipass + bool *use_accum_ptr; + + union { + //TODO: should be endian swapped on big endian + struct { + int32_t depth_layer : 16; + int32_t priority : 16; + }; + + uint32_t depth_key; + }; + + union { + struct { + //from least significant to most significant in sort, TODO: should be endian swapped on big endian + + uint64_t geometry_index : 14; + uint64_t instancing : 1; + uint64_t skeleton : 1; + uint64_t shader_index : 10; + uint64_t material_index : 10; + uint64_t light_index : 8; + uint64_t light_type2 : 1; // if 1==0 : nolight/directional, else omni/spot + uint64_t refprobe_1_index : 8; + uint64_t refprobe_0_index : 8; + uint64_t light_type1 : 1; //no light, directional is 0, omni spot is 1 + uint64_t light_mode : 2; // LightMode enum + }; + + uint64_t sort_key; + }; }; Element *base_elements; @@ -444,7 +559,11 @@ public: struct SortByKey { _FORCE_INLINE_ bool operator()(const Element *A, const Element *B) const { - return A->sort_key < B->sort_key; + if (A->depth_key == B->depth_key) { + return A->sort_key < B->sort_key; + } else { + return A->depth_key < B->depth_key; + } } }; @@ -475,29 +594,6 @@ public: } } - 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() { @@ -548,7 +644,6 @@ public: 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, @@ -558,14 +653,16 @@ public: float p_shadow_normal_bias, bool p_reverse_cull, bool p_alpha_pass, - bool p_shadow, - bool p_directional_add); + bool p_shadow); 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, bool p_alpha_pass, 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); + _FORCE_INLINE_ bool _setup_material(RasterizerStorageGLES2::Material *p_material, bool p_reverse_cull, bool p_alpha_pass, Size2i p_skeleton_tex_size = Size2i(0, 0)); + _FORCE_INLINE_ void _setup_geometry(RenderList::Element *p_element, RasterizerStorageGLES2::Skeleton *p_skeleton); + _FORCE_INLINE_ void _setup_light_type(LightInstance *p_light, ShadowAtlas *shadow_atlas); + _FORCE_INLINE_ void _setup_light(LightInstance *p_light, ShadowAtlas *shadow_atlas, const Transform &p_view_transform); + _FORCE_INLINE_ void _setup_refprobes(ReflectionProbeInstance *p_refprobe1, ReflectionProbeInstance *p_refprobe2, const Transform &p_view_transform, Environment *p_env); + _FORCE_INLINE_ 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); diff --git a/drivers/gles2/rasterizer_storage_gles2.cpp b/drivers/gles2/rasterizer_storage_gles2.cpp index 3cee42983d..5011794735 100644 --- a/drivers/gles2/rasterizer_storage_gles2.cpp +++ b/drivers/gles2/rasterizer_storage_gles2.cpp @@ -27,14 +27,13 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "rasterizer_storage_gles2.h" -#include "project_settings.h" +#include "core/math/transform.h" +#include "core/project_settings.h" #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; @@ -53,6 +52,10 @@ GLuint RasterizerStorageGLES2::system_fbo = 0; #define _GL_HALF_FLOAT_OES 0x8D61 #endif +#define _EXT_TEXTURE_CUBE_MAP_SEAMLESS 0x884F + +#define _DEPTH_COMPONENT24_OES 0x81A6 + 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); @@ -355,7 +358,6 @@ void RasterizerStorageGLES2::texture_allocate(RID p_texture, int p_width, int p_ 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 @@ -496,22 +498,6 @@ void RasterizerStorageGLES2::texture_set_data(RID p_texture, const Ref<Image> &p glTexParameterf(texture->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } -//set swizle for older format compatibility -#ifdef GLES_OVER_GL - switch (texture->format) { - - case Image::FORMAT_L8: { - - } break; - case Image::FORMAT_LA8: { - - } break; - default: { - - } break; - } -#endif - int mipmaps = ((texture->flags & VS::TEXTURE_FLAG_MIPMAPS) && img->has_mipmaps()) ? img->get_mipmap_count() + 1 : 1; int w = img->get_width(); @@ -591,7 +577,7 @@ Ref<Image> RasterizerStorageGLES2::texture_get_data(RID p_texture, int p_layer) PoolVector<uint8_t> data; - int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, real_format, texture->mipmaps > 1 ? -1 : 0); + int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, real_format, texture->mipmaps > 1); data.resize(data_size * 2); //add some memory at the end, just in case for buggy drivers PoolVector<uint8_t>::Write wb = data.write(); @@ -627,8 +613,72 @@ Ref<Image> RasterizerStorageGLES2::texture_get_data(RID p_texture, int p_layer) return Ref<Image>(img); #else - ERR_EXPLAIN("Sorry, It's not posible to obtain images back in OpenGL ES"); - return Ref<Image>(); + Image::Format real_format; + GLenum gl_format; + GLenum gl_internal_format; + GLenum gl_type; + bool compressed; + _get_gl_image_and_format(Ref<Image>(), texture->format, texture->flags, real_format, gl_format, gl_internal_format, gl_type, compressed); + + PoolVector<uint8_t> data; + + int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, Image::FORMAT_RGBA8, false); + + data.resize(data_size * 2); //add some memory at the end, just in case for buggy drivers + PoolVector<uint8_t>::Write wb = data.write(); + + GLuint temp_framebuffer; + glGenFramebuffers(1, &temp_framebuffer); + + GLuint temp_color_texture; + glGenTextures(1, &temp_color_texture); + + glBindFramebuffer(GL_FRAMEBUFFER, temp_framebuffer); + + glBindTexture(GL_TEXTURE_2D, temp_color_texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture->alloc_width, texture->alloc_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, temp_color_texture, 0); + + glDepthMask(GL_FALSE); + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glDisable(GL_BLEND); + glDepthFunc(GL_LEQUAL); + glColorMask(1, 1, 1, 1); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture->tex_id); + + glViewport(0, 0, texture->alloc_width, texture->alloc_height); + + shaders.copy.bind(); + + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT); + bind_quad_array(); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glReadPixels(0, 0, texture->alloc_width, texture->alloc_height, GL_RGBA, GL_UNSIGNED_BYTE, &wb[0]); + + glDeleteTextures(1, &temp_color_texture); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &temp_framebuffer); + + wb = PoolVector<uint8_t>::Write(); + + data.resize(data_size); + + Image *img = memnew(Image(texture->alloc_width, texture->alloc_height, false, Image::FORMAT_RGBA8, data)); + if (!texture->compressed) { + img->convert(real_format); + } + + return Ref<Image>(img); + #endif } @@ -915,26 +965,27 @@ void RasterizerStorageGLES2::sky_set_texture(RID p_sky, RID p_panorama, int p_ra // 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? + GLenum internal_format = GL_RGB; + GLenum format = GL_RGB; + GLenum type = GL_UNSIGNED_BYTE; // Set the initial (empty) mipmaps +#if 1 + //Mobile hardware (PowerVR specially) prefers this approach, the other one kills the game + for (int i = 0; i < 6; i++) { + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, internal_format, size, size, 0, format, type, NULL); + } + + glGenerateMipmap(GL_TEXTURE_CUBE_MAP); +#else while (size >= 1) { for (int i = 0; i < 6; i++) { @@ -945,7 +996,14 @@ void RasterizerStorageGLES2::sky_set_texture(RID p_sky, RID p_panorama, int p_ra size >>= 1; } +#endif + //framebuffer + glGenFramebuffers(1, &tmp_fb); + glBindFramebuffer(GL_FRAMEBUFFER, tmp_fb); + shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES2::USE_SOURCE_PANORAMA, texture->target == GL_TEXTURE_2D); + + shaders.cubemap_filter.bind(); lod = 0; mm_level = mipmaps; @@ -964,6 +1022,7 @@ void RasterizerStorageGLES2::sky_set_texture(RID p_sky, RID p_panorama, int p_ra shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES2::FACE_ID, i); float roughness = mm_level ? lod / (float)(mipmaps - 1) : 1; + roughness = MIN(1.0, roughness); //keep max at 1 shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES2::ROUGHNESS, roughness); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); @@ -1309,8 +1368,13 @@ void RasterizerStorageGLES2::shader_get_param_list(RID p_shader, List<PropertyIn pi.hint_string = "CubeMap"; } break; - default: { - + case ShaderLanguage::TYPE_SAMPLER2DARRAY: + case ShaderLanguage::TYPE_ISAMPLER2DARRAY: + case ShaderLanguage::TYPE_USAMPLER2DARRAY: + case ShaderLanguage::TYPE_SAMPLER3D: + case ShaderLanguage::TYPE_ISAMPLER3D: + case ShaderLanguage::TYPE_USAMPLER3D: { + // Not implemented in GLES2 } break; } @@ -1420,6 +1484,19 @@ Variant RasterizerStorageGLES2::material_get_param(RID p_material, const StringN return material->params[p_param]; } + return material_get_param_default(p_material, p_param); +} + +Variant RasterizerStorageGLES2::material_get_param_default(RID p_material, const StringName &p_param) const { + const Material *material = material_owner.get(p_material); + ERR_FAIL_COND_V(!material, Variant()); + + if (material->shader) { + if (material->shader->uniforms.has(p_param)) { + Vector<ShaderLanguage::ConstantNode::Value> default_value = material->shader->uniforms[p_param].default_value; + return ShaderLanguage::constant_value_to_variant(default_value, material->shader->uniforms[p_param].type); + } + } return Variant(); } @@ -1525,7 +1602,7 @@ void RasterizerStorageGLES2::_update_material(Material *p_material) { 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))) { + (!p_material->shader->spatial.uses_alpha || p_material->shader->spatial.depth_draw_mode == Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS)) { can_cast_shadow = true; } @@ -1552,7 +1629,7 @@ void RasterizerStorageGLES2::_update_material(Material *p_material) { } } - // uniforms and other thigns will be set in the use_material method in ShaderGLES2 + // uniforms and other things will be set in the use_material method in ShaderGLES2 if (p_material->shader && p_material->shader->texture_count > 0) { @@ -2622,10 +2699,10 @@ void RasterizerStorageGLES2::update_dirty_multimeshes() { if (multimesh->mesh.is_valid()) { mesh_aabb = mesh_get_aabb(multimesh->mesh, RID()); - } else { - mesh_aabb.size += Vector3(0.001, 0.001, 0.001); } + mesh_aabb.size += Vector3(0.001, 0.001, 0.001); //in case mesh is empty in one of the sides + int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; int count = multimesh->data.size(); float *data = multimesh->data.ptrw(); @@ -3097,6 +3174,7 @@ void RasterizerStorageGLES2::light_set_param(RID p_light, VS::LightParam p_param light->version++; light->instance_change_notify(); } break; + default: {} } light->param[p_param] = p_value; @@ -3148,6 +3226,9 @@ void RasterizerStorageGLES2::light_set_reverse_cull_face_mode(RID p_light, bool ERR_FAIL_COND(!light); light->reverse_cull = p_enabled; + + light->version++; + light->instance_change_notify(); } void RasterizerStorageGLES2::light_omni_set_shadow_mode(RID p_light, VS::LightOmniShadowMode p_mode) { @@ -3288,69 +3369,194 @@ AABB RasterizerStorageGLES2::light_get_aabb(RID p_light) const { /* PROBE API */ RID RasterizerStorageGLES2::reflection_probe_create() { - return RID(); + + ReflectionProbe *reflection_probe = memnew(ReflectionProbe); + + reflection_probe->intensity = 1.0; + reflection_probe->interior_ambient = Color(); + reflection_probe->interior_ambient_energy = 1.0; + reflection_probe->max_distance = 0; + reflection_probe->extents = Vector3(1, 1, 1); + reflection_probe->origin_offset = Vector3(0, 0, 0); + reflection_probe->interior = false; + reflection_probe->box_projection = false; + reflection_probe->enable_shadows = false; + reflection_probe->cull_mask = (1 << 20) - 1; + reflection_probe->update_mode = VS::REFLECTION_PROBE_UPDATE_ONCE; + reflection_probe->resolution = 128; + + return reflection_probe_owner.make_rid(reflection_probe); } void RasterizerStorageGLES2::reflection_probe_set_update_mode(RID p_probe, VS::ReflectionProbeUpdateMode p_mode) { + + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->update_mode = p_mode; + reflection_probe->instance_change_notify(); } void RasterizerStorageGLES2::reflection_probe_set_intensity(RID p_probe, float p_intensity) { + + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->intensity = p_intensity; } void RasterizerStorageGLES2::reflection_probe_set_interior_ambient(RID p_probe, const Color &p_ambient) { + + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->interior_ambient = p_ambient; } void RasterizerStorageGLES2::reflection_probe_set_interior_ambient_energy(RID p_probe, float p_energy) { + + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->interior_ambient_energy = p_energy; } void RasterizerStorageGLES2::reflection_probe_set_interior_ambient_probe_contribution(RID p_probe, float p_contrib) { + + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->interior_ambient_probe_contrib = p_contrib; } void RasterizerStorageGLES2::reflection_probe_set_max_distance(RID p_probe, float p_distance) { -} -void RasterizerStorageGLES2::reflection_probe_set_extents(RID p_probe, const Vector3 &p_extents) { + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->max_distance = p_distance; + reflection_probe->instance_change_notify(); } +void RasterizerStorageGLES2::reflection_probe_set_extents(RID p_probe, const Vector3 &p_extents) { + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->extents = p_extents; + reflection_probe->instance_change_notify(); +} void RasterizerStorageGLES2::reflection_probe_set_origin_offset(RID p_probe, const Vector3 &p_offset) { + + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->origin_offset = p_offset; + reflection_probe->instance_change_notify(); } void RasterizerStorageGLES2::reflection_probe_set_as_interior(RID p_probe, bool p_enable) { -} + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->interior = p_enable; +} void RasterizerStorageGLES2::reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable) { + + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->box_projection = p_enable; } void RasterizerStorageGLES2::reflection_probe_set_enable_shadows(RID p_probe, bool p_enable) { -} + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->enable_shadows = p_enable; + reflection_probe->instance_change_notify(); +} void RasterizerStorageGLES2::reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) { + + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->cull_mask = p_layers; + reflection_probe->instance_change_notify(); +} + +void RasterizerStorageGLES2::reflection_probe_set_resolution(RID p_probe, int p_resolution) { + + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->resolution = p_resolution; } AABB RasterizerStorageGLES2::reflection_probe_get_aabb(RID p_probe) const { - return AABB(); + const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!reflection_probe, AABB()); + + AABB aabb; + aabb.position = -reflection_probe->extents; + aabb.size = reflection_probe->extents * 2.0; + + return aabb; } VS::ReflectionProbeUpdateMode RasterizerStorageGLES2::reflection_probe_get_update_mode(RID p_probe) const { - return VS::REFLECTION_PROBE_UPDATE_ALWAYS; + + const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!reflection_probe, VS::REFLECTION_PROBE_UPDATE_ALWAYS); + + return reflection_probe->update_mode; } uint32_t RasterizerStorageGLES2::reflection_probe_get_cull_mask(RID p_probe) const { - return 0; + + const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!reflection_probe, 0); + + return reflection_probe->cull_mask; } Vector3 RasterizerStorageGLES2::reflection_probe_get_extents(RID p_probe) const { - return Vector3(); + + const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!reflection_probe, Vector3()); + + return reflection_probe->extents; } Vector3 RasterizerStorageGLES2::reflection_probe_get_origin_offset(RID p_probe) const { - return Vector3(); + + const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!reflection_probe, Vector3()); + + return reflection_probe->origin_offset; } bool RasterizerStorageGLES2::reflection_probe_renders_shadows(RID p_probe) const { - return false; + + const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!reflection_probe, false); + + return reflection_probe->enable_shadows; } float RasterizerStorageGLES2::reflection_probe_get_origin_max_distance(RID p_probe) const { - return 0; + + const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!reflection_probe, 0); + + return reflection_probe->max_distance; +} + +int RasterizerStorageGLES2::reflection_probe_get_resolution(RID p_probe) const { + + const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!reflection_probe, 0); + + return reflection_probe->resolution; } RID RasterizerStorageGLES2::gi_probe_create() { @@ -3451,46 +3657,100 @@ void RasterizerStorageGLES2::gi_probe_dynamic_data_update(RID p_gi_probe_data, i /////// RID RasterizerStorageGLES2::lightmap_capture_create() { - return RID(); + + LightmapCapture *capture = memnew(LightmapCapture); + return lightmap_capture_data_owner.make_rid(capture); } void RasterizerStorageGLES2::lightmap_capture_set_bounds(RID p_capture, const AABB &p_bounds) { -} -AABB RasterizerStorageGLES2::lightmap_capture_get_bounds(RID p_capture) const { - return AABB(); + LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND(!capture); + capture->bounds = p_bounds; + capture->instance_change_notify(); } +AABB RasterizerStorageGLES2::lightmap_capture_get_bounds(RID p_capture) const { -void RasterizerStorageGLES2::lightmap_capture_set_octree(RID p_capture, const PoolVector<uint8_t> &p_octree) { + const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND_V(!capture, AABB()); + return capture->bounds; } +void RasterizerStorageGLES2::lightmap_capture_set_octree(RID p_capture, const PoolVector<uint8_t> &p_octree) { + + LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND(!capture); + + ERR_FAIL_COND(p_octree.size() == 0 || (p_octree.size() % sizeof(LightmapCaptureOctree)) != 0); + capture->octree.resize(p_octree.size() / sizeof(LightmapCaptureOctree)); + if (p_octree.size()) { + PoolVector<LightmapCaptureOctree>::Write w = capture->octree.write(); + PoolVector<uint8_t>::Read r = p_octree.read(); + copymem(w.ptr(), r.ptr(), p_octree.size()); + } + capture->instance_change_notify(); +} PoolVector<uint8_t> RasterizerStorageGLES2::lightmap_capture_get_octree(RID p_capture) const { - return PoolVector<uint8_t>(); + + const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND_V(!capture, PoolVector<uint8_t>()); + + if (capture->octree.size() == 0) + return PoolVector<uint8_t>(); + + PoolVector<uint8_t> ret; + ret.resize(capture->octree.size() * sizeof(LightmapCaptureOctree)); + { + PoolVector<LightmapCaptureOctree>::Read r = capture->octree.read(); + PoolVector<uint8_t>::Write w = ret.write(); + copymem(w.ptr(), r.ptr(), ret.size()); + } + + return ret; } void RasterizerStorageGLES2::lightmap_capture_set_octree_cell_transform(RID p_capture, const Transform &p_xform) { + LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND(!capture); + capture->cell_xform = p_xform; } Transform RasterizerStorageGLES2::lightmap_capture_get_octree_cell_transform(RID p_capture) const { - return Transform(); + const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND_V(!capture, Transform()); + return capture->cell_xform; } void RasterizerStorageGLES2::lightmap_capture_set_octree_cell_subdiv(RID p_capture, int p_subdiv) { + LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND(!capture); + capture->cell_subdiv = p_subdiv; } int RasterizerStorageGLES2::lightmap_capture_get_octree_cell_subdiv(RID p_capture) const { - return 0; + const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND_V(!capture, 0); + return capture->cell_subdiv; } void RasterizerStorageGLES2::lightmap_capture_set_energy(RID p_capture, float p_energy) { + + LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND(!capture); + capture->energy = p_energy; } float RasterizerStorageGLES2::lightmap_capture_get_energy(RID p_capture) const { - return 0.0; + + const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND_V(!capture, 0); + return capture->energy; } const PoolVector<RasterizerStorage::LightmapCaptureOctree> *RasterizerStorageGLES2::lightmap_capture_get_octree_ptr(RID p_capture) const { - return NULL; + const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND_V(!capture, NULL); + return &capture->octree; } /////// @@ -3582,15 +3842,115 @@ void RasterizerStorageGLES2::update_particles() { //////// void RasterizerStorageGLES2::instance_add_skeleton(RID p_skeleton, RasterizerScene::InstanceBase *p_instance) { + + Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + ERR_FAIL_COND(!skeleton); + + skeleton->instances.insert(p_instance); } void RasterizerStorageGLES2::instance_remove_skeleton(RID p_skeleton, RasterizerScene::InstanceBase *p_instance) { + + Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + ERR_FAIL_COND(!skeleton); + + skeleton->instances.erase(p_instance); } void RasterizerStorageGLES2::instance_add_dependency(RID p_base, RasterizerScene::InstanceBase *p_instance) { + + Instantiable *inst = NULL; + switch (p_instance->base_type) { + case VS::INSTANCE_MESH: { + inst = mesh_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break; + case VS::INSTANCE_MULTIMESH: { + inst = multimesh_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break; + case VS::INSTANCE_IMMEDIATE: { + inst = immediate_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break; + /*case VS::INSTANCE_PARTICLES: { + inst = particles_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break;*/ + case VS::INSTANCE_REFLECTION_PROBE: { + inst = reflection_probe_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break; + case VS::INSTANCE_LIGHT: { + inst = light_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break; + /*case VS::INSTANCE_GI_PROBE: { + inst = gi_probe_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break;*/ + case VS::INSTANCE_LIGHTMAP_CAPTURE: { + inst = lightmap_capture_data_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break; + default: { + if (!inst) { + ERR_FAIL(); + } + } + } + + inst->instance_list.add(&p_instance->dependency_item); } void RasterizerStorageGLES2::instance_remove_dependency(RID p_base, RasterizerScene::InstanceBase *p_instance) { + + Instantiable *inst = NULL; + + switch (p_instance->base_type) { + case VS::INSTANCE_MESH: { + inst = mesh_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break; + case VS::INSTANCE_MULTIMESH: { + inst = multimesh_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break; + case VS::INSTANCE_IMMEDIATE: { + inst = immediate_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break; + /*case VS::INSTANCE_PARTICLES: { + inst = particles_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break;*/ + case VS::INSTANCE_REFLECTION_PROBE: { + inst = reflection_probe_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break; + case VS::INSTANCE_LIGHT: { + inst = light_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break; + /*case VS::INSTANCE_GI_PROBE: { + inst = gi_probe_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break; */ + case VS::INSTANCE_LIGHTMAP_CAPTURE: { + inst = lightmap_capture_data_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break; + default: { + + if (!inst) { + ERR_FAIL(); + } + } + } + + ERR_FAIL_COND(!inst); + + inst->instance_list.remove(&p_instance->dependency_item); } /* RENDER TARGET */ @@ -3632,7 +3992,7 @@ void RasterizerStorageGLES2::_render_target_allocate(RenderTarget *rt) { glGenRenderbuffers(1, &rt->depth); glBindRenderbuffer(GL_RENDERBUFFER, rt->depth); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, rt->width, rt->height); + glRenderbufferStorage(GL_RENDERBUFFER, _DEPTH_COMPONENT24_OES, rt->width, rt->height); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rt->depth); GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); @@ -3848,6 +4208,10 @@ VS::InstanceType RasterizerStorageGLES2::get_base_type(RID p_rid) const { return VS::INSTANCE_MULTIMESH; } else if (immediate_owner.owns(p_rid)) { return VS::INSTANCE_IMMEDIATE; + } else if (reflection_probe_owner.owns(p_rid)) { + return VS::INSTANCE_REFLECTION_PROBE; + } else if (lightmap_capture_data_owner.owns(p_rid)) { + return VS::INSTANCE_LIGHTMAP_CAPTURE; } else { return VS::INSTANCE_NONE; } @@ -4025,6 +4389,25 @@ bool RasterizerStorageGLES2::free(RID p_rid) { memdelete(light); return true; + } else if (reflection_probe_owner.owns(p_rid)) { + + // delete the texture + ReflectionProbe *reflection_probe = reflection_probe_owner.get(p_rid); + reflection_probe->instance_remove_deps(); + + reflection_probe_owner.free(p_rid); + memdelete(reflection_probe); + + return true; + } else if (lightmap_capture_data_owner.owns(p_rid)) { + + // delete the texture + LightmapCapture *lightmap_capture = lightmap_capture_data_owner.get(p_rid); + lightmap_capture->instance_remove_deps(); + + lightmap_capture_data_owner.free(p_rid); + memdelete(lightmap_capture); + return true; } else { return false; } @@ -4076,15 +4459,15 @@ void RasterizerStorageGLES2::initialize() { } 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; + + config.float_texture_supported = config.extensions.has("GL_ARB_texture_float") || config.extensions.has("GL_OES_texture_float"); + config.s3tc_supported = config.extensions.has("GL_EXT_texture_compression_s3tc"); + config.etc1_supported = config.extensions.has("GL_OES_compressed_ETC1_RGB8_texture"); frame.count = 0; frame.delta = 0; frame.current_rt = NULL; frame.clear_request = false; - // config.keep_original_textures = false; glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &config.max_texture_image_units); glGetIntegerv(GL_MAX_TEXTURE_SIZE, &config.max_texture_size); @@ -4187,13 +4570,13 @@ void RasterizerStorageGLES2::initialize() { // radical inverse vdc cache texture // used for cubemap filtering - if (config.float_texture_supported) { + if (true /*||config.float_texture_supported*/) { //uint8 is similar and works everywhere 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]; + uint8_t radical_inverse[512]; for (uint32_t i = 0; i < 512; i++) { uint32_t bits = i; @@ -4205,14 +4588,23 @@ void RasterizerStorageGLES2::initialize() { bits = ((bits & 0x00FF00FF) << 8) | ((bits & 0xFF00FF00) >> 8); float value = float(bits) * 2.3283064365386963e-10; - - radical_inverse[i] = value; + radical_inverse[i] = uint8_t(CLAMP(value * 255.0, 0, 255)); } - glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 512, 1, 0, GL_LUMINANCE, GL_FLOAT, radical_inverse); + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 512, 1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, radical_inverse); glBindTexture(GL_TEXTURE_2D, 0); } + +#ifdef GLES_OVER_GL + //this needs to be enabled manually in OpenGL 2.1 + + glEnable(_EXT_TEXTURE_CUBE_MAP_SEAMLESS); + glEnable(GL_POINT_SPRITE); + glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); +#endif + + config.force_vertex_shading = GLOBAL_GET("rendering/quality/shading/force_vertex_shading"); } void RasterizerStorageGLES2::finalize() { @@ -4227,6 +4619,7 @@ void RasterizerStorageGLES2::update_dirty_resources() { update_dirty_shaders(); update_dirty_materials(); update_dirty_skeletons(); + update_dirty_multimeshes(); } RasterizerStorageGLES2::RasterizerStorageGLES2() { diff --git a/drivers/gles2/rasterizer_storage_gles2.h b/drivers/gles2/rasterizer_storage_gles2.h index 88783d7160..04a6f72710 100644 --- a/drivers/gles2/rasterizer_storage_gles2.h +++ b/drivers/gles2/rasterizer_storage_gles2.h @@ -27,11 +27,12 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef RASTERIZERSTORAGEGLES2_H #define RASTERIZERSTORAGEGLES2_H -#include "dvector.h" -#include "self_list.h" +#include "core/dvector.h" +#include "core/self_list.h" #include "servers/visual/rasterizer.h" #include "servers/visual/shader_language.h" #include "shader_compiler_gles2.h" @@ -42,7 +43,6 @@ /* #include "shaders/blend_shape.glsl.gen.h" #include "shaders/canvas.glsl.gen.h" -#include "shaders/copy.glsl.gen.h" #include "shaders/particles.glsl.gen.h" */ @@ -156,7 +156,7 @@ public: //////////////////////////////////DATA/////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////// - struct Instanciable : public RID_Data { + struct Instantiable : public RID_Data { SelfList<RasterizerScene::InstanceBase>::List instance_list; _FORCE_INLINE_ void instance_change_notify() { @@ -186,15 +186,15 @@ public: } } - Instanciable() {} + Instantiable() {} - virtual ~Instanciable() {} + virtual ~Instantiable() {} }; - struct GeometryOwner : public Instanciable { + struct GeometryOwner : public Instantiable { }; - struct Geometry : public Instanciable { + struct Geometry : public Instantiable { enum Type { GEOMETRY_INVALID, @@ -405,6 +405,9 @@ public: String path; + uint32_t index; + uint64_t last_pass; + struct CanvasItem { enum BlendMode { @@ -426,6 +429,7 @@ public: int light_mode; */ + bool uses_screen_texture; bool uses_screen_uv; bool uses_time; @@ -490,6 +494,7 @@ public: valid = false; custom_code_id = 0; version = 1; + last_pass = 0; } }; @@ -562,6 +567,7 @@ public: virtual void material_set_param(RID p_material, const StringName &p_param, const Variant &p_value); virtual Variant material_get_param(RID p_material, const StringName &p_param) const; + virtual Variant material_get_param_default(RID p_material, const StringName &p_param) const; virtual void material_set_line_width(RID p_material, float p_width); virtual void material_set_next_pass(RID p_material, RID p_next_material); @@ -887,7 +893,7 @@ public: /* Light API */ - struct Light : Instanciable { + struct Light : Instantiable { VS::LightType type; float param[VS::LIGHT_PARAM_MAX]; @@ -949,6 +955,26 @@ public: virtual uint64_t light_get_version(RID p_light) const; /* PROBE API */ + + struct ReflectionProbe : Instantiable { + + VS::ReflectionProbeUpdateMode update_mode; + float intensity; + Color interior_ambient; + float interior_ambient_energy; + float interior_ambient_probe_contrib; + float max_distance; + Vector3 extents; + Vector3 origin_offset; + bool interior; + bool box_projection; + bool enable_shadows; + uint32_t cull_mask; + int resolution; + }; + + mutable RID_Owner<ReflectionProbe> reflection_probe_owner; + virtual RID reflection_probe_create(); virtual void reflection_probe_set_update_mode(RID p_probe, VS::ReflectionProbeUpdateMode p_mode); @@ -963,11 +989,14 @@ public: virtual void reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable); virtual void reflection_probe_set_enable_shadows(RID p_probe, bool p_enable); virtual void reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers); + virtual void reflection_probe_set_resolution(RID p_probe, int p_resolution); virtual AABB reflection_probe_get_aabb(RID p_probe) const; virtual VS::ReflectionProbeUpdateMode reflection_probe_get_update_mode(RID p_probe) const; virtual uint32_t reflection_probe_get_cull_mask(RID p_probe) const; + virtual int reflection_probe_get_resolution(RID p_probe) const; + virtual Vector3 reflection_probe_get_extents(RID p_probe) const; virtual Vector3 reflection_probe_get_origin_offset(RID p_probe) const; virtual float reflection_probe_get_origin_max_distance(RID p_probe) const; @@ -1017,6 +1046,21 @@ public: /* LIGHTMAP */ + struct LightmapCapture : public Instantiable { + + PoolVector<LightmapCaptureOctree> octree; + AABB bounds; + Transform cell_xform; + int cell_subdiv; + float energy; + LightmapCapture() { + energy = 1.0; + cell_subdiv = 1; + } + }; + + mutable RID_Owner<LightmapCapture> lightmap_capture_data_owner; + virtual RID lightmap_capture_create(); virtual void lightmap_capture_set_bounds(RID p_capture, const AABB &p_bounds); virtual AABB lightmap_capture_get_bounds(RID p_capture) const; diff --git a/drivers/gles2/shader_compiler_gles2.cpp b/drivers/gles2/shader_compiler_gles2.cpp index 1c87b3ffb5..082c520480 100644 --- a/drivers/gles2/shader_compiler_gles2.cpp +++ b/drivers/gles2/shader_compiler_gles2.cpp @@ -27,11 +27,13 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "shader_compiler_gles2.h" -#include "os/os.h" -#include "string_buffer.h" -#include "string_builder.h" +#include "core/os/os.h" +#include "core/project_settings.h" +#include "core/string_buffer.h" +#include "core/string_builder.h" #define SL ShaderLanguage @@ -641,11 +643,11 @@ String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, Gener case SL::OP_MOD: { - code += "mod("; + code += "mod(float("; code += _dump_node_code(op_node->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - code += ", "; + code += "), float("; code += _dump_node_code(op_node->arguments[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - code += ")"; + code += "))"; } break; default: { @@ -829,6 +831,7 @@ ShaderCompilerGLES2::ShaderCompilerGLES2() { actions[VS::SHADER_SPATIAL].renames["POINT_SIZE"] = "gl_PointSize"; // gl_InstanceID is not available in OpenGL ES 2.0 actions[VS::SHADER_SPATIAL].renames["INSTANCE_ID"] = "0"; + actions[VS::SHADER_SPATIAL].renames["OUTPUT_IS_SRGB"] = "SHADER_IS_SRGB"; //builtins @@ -899,16 +902,30 @@ ShaderCompilerGLES2::ShaderCompilerGLES2() { actions[VS::SHADER_SPATIAL].render_mode_defines["skip_vertex_transform"] = "#define SKIP_TRANSFORM_USED\n"; actions[VS::SHADER_SPATIAL].render_mode_defines["world_vertex_coords"] = "#define VERTEX_WORLD_COORDS_USED\n"; - actions[VS::SHADER_SPATIAL].render_mode_defines["diffuse_burley"] = "#define DIFFUSE_BURLEY\n"; + bool force_lambert = GLOBAL_GET("rendering/quality/shading/force_lambert_over_burley"); + + if (!force_lambert) { + actions[VS::SHADER_SPATIAL].render_mode_defines["diffuse_burley"] = "#define DIFFUSE_BURLEY\n"; + } + actions[VS::SHADER_SPATIAL].render_mode_defines["diffuse_oren_nayar"] = "#define DIFFUSE_OREN_NAYAR\n"; actions[VS::SHADER_SPATIAL].render_mode_defines["diffuse_lambert_wrap"] = "#define DIFFUSE_LAMBERT_WRAP\n"; actions[VS::SHADER_SPATIAL].render_mode_defines["diffuse_toon"] = "#define DIFFUSE_TOON\n"; - actions[VS::SHADER_SPATIAL].render_mode_defines["specular_schlick_ggx"] = "#define SPECULAR_SCHLICK_GGX\n"; + bool force_blinn = GLOBAL_GET("rendering/quality/shading/force_blinn_over_ggx"); + + if (!force_blinn) { + actions[VS::SHADER_SPATIAL].render_mode_defines["specular_schlick_ggx"] = "#define SPECULAR_SCHLICK_GGX\n"; + } else { + actions[VS::SHADER_SPATIAL].render_mode_defines["specular_schlick_ggx"] = "#define SPECULAR_BLINN\n"; + } + actions[VS::SHADER_SPATIAL].render_mode_defines["specular_blinn"] = "#define SPECULAR_BLINN\n"; actions[VS::SHADER_SPATIAL].render_mode_defines["specular_phong"] = "#define SPECULAR_PHONG\n"; actions[VS::SHADER_SPATIAL].render_mode_defines["specular_toon"] = "#define SPECULAR_TOON\n"; actions[VS::SHADER_SPATIAL].render_mode_defines["specular_disabled"] = "#define SPECULAR_DISABLED\n"; + actions[VS::SHADER_SPATIAL].render_mode_defines["shadows_disabled"] = "#define SHADOWS_DISABLED\n"; + actions[VS::SHADER_SPATIAL].render_mode_defines["ambient_light_disabled"] = "#define AMBIENT_LIGHT_DISABLED\n"; /* PARTICLES SHADER */ diff --git a/drivers/gles2/shader_compiler_gles2.h b/drivers/gles2/shader_compiler_gles2.h index 804ead2172..5e9e295204 100644 --- a/drivers/gles2/shader_compiler_gles2.h +++ b/drivers/gles2/shader_compiler_gles2.h @@ -27,16 +27,16 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef SHADERCOMPILERGLES2_H #define SHADERCOMPILERGLES2_H -#include "pair.h" +#include "core/pair.h" +#include "core/string_builder.h" #include "servers/visual/shader_language.h" #include "servers/visual/shader_types.h" #include "servers/visual_server.h" -#include "string_builder.h" - class ShaderCompilerGLES2 { public: struct IdentifierActions { diff --git a/drivers/gles2/shader_gles2.cpp b/drivers/gles2/shader_gles2.cpp index 3b2a29d3ee..628a57c06d 100644 --- a/drivers/gles2/shader_gles2.cpp +++ b/drivers/gles2/shader_gles2.cpp @@ -27,12 +27,12 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "shader_gles2.h" -#include "memory.h" -#include "print_string.h" -#include "string_builder.h" +#include "shader_gles2.h" +#include "core/os/memory.h" +#include "core/print_string.h" +#include "core/string_builder.h" #include "rasterizer_gles2.h" #include "rasterizer_storage_gles2.h" @@ -57,7 +57,7 @@ ShaderGLES2 *ShaderGLES2::active = NULL; -// #define DEBUG_SHADER +//#define DEBUG_SHADER #ifdef DEBUG_SHADER @@ -132,6 +132,11 @@ bool ShaderGLES2::bind() { ERR_FAIL_COND_V(!version, false); + if (!version->ok) { //broken, unable to bind (do not throw error, you saw it before already when it failed compilation). + glUseProgram(0); + return false; + } + glUseProgram(version->id); // find out uniform names and locations @@ -171,72 +176,24 @@ void ShaderGLES2::unbind() { active = NULL; } -static String _fix_error_code_line(const String &p_error, int p_code_start, int p_offset) { - - int last_find_pos = -1; - // NVIDIA - String error = p_error; - while ((last_find_pos = p_error.find("(", last_find_pos + 1)) != -1) { - - int end_pos = last_find_pos + 1; - - while (true) { +static void _display_error_with_code(const String &p_error, const Vector<const char *> &p_code) { - if (p_error[end_pos] >= '0' && p_error[end_pos] <= '9') { - - end_pos++; - continue; - } else if (p_error[end_pos] == ')') { - break; - } else { - - end_pos = -1; - break; - } - } - - if (end_pos == -1) - continue; + int line = 1; + String total_code; - String numstr = error.substr(last_find_pos + 1, (end_pos - last_find_pos) - 1); - String begin = error.substr(0, last_find_pos + 1); - String end = error.substr(end_pos, error.length()); - int num = numstr.to_int() + p_code_start - p_offset; - error = begin + itos(num) + end; + for (int i = 0; i < p_code.size(); i++) { + total_code += String(p_code[i]); } - // ATI - last_find_pos = -1; - while ((last_find_pos = p_error.find("ERROR: ", last_find_pos + 1)) != -1) { + Vector<String> lines = String(total_code).split("\n"); - last_find_pos += 6; - int end_pos = last_find_pos + 1; + for (int j = 0; j < lines.size(); j++) { - while (true) { - - if (p_error[end_pos] >= '0' && p_error[end_pos] <= '9') { - - end_pos++; - continue; - } else if (p_error[end_pos] == ':') { - break; - } else { - - end_pos = -1; - break; - } - } - continue; - if (end_pos == -1) - continue; - - String numstr = error.substr(last_find_pos + 1, (end_pos - last_find_pos) - 1); - String begin = error.substr(0, last_find_pos + 1); - String end = error.substr(end_pos, error.length()); - int num = numstr.to_int() + p_code_start - p_offset; - error = begin + itos(num) + end; + print_line(itos(line) + ": " + lines[j]); + line++; } - return error; + + ERR_PRINTS(p_error); } ShaderGLES2::Version *ShaderGLES2::get_current_version() { @@ -293,7 +250,7 @@ ShaderGLES2::Version *ShaderGLES2::get_current_version() { } } - // keep them around during the functino + // keep them around during the function CharString code_string; CharString code_string2; CharString code_globals; @@ -316,7 +273,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.write[i]); - DEBUG_PRINT("CD #" + itos(i) + ": " + String(cc->custom_defines[i])); + DEBUG_PRINT("CD #" + itos(i) + ": " + String(cc->custom_defines[i].get_data())); } } @@ -375,9 +332,8 @@ ShaderGLES2::Version *ShaderGLES2::get_current_version() { String err_string = get_shader_name() + ": Vertex shader compilation failed:\n"; err_string += ilogmem; - err_string = _fix_error_code_line(err_string, vertex_code_start, define_line_ofs); - ERR_PRINTS(err_string); + _display_error_with_code(err_string, strings); Memory::free_static(ilogmem); glDeleteShader(v.vert_id); @@ -451,9 +407,8 @@ ShaderGLES2::Version *ShaderGLES2::get_current_version() { String err_string = get_shader_name() + ": Fragment shader compilation failed:\n"; err_string += ilogmem; - err_string = _fix_error_code_line(err_string, fragment_code_start, define_line_ofs); - ERR_PRINTS(err_string); + _display_error_with_code(err_string, strings); Memory::free_static(ilogmem); glDeleteShader(v.frag_id); @@ -503,9 +458,8 @@ ShaderGLES2::Version *ShaderGLES2::get_current_version() { String err_string = get_shader_name() + ": Program linking failed:\n"; err_string += ilogmem; - err_string = _fix_error_code_line(err_string, fragment_code_start, define_line_ofs); - ERR_PRINTS(err_string); + _display_error_with_code(err_string, strings); Memory::free_static(ilogmem); glDeleteShader(v.frag_id); @@ -736,11 +690,6 @@ void ShaderGLES2::use_material(void *p_material) { 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()) { @@ -1028,7 +977,7 @@ void ShaderGLES2::use_material(void *p_material) { value.second.resize(default_arg_size); - for (int i = 0; i < default_arg_size; i++) { + for (size_t i = 0; i < default_arg_size; i++) { if (is_float) { value.second.write[i].real = 0.0; } else { @@ -1038,8 +987,6 @@ void ShaderGLES2::use_material(void *p_material) { } } - // GLint location = get_uniform_location(E->key()); - GLint location; if (v->custom_uniform_locations.has(E->key())) { location = v->custom_uniform_locations[E->key()]; @@ -1059,8 +1006,6 @@ void ShaderGLES2::use_material(void *p_material) { 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; diff --git a/drivers/gles2/shader_gles2.h b/drivers/gles2/shader_gles2.h index cb515c199c..9160a7c265 100644 --- a/drivers/gles2/shader_gles2.h +++ b/drivers/gles2/shader_gles2.h @@ -27,11 +27,11 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef SHADER_GLES2_H #define SHADER_GLES2_H -#include <stdio.h> - +// This must come first to avoid windows.h mess #include "platform_config.h" #ifndef GLES2_INCLUDE_H #include <GLES2/gl2.h> @@ -39,14 +39,15 @@ #include GLES2_INCLUDE_H #endif -#include "camera_matrix.h" -#include "hash_map.h" -#include "map.h" -#include "variant.h" - +#include "core/hash_map.h" +#include "core/map.h" +#include "core/math/camera_matrix.h" #include "core/pair.h" +#include "core/variant.h" #include "servers/visual/shader_language.h" +#include <stdio.h> + class RasterizerStorageGLES2; class ShaderGLES2 { @@ -334,6 +335,19 @@ public: case ShaderLanguage::TYPE_SAMPLERCUBE: { } break; + + case ShaderLanguage::TYPE_SAMPLER2DARRAY: + case ShaderLanguage::TYPE_ISAMPLER2DARRAY: + case ShaderLanguage::TYPE_USAMPLER2DARRAY: + case ShaderLanguage::TYPE_SAMPLER3D: + case ShaderLanguage::TYPE_ISAMPLER3D: + case ShaderLanguage::TYPE_USAMPLER3D: { + // Not implemented in GLES2 + } break; + + case ShaderLanguage::TYPE_VOID: { + // Nothing to do? + } break; } } @@ -467,7 +481,8 @@ public: // like forward declared nested classes. void use_material(void *p_material); - uint32_t get_version() const { return new_conditional_version.version; } + _FORCE_INLINE_ uint32_t get_version() const { return new_conditional_version.version; } + _FORCE_INLINE_ bool is_version_valid() const { return version && version->ok; } void set_uniform_camera(int p_idx, const CameraMatrix &p_mat) { diff --git a/drivers/gles2/shaders/SCsub b/drivers/gles2/shaders/SCsub index acb93fff8f..d959d3f740 100644 --- a/drivers/gles2/shaders/SCsub +++ b/drivers/gles2/shaders/SCsub @@ -20,3 +20,4 @@ if 'GLES2_GLSL' in env['BUILDERS']: # env.GLES2_GLSL('exposure.glsl'); # env.GLES2_GLSL('tonemap.glsl'); # env.GLES2_GLSL('particles.glsl'); + env.GLES2_GLSL('lens_distorted.glsl'); diff --git a/drivers/gles2/shaders/blend_shape.glsl b/drivers/gles2/shaders/blend_shape.glsl index d019062ba0..a1e954e33d 100644 --- a/drivers/gles2/shaders/blend_shape.glsl +++ b/drivers/gles2/shaders/blend_shape.glsl @@ -1,3 +1,4 @@ +/* clang-format off */ [vertex] /* @@ -23,6 +24,7 @@ ARRAY_INDEX=8, /* INPUT ATTRIBS */ layout(location = 0) in highp VFORMAT vertex_attrib; +/* clang-format on */ layout(location = 1) in vec3 normal_attrib; #ifdef ENABLE_TANGENT @@ -183,8 +185,10 @@ void main() { gl_Position = vec4(0.0); } +/* clang-format off */ [fragment] void main() { } +/* clang-format on */ diff --git a/drivers/gles2/shaders/canvas.glsl b/drivers/gles2/shaders/canvas.glsl index cee68a58ff..79d4eb2243 100644 --- a/drivers/gles2/shaders/canvas.glsl +++ b/drivers/gles2/shaders/canvas.glsl @@ -1,3 +1,4 @@ +/* clang-format off */ [vertex] #ifdef USE_GLES_OVER_GL @@ -9,6 +10,7 @@ precision mediump int; #endif uniform highp mat4 projection_matrix; +/* clang-format on */ uniform highp mat4 modelview_matrix; uniform highp mat4 extra_matrix; attribute highp vec2 vertex; // attrib:0 @@ -29,8 +31,12 @@ uniform vec4 src_rect; uniform highp float time; +/* clang-format off */ + VERTEX_SHADER_GLOBALS +/* clang-format on */ + vec2 select(vec2 a, vec2 b, bvec2 c) { vec2 ret; @@ -74,17 +80,30 @@ void main() { #endif -{ - vec2 src_vtx = outvec.xy; + { + vec2 src_vtx = outvec.xy; + /* clang-format off */ + VERTEX_SHADER_CODE -} + /* clang-format on */ + } + +#if !defined(SKIP_TRANSFORM_USED) + outvec = extra_matrix * outvec; + outvec = modelview_matrix * outvec; +#endif color_interp = color; - gl_Position = projection_matrix * modelview_matrix * outvec; +#ifdef USE_PIXEL_SNAP + outvec.xy = floor(outvec + 0.5).xy; +#endif + + gl_Position = projection_matrix * outvec; } +/* clang-format off */ [fragment] #ifdef USE_GLES_OVER_GL @@ -96,6 +115,7 @@ precision mediump int; #endif uniform sampler2D color_texture; // texunit:-1 +/* clang-format on */ uniform highp vec2 color_texpixel_size; uniform mediump sampler2D normal_texture; // texunit:-2 @@ -118,22 +138,31 @@ uniform vec2 screen_pixel_size; #endif +/* clang-format off */ + FRAGMENT_SHADER_GLOBALS +/* clang-format on */ + void main() { vec4 color = color_interp; +#if !defined(COLOR_USED) + //default behavior, texture by color color *= texture2D(color_texture, uv_interp); +#endif #ifdef SCREEN_UV_USED vec2 screen_uv = gl_FragCoord.xy * screen_pixel_size; #endif -{ + { + /* clang-format off */ FRAGMENT_SHADER_CODE -} + /* clang-format on */ + } color *= final_modulate; diff --git a/drivers/gles2/shaders/canvas_shadow.glsl b/drivers/gles2/shaders/canvas_shadow.glsl index 81af486101..e3c8140e31 100644 --- a/drivers/gles2/shaders/canvas_shadow.glsl +++ b/drivers/gles2/shaders/canvas_shadow.glsl @@ -1,6 +1,8 @@ +/* clang-format off */ [vertex] uniform highp mat4 projection_matrix; +/* clang-format on */ uniform highp mat4 light_matrix; uniform highp mat4 world_matrix; uniform highp float distance_norm; @@ -15,9 +17,11 @@ void main() { position_interp = gl_Position; } +/* clang-format off */ [fragment] in highp vec4 position_interp; +/* clang-format on */ #ifdef USE_RGBA_SHADOWS diff --git a/drivers/gles2/shaders/copy.glsl b/drivers/gles2/shaders/copy.glsl index 6e9aad2a5d..0b8da4f875 100644 --- a/drivers/gles2/shaders/copy.glsl +++ b/drivers/gles2/shaders/copy.glsl @@ -1,3 +1,4 @@ +/* clang-format off */ [vertex] #ifdef USE_GLES_OVER_GL @@ -9,6 +10,7 @@ precision mediump int; #endif attribute highp vec4 vertex_attrib; // attrib:0 +/* clang-format on */ #if defined(USE_CUBEMAP) || defined(USE_PANORAMA) attribute vec3 cube_in; // attrib:4 @@ -33,6 +35,8 @@ void main() { #if defined(USE_CUBEMAP) || defined(USE_PANORAMA) cube_interp = cube_in; +#elif defined(USE_ASYM_PANO) + uv_interp = vertex_attrib.xy; #else uv_interp = uv_in; #endif @@ -46,6 +50,7 @@ void main() { #endif } +/* clang-format off */ [fragment] #define M_PI 3.14159265359 @@ -63,6 +68,12 @@ varying vec3 cube_interp; #else varying vec2 uv_interp; #endif +/* clang-format on */ + +#ifdef USE_ASYM_PANO +uniform highp mat4 pano_transform; +uniform highp vec4 asym_proj; +#endif #ifdef USE_CUBEMAP uniform samplerCube source_cube; // texunit:0 @@ -104,6 +115,21 @@ void main() { vec4 color = texturePanorama(source, normalize(cube_interp)); +#elif defined(USE_ASYM_PANO) + + // When an asymmetrical projection matrix is used (applicable for stereoscopic rendering i.e. VR) we need to do this calculation per fragment to get a perspective correct result. + // Note that we're ignoring the x-offset for IPD, with Z sufficiently in the distance it becomes neglectible, as a result we could probably just set cube_normal.z to -1. + // The Matrix[2][0] (= asym_proj.x) and Matrix[2][1] (= asym_proj.z) values are what provide the right shift in the image. + + vec3 cube_normal; + cube_normal.z = -1000000.0; + cube_normal.x = (cube_normal.z * (-uv_interp.x - asym_proj.x)) / asym_proj.y; + cube_normal.y = (cube_normal.z * (-uv_interp.y - asym_proj.z)) / asym_proj.a; + cube_normal = mat3(pano_transform) * cube_normal; + cube_normal.z = -cube_normal.z; + + vec4 color = texturePanorama(source, normalize(cube_normal.xyz)); + #elif defined(USE_CUBEMAP) vec4 color = textureCube(source_cube, normalize(cube_interp)); #else diff --git a/drivers/gles2/shaders/cube_to_dp.glsl b/drivers/gles2/shaders/cube_to_dp.glsl index b44aab8946..3d24c36336 100644 --- a/drivers/gles2/shaders/cube_to_dp.glsl +++ b/drivers/gles2/shaders/cube_to_dp.glsl @@ -1,3 +1,4 @@ +/* clang-format off */ [vertex] #ifdef USE_GLES_OVER_GL @@ -9,6 +10,7 @@ precision mediump int; #endif attribute highp vec4 vertex_attrib; // attrib:0 +/* clang-format on */ attribute vec2 uv_in; // attrib:4 varying vec2 uv_interp; @@ -19,6 +21,7 @@ void main() { gl_Position = vertex_attrib; } +/* clang-format off */ [fragment] #ifdef USE_GLES_OVER_GL @@ -30,6 +33,7 @@ precision mediump int; #endif uniform highp samplerCube source_cube; //texunit:0 +/* clang-format on */ varying vec2 uv_interp; uniform bool z_flip; diff --git a/drivers/gles2/shaders/cubemap_filter.glsl b/drivers/gles2/shaders/cubemap_filter.glsl index 0d60104480..b1553c7cd5 100644 --- a/drivers/gles2/shaders/cubemap_filter.glsl +++ b/drivers/gles2/shaders/cubemap_filter.glsl @@ -1,3 +1,4 @@ +/* clang-format off */ [vertex] #ifdef USE_GLES_OVER_GL @@ -9,6 +10,7 @@ precision mediump int; #endif attribute highp vec2 vertex; // attrib:0 +/* clang-format on */ attribute highp vec2 uv; // attrib:4 varying highp vec2 uv_interp; @@ -19,6 +21,7 @@ void main() { gl_Position = vec4(vertex, 0, 1); } +/* clang-format off */ [fragment] #extension GL_ARB_shader_texture_lod : enable @@ -41,6 +44,7 @@ uniform sampler2D source_panorama; //texunit:0 #else uniform samplerCube source_cube; //texunit:0 #endif +/* clang-format on */ uniform int face_id; uniform float roughness; @@ -163,18 +167,21 @@ void main() { vec3 H = ImportanceSampleGGX(xi, roughness, N); vec3 V = N; - vec3 L = normalize(2.0 * dot(V, H) * H - V); + vec3 L = (2.0 * dot(V, H) * H - V); float NdotL = clamp(dot(N, L), 0.0, 1.0); if (NdotL > 0.0) { #ifdef USE_SOURCE_PANORAMA - sum.rgb += texturePanorama(source_panorama, L).rgb * NdotL; + vec3 val = texturePanorama(source_panorama, L).rgb; #else - L.y = -L.y; - sum.rgb += textureCubeLod(source_cube, L, 0.0).rgb * NdotL; + vec3 val = textureCubeLod(source_cube, L, 0.0).rgb; #endif + //mix using Linear, to approximate high end back-end + val = mix(pow((val + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), val * (1.0 / 12.92), vec3(lessThan(val, vec3(0.04045)))); + + sum.rgb += val * NdotL; sum.a += NdotL; } @@ -182,5 +189,8 @@ void main() { sum /= sum.a; + vec3 a = vec3(0.055); + sum.rgb = mix((vec3(1.0) + a) * pow(sum.rgb, vec3(1.0 / 2.4)) - a, 12.92 * sum.rgb, vec3(lessThan(sum.rgb, vec3(0.0031308)))); + gl_FragColor = vec4(sum.rgb, 1.0); } diff --git a/drivers/gles2/shaders/effect_blur.glsl b/drivers/gles2/shaders/effect_blur.glsl index 0e2d28af20..a531802c75 100644 --- a/drivers/gles2/shaders/effect_blur.glsl +++ b/drivers/gles2/shaders/effect_blur.glsl @@ -1,6 +1,8 @@ +/* clang-format off */ [vertex] layout(location = 0) in highp vec4 vertex_attrib; +/* clang-format on */ layout(location = 4) in vec2 uv_in; out vec2 uv_interp; @@ -22,6 +24,7 @@ void main() { #endif } +/* clang-format off */ [fragment] #if !defined(GLES_OVER_GL) @@ -29,6 +32,7 @@ precision mediump float; #endif in vec2 uv_interp; +/* clang-format on */ uniform sampler2D source_color; //texunit:0 #ifdef SSAO_MERGE diff --git a/drivers/gles2/shaders/exposure.glsl b/drivers/gles2/shaders/exposure.glsl index 18fff1ae36..759adcda06 100644 --- a/drivers/gles2/shaders/exposure.glsl +++ b/drivers/gles2/shaders/exposure.glsl @@ -1,15 +1,19 @@ +/* clang-format off */ [vertex] layout(location = 0) in highp vec4 vertex_attrib; +/* clang-format on */ void main() { gl_Position = vertex_attrib; } +/* clang-format off */ [fragment] uniform highp sampler2D source_exposure; //texunit:0 +/* clang-format on */ #ifdef EXPOSURE_BEGIN diff --git a/drivers/gles2/shaders/lens_distorted.glsl b/drivers/gles2/shaders/lens_distorted.glsl new file mode 100644 index 0000000000..d541db9bf9 --- /dev/null +++ b/drivers/gles2/shaders/lens_distorted.glsl @@ -0,0 +1,62 @@ +/* clang-format off */ +[vertex] + +attribute highp vec2 vertex; // attrib:0 +/* clang-format on */ + +uniform vec2 offset; +uniform vec2 scale; + +varying vec2 uv_interp; + +void main() { + + uv_interp = vertex.xy * 2.0 - 1.0; + + vec2 v = vertex.xy * scale + offset; + gl_Position = vec4(v, 0.0, 1.0); +} + +/* clang-format off */ +[fragment] + +uniform sampler2D source; //texunit:0 +/* clang-format on */ + +uniform vec2 eye_center; +uniform float k1; +uniform float k2; +uniform float upscale; +uniform float aspect_ratio; + +varying vec2 uv_interp; + +void main() { + vec2 coords = uv_interp; + vec2 offset = coords - eye_center; + + // take aspect ratio into account + offset.y /= aspect_ratio; + + // distort + vec2 offset_sq = offset * offset; + float radius_sq = offset_sq.x + offset_sq.y; + float radius_s4 = radius_sq * radius_sq; + float distortion_scale = 1.0 + (k1 * radius_sq) + (k2 * radius_s4); + offset *= distortion_scale; + + // reapply aspect ratio + offset.y *= aspect_ratio; + + // add our eye center back in + coords = offset + eye_center; + coords /= upscale; + + // and check our color + if (coords.x < -1.0 || coords.y < -1.0 || coords.x > 1.0 || coords.y > 1.0) { + gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); + } else { + coords = (coords + vec2(1.0)) / vec2(2.0); + gl_FragColor = texture2D(source, coords); + } +} diff --git a/drivers/gles2/shaders/particles.glsl b/drivers/gles2/shaders/particles.glsl index 5af4b65b28..5974050fc1 100644 --- a/drivers/gles2/shaders/particles.glsl +++ b/drivers/gles2/shaders/particles.glsl @@ -1,6 +1,8 @@ +/* clang-format off */ [vertex] layout(location = 0) in highp vec4 color; +/* clang-format on */ layout(location = 1) in highp vec4 velocity_active; layout(location = 2) in highp vec4 custom; layout(location = 3) in highp vec4 xform_1; @@ -45,16 +47,22 @@ out highp vec4 out_xform_3; //tfb: #if defined(USE_MATERIAL) +/* clang-format off */ layout(std140) uniform UniformData { //ubo:0 MATERIAL_UNIFORMS }; +/* clang-format on */ #endif +/* clang-format off */ + VERTEX_SHADER_GLOBALS +/* clang-format on */ + uint hash(uint x) { x = ((x >> uint(16)) ^ x) * uint(0x45d9f3b); @@ -165,9 +173,11 @@ void main() { //execute shader { + /* clang-format off */ VERTEX_SHADER_CODE + /* clang-format on */ } #if !defined(DISABLE_FORCE) @@ -223,6 +233,7 @@ VERTEX_SHADER_CODE #endif //PARTICLES_COPY } +/* clang-format off */ [fragment] //any code here is never executed, stuff is filled just so it works @@ -240,12 +251,16 @@ MATERIAL_UNIFORMS FRAGMENT_SHADER_GLOBALS void main() { - { + LIGHT_SHADER_CODE + } { + FRAGMENT_SHADER_CODE + } } +/* clang-format on */ diff --git a/drivers/gles2/shaders/resolve.glsl b/drivers/gles2/shaders/resolve.glsl index 05bb922eec..5c6f5d6561 100644 --- a/drivers/gles2/shaders/resolve.glsl +++ b/drivers/gles2/shaders/resolve.glsl @@ -1,6 +1,8 @@ +/* clang-format off */ [vertex] layout(location = 0) in highp vec4 vertex_attrib; +/* clang-format on */ layout(location = 4) in vec2 uv_in; out vec2 uv_interp; @@ -11,6 +13,7 @@ void main() { gl_Position = vertex_attrib; } +/* clang-format off */ [fragment] #if !defined(GLES_OVER_GL) @@ -18,6 +21,7 @@ precision mediump float; #endif in vec2 uv_interp; +/* clang-format on */ uniform sampler2D source_specular; //texunit:0 uniform sampler2D source_ssr; //texunit:1 diff --git a/drivers/gles2/shaders/scene.glsl b/drivers/gles2/shaders/scene.glsl index 8bcb3b5f1d..2a781605d1 100644 --- a/drivers/gles2/shaders/scene.glsl +++ b/drivers/gles2/shaders/scene.glsl @@ -1,35 +1,42 @@ +/* clang-format off */ [vertex] #ifdef USE_GLES_OVER_GL #define mediump #define highp #else -precision mediump float; -precision mediump int; +precision highp float; +precision highp int; #endif #include "stdlib.glsl" +#define SHADER_IS_SRGB true + +#define M_PI 3.14159265359 + + // // attributes // attribute highp vec4 vertex_attrib; // attrib:0 +/* clang-format on */ attribute vec3 normal_attrib; // attrib:1 #if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) attribute vec4 tangent_attrib; // attrib:2 #endif -#ifdef ENABLE_COLOR_INTERP +#if defined(ENABLE_COLOR_INTERP) attribute vec4 color_attrib; // attrib:3 #endif -#ifdef ENABLE_UV_INTERP +#if defined(ENABLE_UV_INTERP) attribute vec2 uv_attrib; // attrib:4 #endif -#ifdef ENABLE_UV2_INTERP +#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP) attribute vec2 uv2_attrib; // attrib:5 #endif @@ -37,9 +44,9 @@ attribute vec2 uv2_attrib; // attrib:5 #ifdef USE_SKELETON_SOFTWARE -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 +attribute highp vec4 bone_transform_row_0; // attrib:13 +attribute highp vec4 bone_transform_row_1; // attrib:14 +attribute highp vec4 bone_transform_row_2; // attrib:15 #else @@ -55,12 +62,12 @@ uniform ivec2 skeleton_texture_size; #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 +attribute highp vec4 instance_xform_row_0; // attrib:8 +attribute highp vec4 instance_xform_row_1; // attrib:9 +attribute highp vec4 instance_xform_row_2; // attrib:10 -attribute highp vec4 instance_color; // attrib:15 -attribute highp vec4 instance_custom_data; // attrib:8 +attribute highp vec4 instance_color; // attrib:11 +attribute highp vec4 instance_custom_data; // attrib:12 #endif @@ -68,12 +75,12 @@ attribute highp vec4 instance_custom_data; // attrib:8 // uniforms // -uniform mat4 camera_matrix; -uniform mat4 camera_inverse_matrix; -uniform mat4 projection_matrix; -uniform mat4 projection_inverse_matrix; +uniform highp mat4 camera_matrix; +uniform highp mat4 camera_inverse_matrix; +uniform highp mat4 projection_matrix; +uniform highp mat4 projection_inverse_matrix; -uniform mat4 world_transform; +uniform highp mat4 world_transform; uniform highp float time; @@ -96,20 +103,215 @@ varying vec3 tangent_interp; varying vec3 binormal_interp; #endif -#ifdef ENABLE_COLOR_INTERP +#if defined(ENABLE_COLOR_INTERP) varying vec4 color_interp; #endif -#ifdef ENABLE_UV_INTERP +#if defined(ENABLE_UV_INTERP) varying vec2 uv_interp; #endif -#ifdef ENABLE_UV2_INTERP +#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP) varying vec2 uv2_interp; #endif +/* clang-format off */ + VERTEX_SHADER_GLOBALS +/* clang-format on */ + +#ifdef RENDER_DEPTH_DUAL_PARABOLOID + +varying highp float dp_clip; +uniform highp float shadow_dual_paraboloid_render_zfar; +uniform highp float shadow_dual_paraboloid_render_side; + +#endif + +#if defined(USE_SHADOW) && defined(USE_LIGHTING) + +uniform highp mat4 light_shadow_matrix; +varying highp vec4 shadow_coord; + +#if defined(LIGHT_USE_PSSM2) || defined(LIGHT_USE_PSSM4) +uniform highp mat4 light_shadow_matrix2; +varying highp vec4 shadow_coord2; +#endif + +#if defined(LIGHT_USE_PSSM4) + +uniform highp mat4 light_shadow_matrix3; +uniform highp mat4 light_shadow_matrix4; +varying highp vec4 shadow_coord3; +varying highp vec4 shadow_coord4; + +#endif + +#endif + +#if defined(USE_VERTEX_LIGHTING) && defined(USE_LIGHTING) + +varying highp vec3 diffuse_interp; +varying highp vec3 specular_interp; + +// general for all lights +uniform highp vec4 light_color; +uniform highp float light_specular; + +// directional +uniform highp vec3 light_direction; + +// omni +uniform highp vec3 light_position; + +uniform highp float light_range; +uniform highp float light_attenuation; + +// spot +uniform highp float light_spot_attenuation; +uniform highp float light_spot_range; +uniform highp float light_spot_angle; + +void light_compute( + vec3 N, + vec3 L, + vec3 V, + vec3 light_color, + vec3 attenuation, + float roughness) { + +//this makes lights behave closer to linear, but then addition of lights looks bad +//better left disabled + +//#define SRGB_APPROX(m_var) m_var = pow(m_var,0.4545454545); +/* +#define SRGB_APPROX(m_var) {\ + float S1 = sqrt(m_var);\ + float S2 = sqrt(S1);\ + float S3 = sqrt(S2);\ + m_var = 0.662002687 * S1 + 0.684122060 * S2 - 0.323583601 * S3 - 0.0225411470 * m_var;\ + } +*/ +#define SRGB_APPROX(m_var) + + float NdotL = dot(N, L); + float cNdotL = max(NdotL, 0.0); // clamped NdotL + float NdotV = dot(N, V); + float cNdotV = max(NdotV, 0.0); + +#if defined(DIFFUSE_OREN_NAYAR) + vec3 diffuse_brdf_NL; +#else + float diffuse_brdf_NL; // BRDF times N.L for calculating diffuse radiance +#endif + +#if defined(DIFFUSE_LAMBERT_WRAP) + // energy conserving lambert wrap shader + diffuse_brdf_NL = max(0.0, (NdotL + roughness) / ((1.0 + roughness) * (1.0 + roughness))); + +#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); + } +#else + // lambert by default for everything else + diffuse_brdf_NL = cNdotL * (1.0 / M_PI); +#endif + + SRGB_APPROX(diffuse_brdf_NL) + + diffuse_interp += light_color * diffuse_brdf_NL * attenuation; + + if (roughness > 0.0) { + + // D + float specular_brdf_NL = 0.0; + +#if !defined(SPECULAR_DISABLED) + //normalized blinn always unless disabled + vec3 H = normalize(V + L); + float cNdotH = max(dot(N, H), 0.0); + float cVdotH = max(dot(V, H), 0.0); + float cLdotH = max(dot(L, H), 0.0); + float shininess = exp2(15.0 * (1.0 - roughness) + 1.0) * 0.25; + float blinn = pow(cNdotH, shininess); + blinn *= (shininess + 8.0) / (8.0 * 3.141592654); + specular_brdf_NL = (blinn) / max(4.0 * cNdotV * cNdotL, 0.75); +#endif + + SRGB_APPROX(specular_brdf_NL) + specular_interp += specular_brdf_NL * light_color * attenuation; + } +} + +#endif + +#ifdef USE_VERTEX_LIGHTING + +#ifdef USE_REFLECTION_PROBE1 + +uniform highp mat4 refprobe1_local_matrix; +varying mediump vec4 refprobe1_reflection_normal_blend; +uniform highp vec3 refprobe1_box_extents; + +#ifndef USE_LIGHTMAP +varying mediump vec3 refprobe1_ambient_normal; +#endif + +#endif //reflection probe1 + +#ifdef USE_REFLECTION_PROBE2 + +uniform highp mat4 refprobe2_local_matrix; +varying mediump vec4 refprobe2_reflection_normal_blend; +uniform highp vec3 refprobe2_box_extents; + +#ifndef USE_LIGHTMAP +varying mediump vec3 refprobe2_ambient_normal; +#endif + +#endif //reflection probe2 + +#endif //vertex lighting for refprobes + +#if defined(FOG_DEPTH_ENABLED) || defined(FOG_HEIGHT_ENABLED) + +varying vec4 fog_interp; + +uniform mediump vec4 fog_color_base; +#ifdef LIGHT_MODE_DIRECTIONAL +uniform mediump vec4 fog_sun_color_amount; +#endif + +uniform bool fog_transmit_enabled; +uniform mediump float fog_transmit_curve; + +#ifdef FOG_DEPTH_ENABLED +uniform highp float fog_depth_begin; +uniform mediump float fog_depth_curve; +uniform mediump float fog_max_distance; +#endif + +#ifdef FOG_HEIGHT_ENABLED +uniform highp float fog_height_min; +uniform highp float fog_height_max; +uniform mediump float fog_height_curve; +#endif + +#endif //fog + void main() { highp vec4 vertex = vertex_attrib; @@ -125,6 +327,7 @@ void main() { vec4(0.0, 0.0, 0.0, 1.0)); world_matrix = world_matrix * transpose(m); } + #endif vec3 normal = normal_attrib * normal_mult; @@ -136,18 +339,18 @@ void main() { vec3 binormal = normalize(cross(normal, tangent) * binormalf); #endif -#ifdef ENABLE_COLOR_INTERP +#if defined(ENABLE_COLOR_INTERP) color_interp = color_attrib; #ifdef USE_INSTANCING color_interp *= instance_color; #endif #endif -#ifdef ENABLE_UV_INTERP +#if defined(ENABLE_UV_INTERP) uv_interp = uv_attrib; #endif -#ifdef ENABLE_UV2_INTERP +#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP) uv2_interp = uv2_attrib; #endif @@ -156,7 +359,7 @@ void main() { normal = normalize((world_matrix * vec4(normal, 0.0)).xyz); #if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) - tangent = normalize((world_matrix * vec4(tangent, 0.0)), xyz); + tangent = normalize((world_matrix * vec4(tangent, 0.0)).xyz); binormal = normalize((world_matrix * vec4(binormal, 0.0)).xyz); #endif #endif @@ -202,15 +405,18 @@ void main() { #endif - mat4 modelview = camera_matrix * world_matrix; + mat4 modelview = camera_inverse_matrix * world_matrix; + float roughness = 1.0; #define world_transform world_matrix -{ + { + /* clang-format off */ VERTEX_SHADER_CODE -} + /* clang-format on */ + } vec4 outvec = vertex; @@ -226,11 +432,11 @@ VERTEX_SHADER_CODE #endif #if !defined(SKIP_TRANSFORM_USED) && defined(VERTEX_WORLD_COORDS_USED) - vertex = camera_matrix * vertex; - normal = normalize((camera_matrix * vec4(normal, 0.0)).xyz); + vertex = camera_inverse_matrix * vertex; + normal = normalize((camera_inverse_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); + tangent = normalize((camera_inverse_matrix * vec4(tangent, 0.0)).xyz); + binormal = normalize((camera_inverse_matrix * vec4(binormal, 0.0)).xyz); #endif #endif @@ -244,16 +450,201 @@ VERTEX_SHADER_CODE #ifdef RENDER_DEPTH +#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) * light_bias; + 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 = vtx; + +#else float z_ofs = light_bias; z_ofs += (1.0 - abs(normal_interp.z)) * light_normal_bias; vertex_interp.z -= z_ofs; +#endif //dual parabolloid + +#endif //depth + +//vertex lighting +#if defined(USE_VERTEX_LIGHTING) && defined(USE_LIGHTING) + //vertex shaded version of lighting (more limited) + vec3 L; + vec3 light_att; + +#ifdef LIGHT_MODE_OMNI + vec3 light_vec = light_position - vertex_interp; + float light_length = length(light_vec); + + float normalized_distance = light_length / light_range; + + if (normalized_distance < 1.0) { + + float omni_attenuation = pow(1.0 - normalized_distance, light_attenuation); + + vec3 attenuation = vec3(omni_attenuation); + light_att = vec3(omni_attenuation); + } else { + light_att = vec3(0.0); + } + + L = normalize(light_vec); #endif +#ifdef LIGHT_MODE_SPOT + + vec3 light_rel_vec = light_position - vertex_interp; + float light_length = length(light_rel_vec); + float normalized_distance = light_length / light_range; + + if (normalized_distance < 1.0) { + + float spot_attenuation = pow(1.0 - normalized_distance, light_attenuation); + vec3 spot_dir = light_direction; + + float spot_cutoff = light_spot_angle; + + float angle = dot(-normalize(light_rel_vec), spot_dir); + + if (angle > spot_cutoff) { + + float scos = max(angle, spot_cutoff); + float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - spot_cutoff)); + + spot_attenuation *= 1.0 - pow(spot_rim, light_spot_attenuation); + + light_att = vec3(spot_attenuation); + } else { + light_att = vec3(0.0); + } + } else { + light_att = vec3(0.0); + } + + L = normalize(light_rel_vec); + +#endif + +#ifdef LIGHT_MODE_DIRECTIONAL + vec3 light_vec = -light_direction; + light_att = vec3(1.0); //no base attenuation + L = normalize(light_vec); +#endif + + diffuse_interp = vec3(0.0); + specular_interp = vec3(0.0); + light_compute(normal_interp, L, -normalize(vertex_interp), light_color.rgb, light_att, roughness); + +#endif + +//shadows (for both vertex and fragment) +#if defined(USE_SHADOW) && defined(USE_LIGHTING) + + vec4 vi4 = vec4(vertex_interp, 1.0); + shadow_coord = light_shadow_matrix * vi4; + +#if defined(LIGHT_USE_PSSM2) || defined(LIGHT_USE_PSSM4) + shadow_coord2 = light_shadow_matrix2 * vi4; +#endif + +#if defined(LIGHT_USE_PSSM4) + shadow_coord3 = light_shadow_matrix3 * vi4; + shadow_coord4 = light_shadow_matrix4 * vi4; + +#endif + +#endif //use shadow and use lighting + +#ifdef USE_VERTEX_LIGHTING + +#ifdef USE_REFLECTION_PROBE1 + { + vec3 ref_normal = normalize(reflect(vertex_interp, normal_interp)); + vec3 local_pos = (refprobe1_local_matrix * vec4(vertex_interp, 1.0)).xyz; + vec3 inner_pos = abs(local_pos / refprobe1_box_extents); + float blend = max(inner_pos.x, max(inner_pos.y, inner_pos.z)); + + { + vec3 local_ref_vec = (refprobe1_local_matrix * vec4(ref_normal, 0.0)).xyz; + refprobe1_reflection_normal_blend.xyz = local_ref_vec; + refprobe1_reflection_normal_blend.a = blend; + } +#ifndef USE_LIGHTMAP + + refprobe1_ambient_normal = (refprobe1_local_matrix * vec4(normal_interp, 0.0)).xyz; +#endif + } + +#endif //USE_REFLECTION_PROBE1 + +#ifdef USE_REFLECTION_PROBE2 + { + vec3 ref_normal = normalize(reflect(vertex_interp, normal_interp)); + vec3 local_pos = (refprobe2_local_matrix * vec4(vertex_interp, 1.0)).xyz; + vec3 inner_pos = abs(local_pos / refprobe2_box_extents); + float blend = max(inner_pos.x, max(inner_pos.y, inner_pos.z)); + + { + vec3 local_ref_vec = (refprobe2_local_matrix * vec4(ref_normal, 0.0)).xyz; + refprobe2_reflection_normal_blend.xyz = local_ref_vec; + refprobe2_reflection_normal_blend.a = blend; + } +#ifndef USE_LIGHTMAP + + refprobe2_ambient_normal = (refprobe2_local_matrix * vec4(normal_interp, 0.0)).xyz; +#endif + } + +#endif //USE_REFLECTION_PROBE2 + +#if defined(FOG_DEPTH_ENABLED) || defined(FOG_HEIGHT_ENABLED) + + float fog_amount = 0.0; + +#ifdef LIGHT_MODE_DIRECTIONAL + + vec3 fog_color = mix(fog_color_base.rgb, fog_sun_color_amount.rgb, fog_sun_color_amount.a * pow(max(dot(normalize(vertex_interp), light_direction), 0.0), 8.0)); +#else + vec3 fog_color = fog_color_base.rgb; +#endif + +#ifdef FOG_DEPTH_ENABLED + + { + + float fog_z = smoothstep(fog_depth_begin, fog_max_distance, length(vertex)); + + fog_amount = pow(fog_z, fog_depth_curve); + } +#endif + +#ifdef FOG_HEIGHT_ENABLED + { + float y = (camera_matrix * vec4(vertex_interp, 1.0)).y; + fog_amount = max(fog_amount, pow(smoothstep(fog_height_min, fog_height_max, y), fog_height_curve)); + } +#endif + fog_interp = vec4(fog_color, fog_amount); + +#endif //fog + +#endif //use vertex lighting gl_Position = projection_matrix * vec4(vertex_interp, 1.0); } +/* clang-format off */ [fragment] #extension GL_ARB_shader_texture_lod : enable @@ -267,40 +658,201 @@ VERTEX_SHADER_CODE #define highp #else precision mediump float; -precision mediump int; +precision highp int; #endif #include "stdlib.glsl" #define M_PI 3.14159265359 +#define SHADER_IS_SRGB true // // uniforms // -uniform mat4 camera_matrix; -uniform mat4 camera_inverse_matrix; -uniform mat4 projection_matrix; -uniform mat4 projection_inverse_matrix; +uniform highp mat4 camera_matrix; +/* clang-format on */ +uniform highp mat4 camera_inverse_matrix; +uniform highp mat4 projection_matrix; +uniform highp mat4 projection_inverse_matrix; -uniform mat4 world_transform; +uniform highp mat4 world_transform; uniform highp float time; -#ifdef SCREEN_UV_USED +#if defined(SCREEN_UV_USED) uniform vec2 screen_pixel_size; #endif -uniform highp sampler2D depth_buffer; //texunit:-5 +// I think supporting this in GLES2 is difficult +// uniform highp sampler2D depth_buffer; #if defined(SCREEN_TEXTURE_USED) -uniform highp sampler2D screen_texture; //texunit:-6 +uniform highp sampler2D screen_texture; //texunit:-4 #endif -#ifdef USE_RADIANCE_MAP +#ifdef USE_REFLECTION_PROBE1 + +#ifdef USE_VERTEX_LIGHTING + +varying mediump vec4 refprobe1_reflection_normal_blend; +#ifndef USE_LIGHTMAP +varying mediump vec3 refprobe1_ambient_normal; +#endif + +#else + +uniform bool refprobe1_use_box_project; +uniform highp vec3 refprobe1_box_extents; +uniform vec3 refprobe1_box_offset; +uniform highp mat4 refprobe1_local_matrix; + +#endif //use vertex lighting + +uniform bool refprobe1_exterior; + +uniform highp samplerCube reflection_probe1; //texunit:-5 + +uniform float refprobe1_intensity; +uniform vec4 refprobe1_ambient; + +#endif //USE_REFLECTION_PROBE1 + +#ifdef USE_REFLECTION_PROBE2 + +#ifdef USE_VERTEX_LIGHTING + +varying mediump vec4 refprobe2_reflection_normal_blend; +#ifndef USE_LIGHTMAP +varying mediump vec3 refprobe2_ambient_normal; +#endif + +#else + +uniform bool refprobe2_use_box_project; +uniform highp vec3 refprobe2_box_extents; +uniform vec3 refprobe2_box_offset; +uniform highp mat4 refprobe2_local_matrix; + +#endif //use vertex lighting + +uniform bool refprobe2_exterior; + +uniform highp samplerCube reflection_probe2; //texunit:-6 + +uniform float refprobe2_intensity; +uniform vec4 refprobe2_ambient; + +#endif //USE_REFLECTION_PROBE2 #define RADIANCE_MAX_LOD 6.0 +#if defined(USE_REFLECTION_PROBE1) || defined(USE_REFLECTION_PROBE2) + +void reflection_process(samplerCube reflection_map, +#ifdef USE_VERTEX_LIGHTING + vec3 ref_normal, +#ifndef USE_LIGHTMAP + vec3 amb_normal, +#endif + float ref_blend, + +#else //no vertex lighting + vec3 normal, vec3 vertex, + mat4 local_matrix, + bool use_box_project, vec3 box_extents, vec3 box_offset, +#endif //vertex lighting + bool exterior, float intensity, vec4 ref_ambient, float roughness, vec3 ambient, vec3 skybox, inout highp vec4 reflection_accum, inout highp vec4 ambient_accum) { + + vec4 reflection; + +#ifdef USE_VERTEX_LIGHTING + + reflection.rgb = textureCubeLod(reflection_map, ref_normal, roughness * RADIANCE_MAX_LOD).rgb; + + float blend = ref_blend; //crappier blend formula for vertex + blend *= blend; + blend = max(0.0, 1.0 - blend); + +#else //fragment lighting + + vec3 local_pos = (local_matrix * vec4(vertex, 1.0)).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)); + blend = mix(length(inner_pos), blend, blend); + blend *= blend; + blend = max(0.0, 1.0 - blend); + + //reflect and make local + vec3 ref_normal = normalize(reflect(vertex, normal)); + ref_normal = (local_matrix * vec4(ref_normal, 0.0)).xyz; + + if (use_box_project) { //box project + + vec3 nrdir = normalize(ref_normal); + vec3 rbmax = (box_extents - local_pos) / nrdir; + vec3 rbmin = (-box_extents - local_pos) / nrdir; + + vec3 rbminmax = mix(rbmin, rbmax, vec3(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; + ref_normal = posonbox - box_offset.xyz; + } + + reflection.rgb = textureCubeLod(reflection_map, ref_normal, roughness * RADIANCE_MAX_LOD).rgb; +#endif + + if (exterior) { + reflection.rgb = mix(skybox, reflection.rgb, blend); + } + reflection.rgb *= intensity; + reflection.a = blend; + reflection.rgb *= blend; + + reflection_accum += reflection; + +#ifndef USE_LIGHTMAP + + vec4 ambient_out; +#ifndef USE_VERTEX_LIGHTING + + vec3 amb_normal = (local_matrix * vec4(normal, 0.0)).xyz; +#endif + + ambient_out.rgb = textureCubeLod(reflection_map, amb_normal, RADIANCE_MAX_LOD).rgb; + ambient_out.rgb = mix(ref_ambient.rgb, ambient_out.rgb, ref_ambient.a); + if (exterior) { + ambient_out.rgb = mix(ambient, ambient_out.rgb, blend); + } + + ambient_out.a = blend; + ambient_out.rgb *= blend; + ambient_accum += ambient_out; + +#endif +} + +#endif //use refprobe 1 or 2 + +#ifdef USE_LIGHTMAP +uniform mediump sampler2D lightmap; //texunit:-4 +uniform mediump float lightmap_energy; +#endif + +#ifdef USE_LIGHTMAP_CAPTURE +uniform mediump vec4[12] lightmap_captures; +uniform bool lightmap_capture_sky; + +#endif + +#ifdef USE_RADIANCE_MAP + uniform samplerCube radiance_map; // texunit:-2 uniform mat4 radiance_inverse_xform; @@ -313,49 +865,70 @@ uniform float ambient_sky_contribution; uniform vec4 ambient_color; uniform float ambient_energy; -#ifdef LIGHT_PASS +#ifdef USE_LIGHTING -#define LIGHT_TYPE_DIRECTIONAL 0 -#define LIGHT_TYPE_OMNI 1 -#define LIGHT_TYPE_SPOT 2 +#ifdef USE_VERTEX_LIGHTING -// general for all lights -uniform int light_type; +//get from vertex +varying highp vec3 diffuse_interp; +varying highp vec3 specular_interp; -uniform float light_energy; -uniform vec4 light_color; -uniform float light_specular; +uniform highp vec3 light_direction; //may be used by fog, so leave here -// directional -uniform vec3 light_direction; +#else +//done in fragment +// general for all lights +uniform highp vec4 light_color; +uniform highp float light_specular; +// directional +uniform highp vec3 light_direction; // omni -uniform vec3 light_position; +uniform highp vec3 light_position; -uniform float light_range; -uniform vec4 light_attenuation; +uniform highp float light_attenuation; // spot -uniform float light_spot_attenuation; -uniform float light_spot_range; -uniform float light_spot_angle; +uniform highp float light_spot_attenuation; +uniform highp float light_spot_range; +uniform highp float light_spot_angle; +#endif -// shadows -uniform highp sampler2D light_shadow_atlas; //texunit:-4 -uniform float light_has_shadow; +//this is needed outside above if because dual paraboloid wants it +uniform highp float light_range; + +#ifdef USE_SHADOW + +uniform highp vec2 shadow_pixel_size; + +#if defined(LIGHT_MODE_OMNI) || defined(LIGHT_MODE_SPOT) +uniform highp sampler2D light_shadow_atlas; //texunit:-3 +#endif + +#ifdef LIGHT_MODE_DIRECTIONAL +uniform highp sampler2D light_directional_shadow; // texunit:-3 +uniform highp vec4 light_split_offsets; +#endif + +varying highp vec4 shadow_coord; + +#if defined(LIGHT_USE_PSSM2) || defined(LIGHT_USE_PSSM4) +varying highp vec4 shadow_coord2; +#endif + +#if defined(LIGHT_USE_PSSM4) + +varying highp vec4 shadow_coord3; +varying highp vec4 shadow_coord4; + +#endif -uniform mat4 light_shadow_matrix; uniform vec4 light_clamp; -// directional shadow +#endif // light shadow -uniform highp sampler2D light_directional_shadow; // texunit:-4 -uniform vec4 light_split_offsets; +// directional shadow -uniform mat4 light_shadow_matrix1; -uniform mat4 light_shadow_matrix2; -uniform mat4 light_shadow_matrix3; -uniform mat4 light_shadow_matrix4; #endif // @@ -370,29 +943,133 @@ varying vec3 tangent_interp; varying vec3 binormal_interp; #endif -#ifdef ENABLE_COLOR_INTERP +#if defined(ENABLE_COLOR_INTERP) varying vec4 color_interp; #endif -#ifdef ENABLE_UV_INTERP +#if defined(ENABLE_UV_INTERP) varying vec2 uv_interp; #endif -#ifdef ENABLE_UV2_INTERP +#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP) varying vec2 uv2_interp; #endif varying vec3 view_interp; -vec3 metallic_to_specular_color(float metallic, float specular, vec3 albedo) { - float dielectric = (0.034 * 2.0) * specular; - // energy conservation - return mix(vec3(dielectric), albedo, metallic); // TODO: reference? +vec3 F0(float metallic, float specular, vec3 albedo) { + float dielectric = 0.16 * specular * specular; + // use albedo * metallic as colored specular reflectance at 0 angle for metallic materials; + // see https://google.github.io/filament/Filament.md.html + return mix(vec3(dielectric), albedo, vec3(metallic)); } +/* clang-format off */ + FRAGMENT_SHADER_GLOBALS -#ifdef LIGHT_PASS +/* clang-format on */ + +#ifdef RENDER_DEPTH_DUAL_PARABOLOID + +varying highp float dp_clip; + +#endif + +#ifdef USE_LIGHTING + +// 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. +// +// 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)); +} +*/ + +// This approximates G_GGX_2cos(cos_theta_l, alpha) * G_GGX_2cos(cos_theta_v, alpha) +// See Filament docs, Specular G section. +float V_GGX(float cos_theta_l, float cos_theta_v, float alpha) { + float v = cos_theta_l * (cos_theta_v * (1.0 - alpha) + alpha); + float l = cos_theta_v * (cos_theta_l * (1.0 - alpha) + alpha); + return 0.5 / (v + l); +} + +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); +} + +/* +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 / max(cos_theta_m + sqrt(cos2 + (s_x * s_x + s_y * s_y) * sin2), 0.001); +} +*/ + +// This approximates G_GGX_anisotropic_2cos(cos_theta_l, ...) * G_GGX_anisotropic_2cos(cos_theta_v, ...) +// See Filament docs, Anisotropic specular BRDF section. +float V_GGX_anisotropic(float alpha_x, float alpha_y, float TdotV, float TdotL, float BdotV, float BdotL, float NdotV, float NdotL) { + float Lambda_V = NdotL * length(vec3(alpha_x * TdotV, alpha_y * BdotV, NdotV)); + float Lambda_L = NdotV * length(vec3(alpha_x * TdotL, alpha_y * BdotL, NdotL)); + return 0.5 / (Lambda_V + Lambda_L); +} + +float D_GGX_anisotropic(float cos_theta_m, float alpha_x, float alpha_y, float cos_phi, float sin_phi, float NdotH) { + float alpha2 = alpha_x * alpha_y; + highp vec3 v = vec3(alpha_y * cos_phi, alpha_x * sin_phi, alpha2 * NdotH); + highp float v2 = dot(v, v); + float w2 = alpha2 / v2; + float D = alpha2 * w2 * w2 * (1.0 / M_PI); + return D; + + /* 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 / max(M_PI * alpha_x * alpha_y * d * d, 0.001); */ +} + +float SchlickFresnel(float u) { + float m = 1.0 - u; + float m2 = m * m; + return m2 * m2 * m; // pow(m,5) +} + +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); +} + void light_compute( vec3 N, vec3 L, @@ -406,6 +1083,7 @@ void light_compute( float specular_blob_intensity, float roughness, float metallic, + float specular, float rim, float rim_tint, float clearcoat, @@ -414,52 +1092,305 @@ void light_compute( inout vec3 diffuse_light, inout vec3 specular_light) { +//this makes lights behave closer to linear, but then addition of lights looks bad +//better left disabled + +//#define SRGB_APPROX(m_var) m_var = pow(m_var,0.4545454545); +/* +#define SRGB_APPROX(m_var) {\ + float S1 = sqrt(m_var);\ + float S2 = sqrt(S1);\ + float S3 = sqrt(S2);\ + m_var = 0.662002687 * S1 + 0.684122060 * S2 - 0.323583601 * S3 - 0.0225411470 * m_var;\ + } +*/ +#define SRGB_APPROX(m_var) + +#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; + + /* clang-format off */ + +LIGHT_SHADER_CODE + + /* clang-format on */ + +#else float NdotL = dot(N, L); - float cNdotL = max(NdotL, 0.0); + float cNdotL = max(NdotL, 0.0); // clamped NdotL float NdotV = dot(N, V); float cNdotV = max(NdotV, 0.0); - { - // calculate diffuse reflection - - // TODO hardcode Oren Nayar for now - float diffuse_brdf_NL; + 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 +#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 + + SRGB_APPROX(diffuse_brdf_NL) diffuse_light += light_color * diffuse_color * diffuse_brdf_NL * attenuation; + +#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) + float rim_light = pow(max(0.0, 1.0 - cNdotV), max(0.0, (1.0 - roughness) * 16.0)); + diffuse_light += rim_light * rim * mix(vec3(1.0), diffuse_color, rim_tint) * light_color; +#endif } - { - // calculate specular reflection + if (roughness > 0.0) { + +#if defined(SPECULAR_SCHLICK_GGX) + vec3 specular_brdf_NL = vec3(0.0); +#else + float specular_brdf_NL = 0.0; +#endif + +#if defined(SPECULAR_BLINN) + + //normalized blinn + vec3 H = normalize(V + L); + float cNdotH = max(dot(N, H), 0.0); + float cVdotH = max(dot(V, H), 0.0); + float cLdotH = max(dot(L, H), 0.0); + float shininess = exp2(15.0 * (1.0 - roughness) + 1.0) * 0.25; + float blinn = pow(cNdotH, shininess); + blinn *= (shininess + 8.0) / (8.0 * 3.141592654); + specular_brdf_NL = (blinn) / max(4.0 * cNdotV * cNdotL, 0.75); + +#elif defined(SPECULAR_PHONG) + + vec3 R = normalize(-reflect(L, N)); + float cRdotV = max(0.0, dot(R, V)); + float shininess = exp2(15.0 * (1.0 - roughness) + 1.0) * 0.25; + float phong = pow(cRdotV, shininess); + phong *= (shininess + 8.0) / (8.0 * 3.141592654); + specular_brdf_NL = (phong) / max(4.0 * cNdotV * cNdotL, 0.75); + +#elif defined(SPECULAR_TOON) vec3 R = normalize(-reflect(L, N)); - 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 RdotV = dot(R, V); + float mid = 1.0 - roughness; + mid *= mid; + specular_brdf_NL = smoothstep(mid - roughness * 0.5, mid + roughness * 0.5, RdotV) * mid; + +#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 alpha = roughness * roughness; + float aspect = sqrt(1.0 - anisotropy * 0.9); + float ax = alpha / aspect; + float ay = alpha * aspect; + float XdotH = dot(T, H); + float YdotH = dot(B, H); + float D = D_GGX_anisotropic(cNdotH, ax, ay, XdotH, YdotH, cNdotH); + //float G = G_GGX_anisotropic_2cos(cNdotL, ax, ay, XdotH, YdotH) * G_GGX_anisotropic_2cos(cNdotV, ax, ay, XdotH, YdotH); + float G = V_GGX_anisotropic(ax, ay, dot(T, V), dot(T, L), dot(B, V), dot(B, L), cNdotV, cNdotL)) + +#else + float alpha = roughness * roughness; + float D = D_GGX(cNdotH, alpha); + //float G = G_GGX_2cos(cNdotL, alpha) * G_GGX_2cos(cNdotV, alpha); + float G = V_GGX(cNdotL, cNdotV, alpha); +#endif + // F + vec3 f0 = F0(metallic, specular, diffuse_color); + float cLdotH5 = SchlickFresnel(cLdotH); + vec3 F = mix(vec3(cLdotH5), vec3(1.0), f0); + + specular_brdf_NL = cNdotL * D * F * G; + +#endif + + SRGB_APPROX(specular_brdf_NL) + specular_light += specular_brdf_NL * light_color * specular_blob_intensity * attenuation; + +#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 Gr = V_GGX(cNdotL, cNdotV, 0.25); + + float clearcoat_specular_brdf_NL = 0.25 * clearcoat * Gr * Fr * Dr * cNdotL; + + specular_light += clearcoat_specular_brdf_NL * light_color * specular_blob_intensity * attenuation; + } +#endif } + +#endif //defined(USE_LIGHT_SHADER_CODE) } +#endif // shadows -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; +#ifdef USE_SHADOW + +#define SAMPLE_SHADOW_TEXEL(p_shadow, p_pos, p_depth) step(p_depth, texture2D(p_shadow, p_pos).r) +#define SAMPLE_SHADOW_TEXEL_PROJ(p_shadow, p_pos) step(p_pos.z, texture2DProj(p_shadow, p_pos).r) + +float sample_shadow(highp sampler2D shadow, highp vec4 spos) { + +#ifdef SHADOW_MODE_PCF_13 + + spos.xyz /= spos.w; + vec2 pos = spos.xy; + float depth = spos.z; + + float avg = SAMPLE_SHADOW_TEXEL(shadow, pos, depth); + avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(shadow_pixel_size.x, 0.0), depth); + avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(-shadow_pixel_size.x, 0.0), depth); + avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(0.0, shadow_pixel_size.y), depth); + avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(0.0, -shadow_pixel_size.y), depth); + avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(shadow_pixel_size.x, shadow_pixel_size.y), depth); + avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(-shadow_pixel_size.x, shadow_pixel_size.y), depth); + avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(shadow_pixel_size.x, -shadow_pixel_size.y), depth); + avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(-shadow_pixel_size.x, -shadow_pixel_size.y), depth); + avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(shadow_pixel_size.x * 2.0, 0.0), depth); + avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(-shadow_pixel_size.x * 2.0, 0.0), depth); + avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(0.0, shadow_pixel_size.y * 2.0), depth); + avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(0.0, -shadow_pixel_size.y * 2.0), depth); + return avg * (1.0 / 13.0); +#endif + +#ifdef SHADOW_MODE_PCF_5 + + spos.xyz /= spos.w; + vec2 pos = spos.xy; + float depth = spos.z; + + float avg = SAMPLE_SHADOW_TEXEL(shadow, pos, depth); + avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(shadow_pixel_size.x, 0.0), depth); + avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(-shadow_pixel_size.x, 0.0), depth); + avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(0.0, shadow_pixel_size.y), depth); + avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(0.0, -shadow_pixel_size.y), depth); + return avg * (1.0 / 5.0); + +#endif + +#if !defined(SHADOW_MODE_PCF_5) || !defined(SHADOW_MODE_PCF_13) + + return SAMPLE_SHADOW_TEXEL_PROJ(shadow, spos); +#endif } #endif +#if defined(FOG_DEPTH_ENABLED) || defined(FOG_HEIGHT_ENABLED) + +#if defined(USE_VERTEX_LIGHTING) + +varying vec4 fog_interp; + +#else +uniform mediump vec4 fog_color_base; +#ifdef LIGHT_MODE_DIRECTIONAL +uniform mediump vec4 fog_sun_color_amount; +#endif + +uniform bool fog_transmit_enabled; +uniform mediump float fog_transmit_curve; + +#ifdef FOG_DEPTH_ENABLED +uniform highp float fog_depth_begin; +uniform mediump float fog_depth_curve; +uniform mediump float fog_max_distance; +#endif + +#ifdef FOG_HEIGHT_ENABLED +uniform highp float fog_height_min; +uniform highp float fog_height_max; +uniform mediump float fog_height_curve; +#endif + +#endif //vertex lit +#endif //fog + void main() { +#ifdef RENDER_DEPTH_DUAL_PARABOLOID + + if (dp_clip > 0.0) + discard; +#endif highp vec3 vertex = vertex_interp; vec3 albedo = vec3(1.0); vec3 transmission = vec3(0.0); @@ -473,10 +1404,16 @@ void main() { float clearcoat_gloss = 0.0; float anisotropy = 0.0; vec2 anisotropy_flow = vec2(1.0, 0.0); + float sss_strength = 0.0; //unused float alpha = 1.0; float side = 1.0; + float specular_blob_intensity = 1.0; +#if defined(SPECULAR_TOON) + specular_blob_intensity *= specular * 2.0; +#endif + #if defined(ENABLE_AO) float ao = 1.0; float ao_light_affect = 0.0; @@ -496,26 +1433,28 @@ void main() { #endif float normaldepth = 1.0; -#ifdef ALPHA_SCISSOR_USED +#if defined(ALPHA_SCISSOR_USED) float alpha_scissor = 0.5; #endif -#ifdef SCREEN_UV_USED +#if defined(SCREEN_UV_USED) vec2 screen_uv = gl_FragCoord.xy * screen_pixel_size; #endif -{ + { + /* clang-format off */ FRAGMENT_SHADER_CODE -} + /* clang-format on */ + } #if defined(ENABLE_NORMALMAP) normalmap.xy = normalmap.xy * 2.0 - 1.0; normalmap.z = sqrt(max(0.0, 1.0 - dot(normalmap.xy, normalmap.xy))); - // normal = normalize(mix(normal_interp, tangent * normalmap.x + binormal * normalmap.y + normal * normalmap.z, normaldepth)) * side; - normal = normalmap; + normal = normalize(mix(normal_interp, tangent * normalmap.x + binormal * normalmap.y + normal * normalmap.z, normaldepth)) * side; + //normal = normalmap; #endif normal = normalize(normal); @@ -524,300 +1463,513 @@ FRAGMENT_SHADER_CODE vec3 specular_light = vec3(0.0, 0.0, 0.0); vec3 diffuse_light = vec3(0.0, 0.0, 0.0); - vec3 ambient_light = vec3(0.0, 0.0, 0.0); - vec3 env_reflection_light = vec3(0.0, 0.0, 0.0); - vec3 eye_position = -normalize(vertex_interp); -#ifdef ALPHA_SCISSOR_USED +#if defined(ALPHA_SCISSOR_USED) if (alpha < alpha_scissor) { discard; } #endif +#ifdef BASE_PASS + //none +#ifdef USE_RADIANCE_MAP + + vec3 ref_vec = reflect(-eye_position, N); + ref_vec = normalize((radiance_inverse_xform * vec4(ref_vec, 0.0)).xyz); + + ref_vec.z *= -1.0; + + specular_light = textureCubeLod(radiance_map, ref_vec, roughness * RADIANCE_MAX_LOD).xyz * bg_energy; + + { + 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); + } + +#else + + ambient_light = ambient_color.rgb; + +#endif + + ambient_light *= ambient_energy; + +#if defined(USE_REFLECTION_PROBE1) || defined(USE_REFLECTION_PROBE2) + + vec4 ambient_accum = vec4(0.0); + vec4 reflection_accum = vec4(0.0); + +#ifdef USE_REFLECTION_PROBE1 + + reflection_process(reflection_probe1, +#ifdef USE_VERTEX_LIGHTING + refprobe1_reflection_normal_blend.rgb, +#ifndef USE_LIGHTMAP + refprobe1_ambient_normal, +#endif + refprobe1_reflection_normal_blend.a, +#else + normal_interp, vertex_interp, refprobe1_local_matrix, + refprobe1_use_box_project, refprobe1_box_extents, refprobe1_box_offset, +#endif + refprobe1_exterior, refprobe1_intensity, refprobe1_ambient, roughness, + ambient_light, specular_light, reflection_accum, ambient_accum); + +#endif // USE_REFLECTION_PROBE1 + +#ifdef USE_REFLECTION_PROBE2 + + reflection_process(reflection_probe2, +#ifdef USE_VERTEX_LIGHTING + refprobe2_reflection_normal_blend.rgb, +#ifndef USE_LIGHTMAP + refprobe2_ambient_normal, +#endif + refprobe2_reflection_normal_blend.a, +#else + normal_interp, vertex_interp, refprobe2_local_matrix, + refprobe2_use_box_project, refprobe2_box_extents, refprobe2_box_offset, +#endif + refprobe2_exterior, refprobe2_intensity, refprobe2_ambient, roughness, + ambient_light, specular_light, reflection_accum, ambient_accum); + +#endif // USE_REFLECTION_PROBE2 + + if (reflection_accum.a > 0.0) { + specular_light = reflection_accum.rgb / reflection_accum.a; + } + +#ifndef USE_LIGHTMAP + if (ambient_accum.a > 0.0) { + ambient_light = ambient_accum.rgb / ambient_accum.a; + } +#endif + +#endif // defined(USE_REFLECTION_PROBE1) || defined(USE_REFLECTION_PROBE2) + +#ifdef USE_LIGHTMAP + //ambient light will come entirely from lightmap is lightmap is used + ambient_light = texture2D(lightmap, uv2_interp).rgb * lightmap_energy; +#endif + +#ifdef USE_LIGHTMAP_CAPTURE + { + vec3 cone_dirs[12] = 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), + 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)); + + vec3 local_normal = normalize(camera_matrix * vec4(normal, 0.0)).xyz; + vec4 captured = vec4(0.0); + float sum = 0.0; + for (int i = 0; i < 12; i++) { + float amount = max(0.0, dot(local_normal, cone_dirs[i])); //not correct, but creates a nice wrap around effect + captured += lightmap_captures[i] * amount; + sum += amount; + } + + captured /= sum; + + if (lightmap_capture_sky) { + ambient_light = mix(ambient_light, captured.rgb, captured.a); + } else { + ambient_light = captured.rgb; + } + } +#endif + +#endif //BASE PASS + // // Lighting // -#ifdef LIGHT_PASS +#ifdef USE_LIGHTING - if (light_type == LIGHT_TYPE_OMNI) { - vec3 light_vec = light_position - vertex; - float light_length = length(light_vec); +#ifndef USE_VERTEX_LIGHTING + vec3 L; +#endif + vec3 light_att = vec3(1.0); - float normalized_distance = light_length / light_range; +#ifdef LIGHT_MODE_OMNI - float omni_attenuation = pow(1.0 - normalized_distance, light_attenuation.w); +#ifndef USE_VERTEX_LIGHTING + vec3 light_vec = light_position - vertex; + float light_length = length(light_vec); - vec3 attenuation = vec3(omni_attenuation); + float normalized_distance = light_length / light_range; + if (normalized_distance < 1.0) { + + float omni_attenuation = pow(1.0 - normalized_distance, light_attenuation); + + light_att = vec3(omni_attenuation); + } else { + light_att = vec3(0.0); + } + L = normalize(light_vec); + +#endif + +#ifdef USE_SHADOW + { + highp vec4 splane = shadow_coord; + float shadow_len = length(splane.xyz); - if (light_has_shadow > 0.5) { - highp vec3 splane = (light_shadow_matrix * vec4(vertex, 1.0)).xyz; - float shadow_len = length(splane); + splane.xyz = normalize(splane.xyz); - splane = normalize(splane); + vec4 clamp_rect = light_clamp; - vec4 clamp_rect = light_clamp; + if (splane.z >= 0.0) { + 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.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; + splane.w = 1.0; + + float shadow = sample_shadow(light_shadow_atlas, splane); + + light_att *= shadow; + } +#endif + +#endif //type omni - clamp_rect.y += clamp_rect.w; +#ifdef LIGHT_MODE_DIRECTIONAL + +#ifndef USE_VERTEX_LIGHTING + vec3 light_vec = -light_direction; + L = normalize(light_vec); +#endif + float depth_z = -vertex.z; + +#ifdef USE_SHADOW + +#ifdef USE_VERTEX_LIGHTING + //compute shadows in a mobile friendly way + +#ifdef LIGHT_USE_PSSM4 + //take advantage of prefetch + float shadow1 = sample_shadow(light_directional_shadow, shadow_coord); + float shadow2 = sample_shadow(light_directional_shadow, shadow_coord2); + float shadow3 = sample_shadow(light_directional_shadow, shadow_coord3); + float shadow4 = sample_shadow(light_directional_shadow, shadow_coord4); + + if (depth_z < light_split_offsets.w) { + float pssm_fade = 0.0; + float shadow_att = 1.0; +#ifdef LIGHT_USE_PSSM_BLEND + float shadow_att2 = 1.0; + float pssm_blend = 0.0; + bool use_blend = true; +#endif + if (depth_z < light_split_offsets.y) { + if (depth_z < light_split_offsets.x) { + shadow_att = shadow1; + +#ifdef LIGHT_USE_PSSM_BLEND + shadow_att2 = shadow2; + + pssm_blend = smoothstep(0.0, light_split_offsets.x, depth_z); +#endif } else { - splane.z = 1.0 - splane.z; - } + shadow_att = shadow2; - splane.xy /= splane.z; - splane.xy = splane.xy * 0.5 + 0.5; - splane.z = shadow_len / light_range; +#ifdef LIGHT_USE_PSSM_BLEND + shadow_att2 = shadow3; - splane.xy = clamp_rect.xy + splane.xy * clamp_rect.zw; + pssm_blend = smoothstep(light_split_offsets.x, light_split_offsets.y, depth_z); +#endif + } + } else { + if (depth_z < light_split_offsets.z) { + + shadow_att = shadow3; - float shadow = sample_shadow(light_shadow_atlas, vec2(0.0), splane.xy, splane.z, clamp_rect); +#if defined(LIGHT_USE_PSSM_BLEND) + shadow_att2 = shadow4; + pssm_blend = smoothstep(light_split_offsets.y, light_split_offsets.z, depth_z); +#endif - if (shadow > splane.z) { } else { - attenuation = vec3(0.0); + + shadow_att = shadow4; + pssm_fade = smoothstep(light_split_offsets.z, light_split_offsets.w, depth_z); + +#if defined(LIGHT_USE_PSSM_BLEND) + use_blend = false; +#endif } } +#if defined(LIGHT_USE_PSSM_BLEND) + if (use_blend) { + shadow_att = mix(shadow_att, shadow_att2, pssm_blend); + } +#endif + light_att *= shadow_att; + } + +#endif //LIGHT_USE_PSSM4 + +#ifdef LIGHT_USE_PSSM2 + + //take advantage of prefetch + float shadow1 = sample_shadow(light_directional_shadow, shadow_coord); + float shadow2 = sample_shadow(light_directional_shadow, shadow_coord2); + + if (depth_z < light_split_offsets.y) { + float shadow_att = 1.0; + float pssm_fade = 0.0; + +#ifdef LIGHT_USE_PSSM_BLEND + float shadow_att2 = 1.0; + float pssm_blend = 0.0; + bool use_blend = true; +#endif + if (depth_z < light_split_offsets.x) { + float pssm_fade = 0.0; + shadow_att = shadow1; + +#ifdef LIGHT_USE_PSSM_BLEND + shadow_att2 = shadow2; + pssm_blend = smoothstep(0.0, light_split_offsets.x, depth_z); +#endif + } else { + + shadow_att = shadow2; + pssm_fade = smoothstep(light_split_offsets.x, light_split_offsets.y, depth_z); +#ifdef LIGHT_USE_PSSM_BLEND + use_blend = false; +#endif + } +#ifdef LIGHT_USE_PSSM_BLEND + if (use_blend) { + shadow_att = mix(shadow_att, shadow_att2, pssm_blend); + } +#endif + light_att *= shadow_att; + } - 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) { +#endif //LIGHT_USE_PSSM2 +#if !defined(LIGHT_USE_PSSM4) && !defined(LIGHT_USE_PSSM2) + + light_att *= sample_shadow(light_directional_shadow, shadow_coord); +#endif //orthogonal + +#else //fragment version of pssm + + { #ifdef LIGHT_USE_PSSM4 - if (depth_z < light_split_offsets.w) { + if (depth_z < light_split_offsets.w) { #elif defined(LIGHT_USE_PSSM2) - if (depth_z < light_split_offsets.y) { + if (depth_z < light_split_offsets.y) { #else - if (depth_z < light_split_offsets.x) { -#endif + if (depth_z < light_split_offsets.x) { +#endif //pssm2 - vec3 pssm_coord; - float pssm_fade = 0.0; + highp vec4 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; + highp vec4 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; + + if (depth_z < light_split_offsets.y) { + if (depth_z < light_split_offsets.x) { + pssm_coord = shadow_coord; #ifdef LIGHT_USE_PSSM_BLEND - splane = (light_shadow_matrix2 * vec4(vertex, 1.0)); - pssm_coord2 = splane.xyz / splane.w; + pssm_coord2 = shadow_coord2; - pssm_blend = smoothstep(0.0, light_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 { + pssm_coord = shadow_coord2; #ifdef LIGHT_USE_PSSM_BLEND - splane = (light_shadow_matrix3 * vec4(vertex, 1.0)); - pssm_coord2 = splane.xyz / splane.w; + pssm_coord2 = shadow_coord3; - pssm_blend = smoothstep(light_split_offsets.x, light_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 < light_split_offsets.z) { - highp vec4 splane = (light_shadow_matrix3 * vec4(vertex, 1.0)); - pssm_coord = splane.xyz / splane.w; + pssm_coord = shadow_coord3; #if defined(LIGHT_USE_PSSM_BLEND) - 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); + pssm_coord2 = shadow_coord4; + pssm_blend = smoothstep(light_split_offsets.y, light_split_offsets.z, depth_z); #endif - } else { + } else { - 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); + pssm_coord = shadow_coord4; + 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 #ifdef LIGHT_USE_PSSM2 - if (depth_z < light_split_offsets.x) { + if (depth_z < light_split_offsets.x) { - highp vec4 splane = (light_shadow_matrix1 * vec4(vertex, 1.0)); - pssm_coord = splane.xyz / splane.w; + pssm_coord = shadow_coord; #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); + pssm_coord2 = shadow_coord2; + 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; - pssm_fade = smoothstep(light_split_offsets.x, light_split_offsets.y, depth_z); + } else { + + pssm_coord = shadow_coord2; + pssm_fade = smoothstep(light_split_offsets.x, light_split_offsets.y, depth_z); #ifdef LIGHT_USE_PSSM_BLEND - use_blend = false; + use_blend = false; #endif - } + } #endif // LIGHT_USE_PSSM2 #if !defined(LIGHT_USE_PSSM4) && !defined(LIGHT_USE_PSSM2) - { - highp vec4 splane = (light_shadow_matrix1 * vec4(vertex, 1.0)); - pssm_coord = splane.xyz / splane.w; - } + { + pssm_coord = shadow_coord; + } #endif - float shadow = sample_shadow(light_shadow_atlas, vec2(0.0), pssm_coord.xy, pssm_coord.z, light_clamp); + float shadow = sample_shadow(light_directional_shadow, pssm_coord); #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); - } + if (use_blend) { + shadow = mix(shadow, sample_shadow(light_directional_shadow, pssm_coord2), pssm_blend); + } #endif - attenuation *= shadow; - } + light_att *= shadow; } + } +#endif //use vertex lighting - 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); - } - } +#endif //use shadow - vec3 light_rel_vec = light_position - vertex; - float light_length = length(light_rel_vec); - float normalized_distance = light_length / light_range; +#endif - float spot_attenuation = pow(1.0 - normalized_distance, light_attenuation.w); - vec3 spot_dir = light_direction; +#ifdef LIGHT_MODE_SPOT - float spot_cutoff = light_spot_angle; + light_att = vec3(1.0); - 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)); - - spot_attenuation *= 1.0 - pow(spot_rim, light_spot_attenuation); - - light_att *= vec3(spot_attenuation); - - 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); - } +#ifndef USE_VERTEX_LIGHTING - gl_FragColor = vec4(ambient_light + diffuse_light + specular_light, alpha); -#else + vec3 light_rel_vec = light_position - vertex; + float light_length = length(light_rel_vec); + float normalized_distance = light_length / light_range; -#ifdef RENDER_DEPTH + if (normalized_distance < 1.0) { + float spot_attenuation = pow(1.0 - normalized_distance, light_attenuation); + vec3 spot_dir = light_direction; -#else + float spot_cutoff = light_spot_angle; + float angle = dot(-normalize(light_rel_vec), spot_dir); -#ifdef USE_RADIANCE_MAP + if (angle > spot_cutoff) { + float scos = max(angle, spot_cutoff); + float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - spot_cutoff)); + spot_attenuation *= 1.0 - pow(spot_rim, light_spot_attenuation); - vec3 ref_vec = reflect(-eye_position, N); - ref_vec = normalize((radiance_inverse_xform * vec4(ref_vec, 0.0)).xyz); + light_att = vec3(spot_attenuation); + } else { + light_att = vec3(0.0); + } + } else { + light_att = vec3(0.0); + } - ref_vec.z *= -1.0; + L = normalize(light_rel_vec); - env_reflection_light = textureCubeLod(radiance_map, ref_vec, roughness * RADIANCE_MAX_LOD).xyz * bg_energy; +#endif +#ifdef USE_SHADOW { - 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; + highp vec4 splane = shadow_coord; + splane.xyz /= splane.w; - ambient_light = mix(ambient_color.rgb, env_ambient, ambient_sky_contribution); + float shadow = sample_shadow(light_shadow_atlas, splane); + light_att *= shadow; } +#endif - ambient_light *= ambient_energy; +#endif // LIGHT_MODE_SPOT + +#ifdef USE_VERTEX_LIGHTING + //vertex lighting + + specular_light += specular_interp * specular_blob_intensity * light_att; + diffuse_light += diffuse_interp * albedo * light_att; - specular_light += env_reflection_light; +#else + //fragment lighting + light_compute( + normal, + L, + eye_position, + binormal, + tangent, + light_color.xyz, + light_att, + albedo, + transmission, + specular_blob_intensity * light_specular, + roughness, + metallic, + specular, + rim, + rim_tint, + clearcoat, + clearcoat_gloss, + anisotropy, + diffuse_light, + specular_light); + +#endif //vertex lighting + +#endif //USE_LIGHTING + //compute and merge + +#ifndef RENDER_DEPTH + +#ifdef SHADELESS + + gl_FragColor = vec4(albedo, alpha); +#else ambient_light *= albedo; @@ -833,26 +1985,79 @@ FRAGMENT_SHADER_CODE // environment BRDF approximation - // TODO shadeless { + +#if defined(DIFFUSE_TOON) + //simplify for toon, as + specular_light *= specular * metallic * albedo * 2.0; +#else + //TODO: this curve is not really designed for gammaspace, should be adjusted 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_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; + vec2 env = 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; + vec3 f0 = F0(metallic, specular, albedo); + specular_light *= env.x * f0 + env.y; +#endif } gl_FragColor = vec4(ambient_light + diffuse_light + specular_light, alpha); + + //add emission if in base pass +#ifdef BASE_PASS + gl_FragColor.rgb += emission; +#endif // gl_FragColor = vec4(normal, 1.0); +#endif //unshaded + +//apply fog +#if defined(FOG_DEPTH_ENABLED) || defined(FOG_HEIGHT_ENABLED) + +#if defined(USE_VERTEX_LIGHTING) + + gl_FragColor.rgb = mix(gl_FragColor.rgb, fog_interp.rgb, fog_interp.a); +#else //pixel based fog + float fog_amount = 0.0; + +#ifdef LIGHT_MODE_DIRECTIONAL + + vec3 fog_color = mix(fog_color_base.rgb, fog_sun_color_amount.rgb, fog_sun_color_amount.a * pow(max(dot(eye_position, light_direction), 0.0), 8.0)); #else - gl_FragColor = vec4(albedo, alpha); + vec3 fog_color = fog_color_base.rgb; +#endif + +#ifdef FOG_DEPTH_ENABLED + + { + + float fog_z = smoothstep(fog_depth_begin, fog_max_distance, length(vertex)); + + fog_amount = pow(fog_z, fog_depth_curve); + + if (fog_transmit_enabled) { + vec3 total_light = gl_FragColor.rgb; + float transmit = pow(fog_z, fog_transmit_curve); + fog_color = mix(max(total_light, fog_color), fog_color, transmit); + } + } #endif -#endif // RENDER_DEPTH -#endif // lighting +#ifdef 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)); + } +#endif + + gl_FragColor.rgb = mix(gl_FragColor.rgb, fog_color, fog_amount); + +#endif //use vertex lit + +#endif // defined(FOG_DEPTH_ENABLED) || defined(FOG_HEIGHT_ENABLED) + +#endif // not RENDER_DEPTH } diff --git a/drivers/gles2/shaders/screen_space_reflection.glsl b/drivers/gles2/shaders/screen_space_reflection.glsl index 77af874ee4..a11da10b61 100644 --- a/drivers/gles2/shaders/screen_space_reflection.glsl +++ b/drivers/gles2/shaders/screen_space_reflection.glsl @@ -1,6 +1,8 @@ +/* clang-format off */ [vertex] layout(location = 0) in highp vec4 vertex_attrib; +/* clang-format on */ layout(location = 4) in vec2 uv_in; out vec2 uv_interp; @@ -13,9 +15,11 @@ void main() { pos_interp.xy = gl_Position.xy; } +/* clang-format off */ [fragment] in vec2 uv_interp; +/* clang-format on */ in vec2 pos_interp; uniform sampler2D source_diffuse; //texunit:0 diff --git a/drivers/gles2/shaders/ssao.glsl b/drivers/gles2/shaders/ssao.glsl index 8dbe8c6ba9..82eea8f274 100644 --- a/drivers/gles2/shaders/ssao.glsl +++ b/drivers/gles2/shaders/ssao.glsl @@ -1,6 +1,8 @@ +/* clang-format off */ [vertex] layout(location = 0) in highp vec4 vertex_attrib; +/* clang-format on */ void main() { @@ -8,6 +10,7 @@ void main() { gl_Position.z = 1.0; } +/* clang-format off */ [fragment] #define TWO_PI 6.283185307179586476925286766559 @@ -53,6 +56,7 @@ const int ROTATIONS[] = int[]( 29, 21, 19, 27, 31, 29, 21, 18, 17, 29, 31, 31, 23, 18, 25, 26, 25, 23, 19, 34, 19, 27, 21, 25, 39, 29, 17, 21, 27); +/* clang-format on */ //#define NUM_SPIRAL_TURNS (7) const int NUM_SPIRAL_TURNS = ROTATIONS[NUM_SAMPLES - 1]; diff --git a/drivers/gles2/shaders/ssao_blur.glsl b/drivers/gles2/shaders/ssao_blur.glsl index 82b735a2b4..e4133ad534 100644 --- a/drivers/gles2/shaders/ssao_blur.glsl +++ b/drivers/gles2/shaders/ssao_blur.glsl @@ -1,6 +1,8 @@ +/* clang-format off */ [vertex] layout(location = 0) in highp vec4 vertex_attrib; +/* clang-format on */ void main() { @@ -8,9 +10,11 @@ void main() { gl_Position.z = 1.0; } +/* clang-format off */ [fragment] uniform sampler2D source_ssao; //texunit:0 +/* clang-format on */ uniform sampler2D source_depth; //texunit:1 uniform sampler2D source_normal; //texunit:3 @@ -43,7 +47,7 @@ const float gaussian[R + 1] = //float[](0.356642, 0.239400, 0.072410, 0.009869); //float[](0.398943, 0.241971, 0.053991, 0.004432, 0.000134); // stddev = 1.0 float[](0.153170, 0.144893, 0.122649, 0.092902, 0.062970); // stddev = 2.0 - //float[](0.111220, 0.107798, 0.098151, 0.083953, 0.067458, 0.050920, 0.036108); // stddev = 3.0 +//float[](0.111220, 0.107798, 0.098151, 0.083953, 0.067458, 0.050920, 0.036108); // stddev = 3.0 /** (1, 0) or (0, 1)*/ uniform ivec2 axis; diff --git a/drivers/gles2/shaders/ssao_minify.glsl b/drivers/gles2/shaders/ssao_minify.glsl index 777a0069fc..272f3e236e 100644 --- a/drivers/gles2/shaders/ssao_minify.glsl +++ b/drivers/gles2/shaders/ssao_minify.glsl @@ -1,12 +1,15 @@ +/* clang-format off */ [vertex] layout(location = 0) in highp vec4 vertex_attrib; +/* clang-format on */ void main() { gl_Position = vertex_attrib; } +/* clang-format off */ [fragment] #ifdef MINIFY_START @@ -14,6 +17,7 @@ void main() { #define SDEPTH_TYPE highp sampler2D uniform float camera_z_far; uniform float camera_z_near; +/* clang-format on */ #else diff --git a/drivers/gles2/shaders/stdlib.glsl b/drivers/gles2/shaders/stdlib.glsl index 6bc81a22d8..3674d70c9f 100644 --- a/drivers/gles2/shaders/stdlib.glsl +++ b/drivers/gles2/shaders/stdlib.glsl @@ -35,3 +35,13 @@ highp vec4 texel2DFetch(highp sampler2D tex, ivec2 size, ivec2 coord) { return texture2DLod(tex, vec2(x_coord, y_coord), 0.0); } + +#ifndef USE_GLES_OVER_GL +highp mat4 transpose(highp mat4 src) { + return mat4( + vec4(src[0].x, src[1].x, src[2].x, src[3].x), + vec4(src[0].y, src[1].y, src[2].y, src[3].y), + vec4(src[0].z, src[1].z, src[2].z, src[3].z), + vec4(src[0].w, src[1].w, src[2].w, src[3].w)); +} +#endif diff --git a/drivers/gles2/shaders/subsurf_scattering.glsl b/drivers/gles2/shaders/subsurf_scattering.glsl index fb7908f06a..f40fb3a244 100644 --- a/drivers/gles2/shaders/subsurf_scattering.glsl +++ b/drivers/gles2/shaders/subsurf_scattering.glsl @@ -1,6 +1,8 @@ +/* clang-format off */ [vertex] layout(location = 0) in highp vec4 vertex_attrib; +/* clang-format on */ layout(location = 4) in vec2 uv_in; out vec2 uv_interp; @@ -11,6 +13,7 @@ void main() { gl_Position = vertex_attrib; } +/* clang-format off */ [fragment] //#define QUALIFIER uniform // some guy on the interweb says it may be faster with this @@ -18,6 +21,7 @@ void main() { #ifdef USE_25_SAMPLES const int kernel_size = 25; +/* clang-format on */ QUALIFIER vec2 kernel[25] = vec2[]( vec2(0.530605, 0.0), vec2(0.000973794, -3.0), diff --git a/drivers/gles2/shaders/tonemap.glsl b/drivers/gles2/shaders/tonemap.glsl index f3f6a85565..eae3b5a1ca 100644 --- a/drivers/gles2/shaders/tonemap.glsl +++ b/drivers/gles2/shaders/tonemap.glsl @@ -1,6 +1,8 @@ +/* clang-format off */ [vertex] layout(location = 0) in highp vec4 vertex_attrib; +/* clang-format on */ layout(location = 4) in vec2 uv_in; out vec2 uv_interp; @@ -14,6 +16,7 @@ void main() { #endif } +/* clang-format off */ [fragment] #if !defined(GLES_OVER_GL) @@ -21,6 +24,7 @@ precision mediump float; #endif in vec2 uv_interp; +/* clang-format on */ uniform highp sampler2D source; //texunit:0 @@ -115,7 +119,7 @@ vec4 texture2D_bicubic(sampler2D tex, vec2 uv, int p_lod) { vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - 0.5) * pixel_size; return (g0(fuv.y) * (g0x * textureLod(tex, p0, lod) + g1x * textureLod(tex, p1, lod))) + - (g1(fuv.y) * (g0x * textureLod(tex, p2, lod) + g1x * textureLod(tex, p3, lod))); + (g1(fuv.y) * (g0x * textureLod(tex, p2, lod) + g1x * textureLod(tex, p3, lod))); } #define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) texture2D_bicubic(m_tex, m_uv, m_lod) diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index 643d50797e..0c6893d419 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -29,10 +29,12 @@ /*************************************************************************/ #include "rasterizer_canvas_gles3.h" -#include "os/os.h" -#include "project_settings.h" + +#include "core/os/os.h" +#include "core/project_settings.h" #include "rasterizer_scene_gles3.h" #include "servers/visual/visual_server_raster.h" + #ifndef GLES_OVER_GL #define glClearDepth glClearDepthf #endif @@ -832,6 +834,120 @@ void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *cur } } } break; + case Item::Command::TYPE_MULTIMESH: { + + Item::CommandMultiMesh *mmesh = static_cast<Item::CommandMultiMesh *>(c); + + RasterizerStorageGLES3::MultiMesh *multi_mesh = storage->multimesh_owner.getornull(mmesh->multimesh); + + if (!multi_mesh) + break; + + RasterizerStorageGLES3::Mesh *mesh_data = storage->mesh_owner.getornull(multi_mesh->mesh); + + if (!mesh_data) + break; + + RasterizerStorageGLES3::Texture *texture = _bind_canvas_texture(mmesh->texture, mmesh->normal_map); + + state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCE_CUSTOM, multi_mesh->custom_data_format != VS::MULTIMESH_CUSTOM_DATA_NONE); + state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCING, true); + //reset shader and force rebind + state.using_texture_rect = true; + _set_texture_rect_mode(false); + + if (texture) { + Size2 texpixel_size(1.0 / texture->width, 1.0 / texture->height); + state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, texpixel_size); + } + + int amount = MAX(multi_mesh->size, multi_mesh->visible_instances); + + for (int j = 0; j < mesh_data->surfaces.size(); j++) { + RasterizerStorageGLES3::Surface *s = mesh_data->surfaces[j]; + // materials are ignored in 2D meshes, could be added but many things (ie, lighting mode, reading from screen, etc) would break as they are not meant be set up at this point of drawing + glBindVertexArray(s->instancing_array_id); + + glBindBuffer(GL_ARRAY_BUFFER, multi_mesh->buffer); //modify the buffer + + int stride = (multi_mesh->xform_floats + multi_mesh->color_floats + multi_mesh->custom_data_floats) * 4; + glEnableVertexAttribArray(8); + glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + 0); + glVertexAttribDivisor(8, 1); + glEnableVertexAttribArray(9); + glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + 4 * 4); + glVertexAttribDivisor(9, 1); + + int color_ofs; + + if (multi_mesh->transform_format == VS::MULTIMESH_TRANSFORM_3D) { + glEnableVertexAttribArray(10); + glVertexAttribPointer(10, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + 8 * 4); + glVertexAttribDivisor(10, 1); + color_ofs = 12 * 4; + } else { + glDisableVertexAttribArray(10); + glVertexAttrib4f(10, 0, 0, 1, 0); + color_ofs = 8 * 4; + } + + int custom_data_ofs = color_ofs; + + switch (multi_mesh->color_format) { + + case VS::MULTIMESH_COLOR_NONE: { + glDisableVertexAttribArray(11); + glVertexAttrib4f(11, 1, 1, 1, 1); + } break; + case VS::MULTIMESH_COLOR_8BIT: { + glEnableVertexAttribArray(11); + glVertexAttribPointer(11, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, ((uint8_t *)NULL) + color_ofs); + glVertexAttribDivisor(11, 1); + custom_data_ofs += 4; + + } break; + case VS::MULTIMESH_COLOR_FLOAT: { + glEnableVertexAttribArray(11); + glVertexAttribPointer(11, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + color_ofs); + glVertexAttribDivisor(11, 1); + custom_data_ofs += 4 * 4; + } break; + } + + switch (multi_mesh->custom_data_format) { + + case VS::MULTIMESH_CUSTOM_DATA_NONE: { + glDisableVertexAttribArray(12); + glVertexAttrib4f(12, 1, 1, 1, 1); + } break; + case VS::MULTIMESH_CUSTOM_DATA_8BIT: { + glEnableVertexAttribArray(12); + glVertexAttribPointer(12, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, ((uint8_t *)NULL) + custom_data_ofs); + glVertexAttribDivisor(12, 1); + + } break; + case VS::MULTIMESH_CUSTOM_DATA_FLOAT: { + glEnableVertexAttribArray(12); + glVertexAttribPointer(12, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + custom_data_ofs); + glVertexAttribDivisor(12, 1); + } break; + } + + if (s->index_array_len) { + glDrawElementsInstanced(gl_primitive[s->primitive], s->index_array_len, (s->array_len >= (1 << 16)) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, 0, amount); + } else { + glDrawArraysInstanced(gl_primitive[s->primitive], 0, s->array_len, amount); + } + + glBindVertexArray(0); + } + + state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCE_CUSTOM, false); + state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCING, false); + state.using_texture_rect = true; + _set_texture_rect_mode(false); + + } break; case Item::Command::TYPE_PARTICLES: { Item::CommandParticles *particles_cmd = static_cast<Item::CommandParticles *>(c); @@ -851,7 +967,6 @@ void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *cur //enable instancing state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCE_CUSTOM, true); - state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_PARTICLES, true); state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCING, true); //reset shader and force rebind state.using_texture_rect = true; @@ -860,10 +975,8 @@ void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *cur RasterizerStorageGLES3::Texture *texture = _bind_canvas_texture(particles_cmd->texture, particles_cmd->normal_map); if (texture) { - Size2 texpixel_size(1.0 / (texture->width / particles_cmd->h_frames), 1.0 / (texture->height / particles_cmd->v_frames)); + Size2 texpixel_size(1.0 / texture->width, 1.0 / texture->height); state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, texpixel_size); - } else { - state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, Vector2(1.0, 1.0)); } if (!particles->use_local_coords) { @@ -877,9 +990,6 @@ void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *cur state.canvas_shader.set_uniform(CanvasShaderGLES3::MODELVIEW_MATRIX, state.final_transform * inv_xf); } - state.canvas_shader.set_uniform(CanvasShaderGLES3::H_FRAMES, particles_cmd->h_frames); - state.canvas_shader.set_uniform(CanvasShaderGLES3::V_FRAMES, particles_cmd->v_frames); - glBindVertexArray(data.particle_quad_array); //use particle quad array glBindBuffer(GL_ARRAY_BUFFER, particles->particle_buffers[0]); //bind particle buffer @@ -957,7 +1067,6 @@ void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *cur state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCE_CUSTOM, false); state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCING, false); - state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_PARTICLES, false); state.using_texture_rect = true; _set_texture_rect_mode(false); @@ -1107,8 +1216,6 @@ void RasterizerCanvasGLES3::canvas_render_items(Item *p_item_list, int p_z, cons bool rebind_shader = true; - Size2 rt_size = Size2(storage->frame.current_rt->width, storage->frame.current_rt->height); - state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_DISTANCE_FIELD, false); glBindBuffer(GL_UNIFORM_BUFFER, state.canvas_item_ubo); @@ -1304,7 +1411,7 @@ void RasterizerCanvasGLES3::canvas_render_items(Item *p_item_list, int p_z, cons if (blend_mode == RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_DISABLED && (!storage->frame.current_rt || !storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT])) { blend_mode = RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_MIX; } - bool unshaded = shader_cache && (shader_cache->canvas_item.light_mode == RasterizerStorageGLES3::Shader::CanvasItem::LIGHT_MODE_UNSHADED || blend_mode != RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_MIX); + bool unshaded = shader_cache && (shader_cache->canvas_item.light_mode == RasterizerStorageGLES3::Shader::CanvasItem::LIGHT_MODE_UNSHADED || (blend_mode != RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_MIX && blend_mode != RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_PMALPHA)); bool reclip = false; if (last_blend_mode != blend_mode) { @@ -1477,6 +1584,7 @@ void RasterizerCanvasGLES3::canvas_render_items(Item *p_item_list, int p_z, cons if (!t) { glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex); } else { + t = t->get_ptr(); glBindTexture(t->target, t->tex_id); } @@ -1754,6 +1862,39 @@ void RasterizerCanvasGLES3::draw_generic_textured_rect(const Rect2 &p_rect, cons glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } +void RasterizerCanvasGLES3::draw_lens_distortion_rect(const Rect2 &p_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample) { + Vector2 half_size; + if (storage->frame.current_rt) { + half_size = Vector2(storage->frame.current_rt->width, storage->frame.current_rt->height); + } else { + half_size = OS::get_singleton()->get_window_size(); + } + half_size *= 0.5; + Vector2 offset((p_rect.position.x - half_size.x) / half_size.x, (p_rect.position.y - half_size.y) / half_size.y); + Vector2 scale(p_rect.size.x / half_size.x, p_rect.size.y / half_size.y); + + float aspect_ratio = p_rect.size.x / p_rect.size.y; + + // setup our lens shader + state.lens_shader.bind(); + state.lens_shader.set_uniform(LensDistortedShaderGLES3::OFFSET, offset); + state.lens_shader.set_uniform(LensDistortedShaderGLES3::SCALE, scale); + state.lens_shader.set_uniform(LensDistortedShaderGLES3::K1, p_k1); + state.lens_shader.set_uniform(LensDistortedShaderGLES3::K2, p_k2); + state.lens_shader.set_uniform(LensDistortedShaderGLES3::EYE_CENTER, p_eye_center); + state.lens_shader.set_uniform(LensDistortedShaderGLES3::UPSCALE, p_oversample); + state.lens_shader.set_uniform(LensDistortedShaderGLES3::ASPECT_RATIO, aspect_ratio); + + glBindBufferBase(GL_UNIFORM_BUFFER, 0, state.canvas_item_ubo); + glBindVertexArray(data.canvas_quad_array); + + // and draw + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + glBindVertexArray(0); + glBindBufferBase(GL_UNIFORM_BUFFER, 0, 0); +} + void RasterizerCanvasGLES3::draw_window_margins(int *black_margin, RID *black_image) { Vector2 window_size = OS::get_singleton()->get_window_size(); @@ -1879,6 +2020,7 @@ void RasterizerCanvasGLES3::initialize() { { uint32_t poly_size = GLOBAL_DEF_RST("rendering/limits/buffers/canvas_polygon_buffer_size_kb", 128); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/buffers/canvas_polygon_buffer_size_kb", PropertyInfo(Variant::INT, "rendering/limits/buffers/canvas_polygon_buffer_size_kb", PROPERTY_HINT_RANGE, "0,256,1,or_greater")); poly_size *= 1024; //kb poly_size = MAX(poly_size, (2 + 2 + 4) * 4 * sizeof(float)); glGenBuffers(1, &data.polygon_buffer); @@ -1926,6 +2068,7 @@ void RasterizerCanvasGLES3::initialize() { glGenVertexArrays(1, &data.polygon_buffer_pointer_array); uint32_t index_size = GLOBAL_DEF_RST("rendering/limits/buffers/canvas_polygon_index_buffer_size_kb", 128); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/buffers/canvas_polygon_index_buffer_size_kb", PropertyInfo(Variant::INT, "rendering/limits/buffers/canvas_polygon_index_buffer_size_kb", PROPERTY_HINT_RANGE, "0,256,1,or_greater")); index_size *= 1024; //kb glGenBuffers(1, &data.polygon_index_buffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.polygon_index_buffer); @@ -1943,6 +2086,7 @@ void RasterizerCanvasGLES3::initialize() { state.canvas_shader.init(); state.canvas_shader.set_base_material_tex_index(2); state.canvas_shadow_shader.init(); + state.lens_shader.init(); state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_RGBA_SHADOWS, storage->config.use_rgba_2d_shadows); state.canvas_shadow_shader.set_conditional(CanvasShadowShaderGLES3::USE_RGBA_SHADOWS, storage->config.use_rgba_2d_shadows); diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h index c7f2e54efb..3f306003b4 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.h +++ b/drivers/gles3/rasterizer_canvas_gles3.h @@ -33,7 +33,9 @@ #include "rasterizer_storage_gles3.h" #include "servers/visual/rasterizer.h" + #include "shaders/canvas_shadow.glsl.gen.h" +#include "shaders/lens_distorted.glsl.gen.h" class RasterizerSceneGLES3; @@ -71,6 +73,7 @@ public: bool canvas_texscreen_used; CanvasShaderGLES3 canvas_shader; CanvasShadowShaderGLES3 canvas_shadow_shader; + LensDistortedShaderGLES3 lens_shader; bool using_texture_rect; bool using_ninepatch; @@ -140,6 +143,7 @@ public: virtual void reset_canvas(); void draw_generic_textured_rect(const Rect2 &p_rect, const Rect2 &p_src); + void draw_lens_distortion_rect(const Rect2 &p_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample); void initialize(); void finalize(); diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index 0d42635194..2f6a556773 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -30,11 +30,9 @@ #include "rasterizer_gles3.h" -#include "gl_context/context_gl.h" -#include "os/os.h" -#include "project_settings.h" - -#include <string.h> +#include "core/os/os.h" +#include "core/project_settings.h" +#include "drivers/gl_context/context_gl.h" RasterizerStorage *RasterizerGLES3::get_storage() { @@ -75,6 +73,12 @@ RasterizerScene *RasterizerGLES3::get_scene() { #define _EXT_DEBUG_SEVERITY_LOW_ARB 0x9148 #define _EXT_DEBUG_OUTPUT 0x92E0 +#if defined(MINGW_ENABLED) || defined(_MSC_VER) +#define strcpy strcpy_s +#endif + +#ifdef GLAD_ENABLED +// Restricting to GLAD as only used in initialize() with GLAD_GL_ARB_debug_output #if (defined WINDOWS_ENABLED) && !(defined UWP_ENABLED) #define GLAPIENTRY APIENTRY #else @@ -125,6 +129,7 @@ static void GLAPIENTRY _gl_debug_print(GLenum source, GLenum type, GLuint id, GL ERR_PRINTS(output); } +#endif // GLAD_ENABLED typedef void (*DEBUGPROCARB)(GLenum source, GLenum type, @@ -136,28 +141,32 @@ typedef void (*DEBUGPROCARB)(GLenum source, typedef void (*DebugMessageCallbackARB)(DEBUGPROCARB callback, const void *userParam); -void RasterizerGLES3::initialize() { - - print_verbose("Using GLES3 video driver"); +Error RasterizerGLES3::is_viable() { #ifdef GLAD_ENABLED if (!gladLoadGL()) { ERR_PRINT("Error initializing GLAD"); + return ERR_UNAVAILABLE; } // GLVersion seems to be used for both GL and GL ES, so we need different version checks for them #ifdef OPENGL_ENABLED // OpenGL 3.3 Core Profile required - if (GLVersion.major < 3 && GLVersion.minor < 3) { + if (GLVersion.major < 3 || (GLVersion.major == 3 && GLVersion.minor < 3)) { #else // OpenGL ES 3.0 if (GLVersion.major < 3) { #endif - ERR_PRINT("Your system's graphic drivers seem not to support OpenGL 3.3 / OpenGL ES 3.0, sorry :(\n" - "Try a drivers update, buy a new GPU or try software rendering on Linux; Godot will now crash with a segmentation fault."); - OS::get_singleton()->alert("Your system's graphic drivers seem not to support OpenGL 3.3 / OpenGL ES 3.0, sorry :(\n" - "Godot Engine will self-destruct as soon as you acknowledge this error message.", - "Fatal error: Insufficient OpenGL / GLES driver support"); + return ERR_UNAVAILABLE; } +#endif // GLAD_ENABLED + return OK; +} + +void RasterizerGLES3::initialize() { + + print_verbose("Using GLES3 video driver"); + +#ifdef GLAD_ENABLED if (OS::get_singleton()->is_stdout_verbose()) { if (GLAD_GL_ARB_debug_output) { glEnable(_EXT_DEBUG_OUTPUT_SYNCHRONOUS_ARB); @@ -167,7 +176,6 @@ void RasterizerGLES3::initialize() { print_line("OpenGL debugging not supported!"); } } - #endif // GLAD_ENABLED /* // For debugging @@ -358,6 +366,26 @@ void RasterizerGLES3::blit_render_target_to_screen(RID p_render_target, const Re #endif } +void RasterizerGLES3::output_lens_distorted_to_screen(RID p_render_target, const Rect2 &p_screen_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample) { + ERR_FAIL_COND(storage->frame.current_rt); + + RasterizerStorageGLES3::RenderTarget *rt = storage->render_target_owner.getornull(p_render_target); + ERR_FAIL_COND(!rt); + + glDisable(GL_BLEND); + + // render to our framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, RasterizerStorageGLES3::system_fbo); + + // output our texture + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, rt->color); + + canvas->draw_lens_distortion_rect(p_screen_rect, p_k1, p_k2, p_eye_center, p_oversample); + + glBindTexture(GL_TEXTURE_2D, 0); +} + void RasterizerGLES3::end_frame(bool p_swap_buffers) { if (OS::get_singleton()->is_layered_allowed()) { @@ -408,6 +436,7 @@ void RasterizerGLES3::register_config() { GLOBAL_DEF("rendering/quality/filters/anisotropic_filter_level", 4); ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/filters/anisotropic_filter_level", PropertyInfo(Variant::INT, "rendering/quality/filters/anisotropic_filter_level", PROPERTY_HINT_RANGE, "1,16,1")); GLOBAL_DEF("rendering/limits/time/time_rollover_secs", 3600); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/time/time_rollover_secs", PropertyInfo(Variant::REAL, "rendering/limits/time/time_rollover_secs", PROPERTY_HINT_RANGE, "0,10000,1,or_greater")); } RasterizerGLES3::RasterizerGLES3() { diff --git a/drivers/gles3/rasterizer_gles3.h b/drivers/gles3/rasterizer_gles3.h index f4449ac0f9..477e0dfd48 100644 --- a/drivers/gles3/rasterizer_gles3.h +++ b/drivers/gles3/rasterizer_gles3.h @@ -59,12 +59,16 @@ public: virtual void restore_render_target(); virtual void clear_render_target(const Color &p_color); virtual void blit_render_target_to_screen(RID p_render_target, const Rect2 &p_screen_rect, int p_screen = 0); + virtual void output_lens_distorted_to_screen(RID p_render_target, const Rect2 &p_screen_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample); virtual void end_frame(bool p_swap_buffers); virtual void finalize(); + static Error is_viable(); static void make_current(); - static void register_config(); + + virtual bool is_low_end() const { return false; } + RasterizerGLES3(); ~RasterizerGLES3(); }; diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index eebdbe9493..792e9eb238 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -29,9 +29,10 @@ /*************************************************************************/ #include "rasterizer_scene_gles3.h" -#include "math_funcs.h" -#include "os/os.h" -#include "project_settings.h" + +#include "core/math/math_funcs.h" +#include "core/os/os.h" +#include "core/project_settings.h" #include "rasterizer_canvas_gles3.h" #include "servers/visual/visual_server_raster.h" @@ -50,26 +51,6 @@ static const GLenum _cube_side_enum[6] = { }; -static _FORCE_INLINE_ void store_transform2d(const Transform2D &p_mtx, float *p_array) { - - p_array[0] = p_mtx.elements[0][0]; - p_array[1] = p_mtx.elements[0][1]; - p_array[2] = 0; - p_array[3] = 0; - p_array[4] = p_mtx.elements[1][0]; - p_array[5] = p_mtx.elements[1][1]; - p_array[6] = 0; - p_array[7] = 0; - p_array[8] = 0; - p_array[9] = 0; - p_array[10] = 1; - p_array[11] = 0; - p_array[12] = p_mtx.elements[2][0]; - p_array[13] = p_mtx.elements[2][1]; - p_array[14] = 0; - p_array[15] = 1; -} - static _FORCE_INLINE_ void store_transform(const Transform &p_mtx, float *p_array) { p_array[0] = p_mtx.basis.elements[0][0]; p_array[1] = p_mtx.basis.elements[1][0]; @@ -672,7 +653,7 @@ bool RasterizerSceneGLES3::reflection_probe_instance_begin_render(RID p_instance int best_free = -1; int best_used = -1; - uint64_t best_used_frame; + uint64_t best_used_frame = 0; for (int i = 0; i < reflection_atlas->reflections.size(); i++) { if (reflection_atlas->reflections[i].owner == RID()) { @@ -1008,7 +989,10 @@ RID RasterizerSceneGLES3::light_instance_create(RID p_light) { light_instance->light = p_light; light_instance->light_ptr = storage->light_owner.getornull(p_light); - ERR_FAIL_COND_V(!light_instance->light_ptr, RID()); + if (!light_instance->light_ptr) { + memdelete(light_instance); + ERR_FAIL_COND_V(!light_instance->light_ptr, RID()); + } light_instance->self = light_instance_owner.make_rid(light_instance); @@ -1198,7 +1182,7 @@ bool RasterizerSceneGLES3::_setup_material(RasterizerStorageGLES3::Material *p_m glActiveTexture(GL_TEXTURE0 + i); - GLenum target; + GLenum target = GL_TEXTURE_2D; GLuint tex = 0; RasterizerStorageGLES3::Texture *t = storage->texture_owner.getptr(textures[i]); @@ -1262,14 +1246,11 @@ bool RasterizerSceneGLES3::_setup_material(RasterizerStorageGLES3::Material *p_m case ShaderLanguage::TYPE_SAMPLER3D: { target = GL_TEXTURE_3D; + tex = storage->resources.white_tex_3d; - switch (texture_hints[i]) { - - // TODO - default: { - tex = storage->resources.white_tex_3d; - } break; - } + //switch (texture_hints[i]) { + // TODO + //} } break; @@ -1278,6 +1259,8 @@ bool RasterizerSceneGLES3::_setup_material(RasterizerStorageGLES3::Material *p_m case ShaderLanguage::TYPE_SAMPLER2DARRAY: { // TODO } break; + + default: {} } } @@ -1445,7 +1428,16 @@ void RasterizerSceneGLES3::_setup_geometry(RenderList::Element *e, const Transfo if (particles->draw_order == VS::PARTICLES_DRAW_ORDER_VIEW_DEPTH && particles->particle_valid_histories[1]) { glBindBuffer(GL_ARRAY_BUFFER, particles->particle_buffer_histories[1]); //modify the buffer, this was used 2 frames ago so it should be good enough for flushing - RasterizerGLES3Particle *particle_array = (RasterizerGLES3Particle *)glMapBufferRange(GL_ARRAY_BUFFER, 0, particles->amount * 24 * sizeof(float), GL_MAP_READ_BIT | GL_MAP_WRITE_BIT); + RasterizerGLES3Particle *particle_array; +#ifndef __EMSCRIPTEN__ + particle_array = static_cast<RasterizerGLES3Particle *>(glMapBufferRange(GL_ARRAY_BUFFER, 0, particles->amount * 24 * sizeof(float), GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)); +#else + PoolVector<RasterizerGLES3Particle> particle_vector; + particle_vector.resize(particles->amount); + PoolVector<RasterizerGLES3Particle>::Write w = particle_vector.write(); + particle_array = w.ptr(); + glGetBufferSubData(GL_ARRAY_BUFFER, 0, particles->amount * sizeof(RasterizerGLES3Particle), particle_array); +#endif SortArray<RasterizerGLES3Particle, RasterizerGLES3ParticleSort> sorter; @@ -1457,7 +1449,17 @@ void RasterizerSceneGLES3::_setup_geometry(RenderList::Element *e, const Transfo sorter.sort(particle_array, particles->amount); +#ifndef __EMSCRIPTEN__ glUnmapBuffer(GL_ARRAY_BUFFER); +#else + w = PoolVector<RasterizerGLES3Particle>::Write(); + particle_array = NULL; + { + PoolVector<RasterizerGLES3Particle>::Read r = particle_vector.read(); + glBufferSubData(GL_ARRAY_BUFFER, 0, particles->amount * sizeof(RasterizerGLES3Particle), r.ptr()); + } + particle_vector = PoolVector<RasterizerGLES3Particle>(); +#endif #ifdef DEBUG_ENABLED if (state.debug_draw == VS::VIEWPORT_DEBUG_DRAW_WIREFRAME && s->instancing_array_wireframe_id) { glBindVertexArray(s->instancing_array_wireframe_id); // use the wireframe instancing array ID @@ -1505,6 +1507,7 @@ void RasterizerSceneGLES3::_setup_geometry(RenderList::Element *e, const Transfo } } break; + default: {} } } @@ -1553,8 +1556,11 @@ void RasterizerSceneGLES3::_render_geometry(RenderList::Element *e) { RasterizerStorageGLES3::MultiMesh *multi_mesh = static_cast<RasterizerStorageGLES3::MultiMesh *>(e->owner); RasterizerStorageGLES3::Surface *s = static_cast<RasterizerStorageGLES3::Surface *>(e->geometry); - int amount = MAX(multi_mesh->size, multi_mesh->visible_instances); + int amount = MIN(multi_mesh->size, multi_mesh->visible_instances); + if (amount == -1) { + amount = multi_mesh->size; + } #ifdef DEBUG_ENABLED if (state.debug_draw == VS::VIEWPORT_DEBUG_DRAW_WIREFRAME && s->array_wireframe_id) { @@ -1823,6 +1829,7 @@ void RasterizerSceneGLES3::_render_geometry(RenderList::Element *e) { } } break; + default: {} } } @@ -2360,14 +2367,15 @@ void RasterizerSceneGLES3::_add_geometry_with_material(RasterizerStorageGLES3::G 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_depth_pass) { + if (e->material->last_pass != render_pass) { + e->material->last_pass = render_pass; + e->material->index = current_material_index++; + } - if (e->material->last_pass != render_pass) { - e->material->last_pass = render_pass; - e->material->index = current_material_index++; - } + e->sort_key |= uint64_t(e->material->index) << RenderList::SORT_KEY_MATERIAL_INDEX_SHIFT; + e->sort_key |= uint64_t(e->instance->depth_layer) << RenderList::SORT_KEY_OPAQUE_DEPTH_LAYER_SHIFT; - e->sort_key |= uint64_t(e->material->index) << RenderList::SORT_KEY_MATERIAL_INDEX_SHIFT; + if (!p_depth_pass) { if (e->instance->gi_probe_instances.size()) { e->sort_key |= SORT_KEY_GI_PROBES_FLAG; @@ -2382,9 +2390,6 @@ void RasterizerSceneGLES3::_add_geometry_with_material(RasterizerStorageGLES3::G } e->sort_key |= uint64_t(p_material->render_priority + 128) << RenderList::SORT_KEY_PRIORITY_SHIFT; - } else { - e->sort_key |= uint64_t(e->instance->depth_layer) << RenderList::SORT_KEY_OPAQUE_DEPTH_LAYER_SHIFT; - e->sort_key |= uint64_t(e->material->index) << RenderList::SORT_KEY_MATERIAL_INDEX_SHIFT; } /* @@ -2530,7 +2535,7 @@ void RasterizerSceneGLES3::_draw_sky(RasterizerStorageGLES3::Sky *p_sky, const C storage->shaders.copy.set_conditional(CopyShaderGLES3::USE_PANORAMA, false); } -void RasterizerSceneGLES3::_setup_environment(Environment *env, const CameraMatrix &p_cam_projection, const Transform &p_cam_transform) { +void RasterizerSceneGLES3::_setup_environment(Environment *env, const CameraMatrix &p_cam_projection, const Transform &p_cam_transform, bool p_no_fog) { //store camera into ubo store_camera(p_cam_projection, state.ubo_data.projection_matrix); @@ -2581,7 +2586,7 @@ void RasterizerSceneGLES3::_setup_environment(Environment *env, const CameraMatr state.ubo_data.fog_color_enabled[0] = linear_fog.r; state.ubo_data.fog_color_enabled[1] = linear_fog.g; state.ubo_data.fog_color_enabled[2] = linear_fog.b; - state.ubo_data.fog_color_enabled[3] = env->fog_enabled ? 1.0 : 0.0; + state.ubo_data.fog_color_enabled[3] = (!p_no_fog && env->fog_enabled) ? 1.0 : 0.0; Color linear_sun = env->fog_sun_color.to_linear(); state.ubo_data.fog_sun_color_amount[0] = linear_sun.r; @@ -2734,7 +2739,7 @@ void RasterizerSceneGLES3::_setup_directional_light(int p_index, const Transform ubo_data.shadow_split_offsets[j] = li->shadow_transform[j].split; - Transform modelview = (p_camera_inverse_transform * li->shadow_transform[j].transform).inverse(); + Transform modelview = (p_camera_inverse_transform * li->shadow_transform[j].transform).affine_inverse(); CameraMatrix bias; bias.set_light_bias(); @@ -3031,13 +3036,14 @@ void RasterizerSceneGLES3::_setup_reflections(RID *p_reflection_probe_cull_resul reflection_ubo.ambient[3] = rpi->probe_ptr->interior_ambient_probe_contrib; } else { Color ambient_linear; - float contrib = 0; + // FIXME: contrib was retrieved but never used, is it meant to be set as ambient[3]? (GH-20361) + //float contrib = 0; if (p_env) { ambient_linear = p_env->ambient_color.to_linear(); ambient_linear.r *= p_env->ambient_energy; ambient_linear.g *= p_env->ambient_energy; ambient_linear.b *= p_env->ambient_energy; - contrib = p_env->ambient_sky_contribution; + //contrib = p_env->ambient_sky_contribution; } reflection_ubo.ambient[0] = ambient_linear.r; @@ -3094,40 +3100,6 @@ void RasterizerSceneGLES3::_copy_screen(bool p_invalidate_color, bool p_invalida glBindVertexArray(0); } -void RasterizerSceneGLES3::_copy_to_front_buffer(Environment *env) { - - //copy to front buffer - glBindFramebuffer(GL_FRAMEBUFFER, storage->frame.current_rt->fbo); - - glDepthMask(GL_FALSE); - glDisable(GL_DEPTH_TEST); - glDisable(GL_CULL_FACE); - glDisable(GL_BLEND); - glDepthFunc(GL_LEQUAL); - glColorMask(1, 1, 1, 1); - - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, storage->frame.current_rt->buffers.diffuse); - - storage->shaders.copy.set_conditional(CopyShaderGLES3::DISABLE_ALPHA, true); - - if (!env) { - //no environment, simply convert from linear to srgb - storage->shaders.copy.set_conditional(CopyShaderGLES3::LINEAR_TO_SRGB, true); - } else { - /* FIXME: Why are both statements equal? */ - storage->shaders.copy.set_conditional(CopyShaderGLES3::LINEAR_TO_SRGB, true); - } - - storage->shaders.copy.bind(); - - _copy_screen(); - - //turn off everything used - storage->shaders.copy.set_conditional(CopyShaderGLES3::LINEAR_TO_SRGB, false); - storage->shaders.copy.set_conditional(CopyShaderGLES3::DISABLE_ALPHA, false); -} - void RasterizerSceneGLES3::_copy_texture_to_front_buffer(GLuint p_texture) { //copy to front buffer @@ -3161,7 +3133,6 @@ void RasterizerSceneGLES3::_fill_render_list(InstanceBase **p_cull_result, int p current_material_index = 0; state.used_sss = false; state.used_screen_texture = false; - //fill list for (int i = 0; i < p_cull_count; i++) { @@ -3239,6 +3210,7 @@ void RasterizerSceneGLES3::_fill_render_list(InstanceBase **p_cull_result, int p } } break; + default: {} } } } @@ -3975,7 +3947,7 @@ void RasterizerSceneGLES3::_post_process(Environment *env, const CameraMatrix &p state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_FILMIC_TONEMAPPER, env->tone_mapper == VS::ENV_TONE_MAPPER_FILMIC); state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_ACES_TONEMAPPER, env->tone_mapper == VS::ENV_TONE_MAPPER_ACES); - state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_REINDHART_TONEMAPPER, env->tone_mapper == VS::ENV_TONE_MAPPER_REINHARDT); + state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_REINHARD_TONEMAPPER, env->tone_mapper == VS::ENV_TONE_MAPPER_REINHARD); state.tonemap_shader.set_conditional(TonemapShaderGLES3::KEEP_3D_LINEAR, storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_KEEP_3D_LINEAR]); state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_AUTO_EXPOSURE, env->auto_exposure); @@ -4062,7 +4034,7 @@ void RasterizerSceneGLES3::_post_process(Environment *env, const CameraMatrix &p state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_AUTO_EXPOSURE, false); state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_FILMIC_TONEMAPPER, false); state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_ACES_TONEMAPPER, false); - state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_REINDHART_TONEMAPPER, false); + state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_REINHARD_TONEMAPPER, false); state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_GLOW_LEVEL1, false); state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_GLOW_LEVEL2, false); state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_GLOW_LEVEL3, false); @@ -4127,7 +4099,7 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const state.ubo_data.screen_pixel_size[1] = 1.0 / storage->frame.current_rt->height; } - _setup_environment(env, p_cam_projection, p_cam_transform); + _setup_environment(env, p_cam_projection, p_cam_transform, p_reflection_probe.is_valid()); bool fb_cleared = false; @@ -4325,7 +4297,6 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const if (env) { switch (env->bg_mode) { case VS::ENV_BG_COLOR_SKY: - case VS::ENV_BG_SKY: sky = storage->sky_owner.getornull(env->sky); @@ -4363,6 +4334,7 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); break; + default: {} } } @@ -4531,7 +4503,7 @@ void RasterizerSceneGLES3::render_shadow(RID p_light, RID p_shadow_atlas, int p_ RasterizerStorageGLES3::Light *light = storage->light_owner.getornull(light_instance->light); ERR_FAIL_COND(!light); - uint32_t x, y, width, height, vp_height; + uint32_t x, y, width, height; float dp_direction = 0.0; float zfar = 0; @@ -4613,7 +4585,6 @@ void RasterizerSceneGLES3::render_shadow(RID p_light, RID p_shadow_atlas, int p_ 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 { //set from shadow atlas @@ -4623,7 +4594,6 @@ void RasterizerSceneGLES3::render_shadow(RID p_light, RID p_shadow_atlas, int p_ 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]; @@ -4901,10 +4871,7 @@ void RasterizerSceneGLES3::initialize() { glBindBuffer(GL_UNIFORM_BUFFER, 0); render_list.max_elements = GLOBAL_DEF_RST("rendering/limits/rendering/max_renderable_elements", (int)RenderList::DEFAULT_MAX_ELEMENTS); - if (render_list.max_elements > 1000000) - render_list.max_elements = 1000000; - if (render_list.max_elements < 1024) - render_list.max_elements = 1024; + ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/rendering/max_renderable_elements", PropertyInfo(Variant::INT, "rendering/limits/rendering/max_renderable_elements", PROPERTY_HINT_RANGE, "1024,1000000,1")); { //quad buffers @@ -5103,6 +5070,7 @@ void RasterizerSceneGLES3::initialize() { { uint32_t immediate_buffer_size = GLOBAL_DEF("rendering/limits/buffers/immediate_buffer_size_kb", 2048); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/buffers/immediate_buffer_size_kb", PropertyInfo(Variant::INT, "rendering/limits/buffers/immediate_buffer_size_kb", PROPERTY_HINT_RANGE, "0,8192,1,or_greater")); glGenBuffers(1, &state.immediate_buffer); glBindBuffer(GL_ARRAY_BUFFER, state.immediate_buffer); @@ -5177,13 +5145,13 @@ void RasterizerSceneGLES3::initialize() { void RasterizerSceneGLES3::iteration() { - shadow_filter_mode = ShadowFilterMode(int(ProjectSettings::get_singleton()->get("rendering/quality/shadows/filter_mode"))); - subsurface_scatter_follow_surface = ProjectSettings::get_singleton()->get("rendering/quality/subsurface_scattering/follow_surface"); - subsurface_scatter_weight_samples = ProjectSettings::get_singleton()->get("rendering/quality/subsurface_scattering/weight_samples"); - subsurface_scatter_quality = SubSurfaceScatterQuality(int(ProjectSettings::get_singleton()->get("rendering/quality/subsurface_scattering/quality"))); - subsurface_scatter_size = ProjectSettings::get_singleton()->get("rendering/quality/subsurface_scattering/scale"); + shadow_filter_mode = ShadowFilterMode(int(GLOBAL_GET("rendering/quality/shadows/filter_mode"))); + subsurface_scatter_follow_surface = GLOBAL_GET("rendering/quality/subsurface_scattering/follow_surface"); + subsurface_scatter_weight_samples = GLOBAL_GET("rendering/quality/subsurface_scattering/weight_samples"); + subsurface_scatter_quality = SubSurfaceScatterQuality(int(GLOBAL_GET("rendering/quality/subsurface_scattering/quality"))); + subsurface_scatter_size = GLOBAL_GET("rendering/quality/subsurface_scattering/scale"); - state.scene_shader.set_conditional(SceneShaderGLES3::VCT_QUALITY_HIGH, ProjectSettings::get_singleton()->get("rendering/quality/voxel_cone_tracing/high_quality")); + state.scene_shader.set_conditional(SceneShaderGLES3::VCT_QUALITY_HIGH, GLOBAL_GET("rendering/quality/voxel_cone_tracing/high_quality")); } void RasterizerSceneGLES3::finalize() { diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index cf387a69bc..4b4a0b9303 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -831,13 +831,12 @@ public: void _draw_sky(RasterizerStorageGLES3::Sky *p_sky, const CameraMatrix &p_projection, const Transform &p_transform, bool p_vflip, float p_custom_fov, float p_energy); - void _setup_environment(Environment *env, const CameraMatrix &p_cam_projection, const Transform &p_cam_transform); + void _setup_environment(Environment *env, const CameraMatrix &p_cam_projection, const Transform &p_cam_transform, bool p_no_fog = false); void _setup_directional_light(int p_index, const Transform &p_camera_inverse_transform, bool p_use_shadows); void _setup_lights(RID *p_light_cull_result, int p_light_cull_count, const Transform &p_camera_inverse_transform, const CameraMatrix &p_camera_projection, RID p_shadow_atlas); void _setup_reflections(RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, const Transform &p_camera_inverse_transform, const CameraMatrix &p_camera_projection, RID p_reflection_atlas, Environment *p_env); void _copy_screen(bool p_invalidate_color = false, bool p_invalidate_depth = false); - void _copy_to_front_buffer(Environment *env); void _copy_texture_to_front_buffer(GLuint p_texture); //used for debug void _fill_render_list(InstanceBase **p_cull_result, int p_cull_count, bool p_depth_pass, bool p_shadow_pass); diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp index 3697cccef4..649449a891 100644 --- a/drivers/gles3/rasterizer_storage_gles3.cpp +++ b/drivers/gles3/rasterizer_storage_gles3.cpp @@ -29,8 +29,8 @@ /*************************************************************************/ #include "rasterizer_storage_gles3.h" -#include "engine.h" -#include "project_settings.h" +#include "core/engine.h" +#include "core/project_settings.h" #include "rasterizer_canvas_gles3.h" #include "rasterizer_scene_gles3.h" @@ -101,6 +101,28 @@ #define _EXT_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E #define _EXT_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F +#ifdef __EMSCRIPTEN__ +#include <emscripten/emscripten.h> + +void glGetBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, GLvoid *data) { + + /* clang-format off */ + EM_ASM({ + GLctx.getBufferSubData($0, $1, HEAPU8, $2, $3); + }, target, offset, data, size); + /* clang-format on */ +} + +void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data) { + + /* clang-format off */ + EM_ASM({ + GLctx.bufferSubData($0, $1, HEAPU8, $2, $3); + }, target, offset, data, size); + /* clang-format on */ +} +#endif + void glTexStorage2DCustom(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type) { #ifdef GLES_OVER_GL @@ -730,7 +752,7 @@ void RasterizerStorageGLES3::texture_set_data(RID p_texture, const Ref<Image> &p } }; - GLenum blit_target; + GLenum blit_target = GL_TEXTURE_2D; switch (texture->type) { case VS::TEXTURE_TYPE_2D: { @@ -948,7 +970,7 @@ void RasterizerStorageGLES3::texture_set_data_partial(RID p_texture, const Ref<I Image::Format real_format; Ref<Image> img = _get_gl_image_and_format(p_sub_img, p_sub_img->get_format(), texture->flags, real_format, format, internal_format, type, compressed, srgb); - GLenum blit_target; + GLenum blit_target = GL_TEXTURE_2D; switch (texture->type) { case VS::TEXTURE_TYPE_2D: { @@ -1029,7 +1051,7 @@ Ref<Image> RasterizerStorageGLES3::texture_get_data(RID p_texture, int p_layer) PoolVector<uint8_t> data; - int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, real_format, texture->mipmaps > 1 ? -1 : 0); + int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, real_format, texture->mipmaps > 1); data.resize(data_size * 2); //add some memory at the end, just in case for buggy drivers PoolVector<uint8_t>::Write wb = data.write(); @@ -1072,7 +1094,7 @@ Ref<Image> RasterizerStorageGLES3::texture_get_data(RID p_texture, int p_layer) uint32_t *ptr = (uint32_t *)wb.ptr(); uint32_t num_pixels = data_size / 4; - for (int ofs = 0; ofs < num_pixels; ofs++) { + for (uint32_t ofs = 0; ofs < num_pixels; ofs++) { uint32_t px = ptr[ofs]; uint32_t a = px >> 30 & 0xFF; @@ -1094,8 +1116,78 @@ Ref<Image> RasterizerStorageGLES3::texture_get_data(RID p_texture, int p_layer) return Ref<Image>(img); #else - ERR_EXPLAIN("Sorry, It's not possible to obtain images back in OpenGL ES"); - return Ref<Image>(); + Image::Format real_format; + GLenum gl_format; + GLenum gl_internal_format; + GLenum gl_type; + bool compressed; + bool srgb; + _get_gl_image_and_format(Ref<Image>(), texture->format, texture->flags, real_format, gl_format, gl_internal_format, gl_type, compressed, srgb); + + PoolVector<uint8_t> data; + + int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, Image::FORMAT_RGBA8, false); + + data.resize(data_size * 2); //add some memory at the end, just in case for buggy drivers + PoolVector<uint8_t>::Write wb = data.write(); + + GLuint temp_framebuffer; + glGenFramebuffers(1, &temp_framebuffer); + + GLuint temp_color_texture; + glGenTextures(1, &temp_color_texture); + + glBindFramebuffer(GL_FRAMEBUFFER, temp_framebuffer); + + glBindTexture(GL_TEXTURE_2D, temp_color_texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture->alloc_width, texture->alloc_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + print_line(itos(texture->alloc_width) + " xx " + itos(texture->alloc_height) + " -> " + itos(real_format)); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, temp_color_texture, 0); + + glDepthMask(GL_FALSE); + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glDisable(GL_BLEND); + glDepthFunc(GL_LEQUAL); + glColorMask(1, 1, 1, 1); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture->tex_id); + + glViewport(0, 0, texture->alloc_width, texture->alloc_height); + + shaders.copy.bind(); + + shaders.copy.set_conditional(CopyShaderGLES3::LINEAR_TO_SRGB, !srgb); + + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT); + glBindVertexArray(resources.quadie_array); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + glBindVertexArray(0); + + glReadPixels(0, 0, texture->alloc_width, texture->alloc_height, GL_RGBA, GL_UNSIGNED_BYTE, &wb[0]); + + shaders.copy.set_conditional(CopyShaderGLES3::LINEAR_TO_SRGB, false); + + glDeleteTextures(1, &temp_color_texture); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &temp_framebuffer); + + wb = PoolVector<uint8_t>::Write(); + + data.resize(data_size); + + Image *img = memnew(Image(texture->alloc_width, texture->alloc_height, false, Image::FORMAT_RGBA8, data)); + if (!texture->compressed) { + img->convert(real_format); + } + + return Ref<Image>(img); #endif } @@ -1905,6 +1997,7 @@ void RasterizerStorageGLES3::_update_shader(Shader *p_shader) const { actions = &shaders.actions_particles; actions->uniforms = &p_shader->uniforms; } break; + case VS::SHADER_MAX: break; // Can't happen, but silences warning } Error err = shaders.compiler.compile(p_shader->mode, p_shader->code, actions, p_shader->path, gen_code); @@ -2028,6 +2121,14 @@ 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_SAMPLER2DARRAY: + case ShaderLanguage::TYPE_ISAMPLER2DARRAY: + case ShaderLanguage::TYPE_USAMPLER2DARRAY: { + + pi.type = Variant::OBJECT; + pi.hint = PROPERTY_HINT_RESOURCE_TYPE; + pi.hint_string = "TextureArray"; + } break; case ShaderLanguage::TYPE_SAMPLER3D: case ShaderLanguage::TYPE_ISAMPLER3D: case ShaderLanguage::TYPE_USAMPLER3D: { @@ -2139,6 +2240,19 @@ Variant RasterizerStorageGLES3::material_get_param(RID p_material, const StringN if (material->params.has(p_param)) return material->params[p_param]; + return material_get_param_default(p_material, p_param); +} + +Variant RasterizerStorageGLES3::material_get_param_default(RID p_material, const StringName &p_param) const { + const Material *material = material_owner.get(p_material); + ERR_FAIL_COND_V(!material, Variant()); + + if (material->shader) { + if (material->shader->uniforms.has(p_param)) { + Vector<ShaderLanguage::ConstantNode::Value> default_value = material->shader->uniforms[p_param].default_value; + return ShaderLanguage::constant_value_to_variant(default_value, material->shader->uniforms[p_param].type); + } + } return Variant(); } @@ -2716,7 +2830,7 @@ void RasterizerStorageGLES3::_update_material(Material *material) { if (material->shader && material->shader->mode == VS::SHADER_SPATIAL) { if (material->shader->spatial.blend_mode == Shader::Spatial::BLEND_MODE_MIX && - (!material->shader->spatial.uses_alpha || (material->shader->spatial.uses_alpha && material->shader->spatial.depth_draw_mode == Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS))) { + (!material->shader->spatial.uses_alpha || material->shader->spatial.depth_draw_mode == Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS)) { can_cast_shadow = true; } @@ -3472,21 +3586,26 @@ PoolVector<uint8_t> RasterizerStorageGLES3::mesh_surface_get_array(RID p_mesh, i Surface *surface = mesh->surfaces[p_surface]; - glBindBuffer(GL_ARRAY_BUFFER, surface->vertex_id); - void *data = glMapBufferRange(GL_ARRAY_BUFFER, 0, surface->array_byte_size, GL_MAP_READ_BIT); - - ERR_FAIL_COND_V(!data, PoolVector<uint8_t>()); - PoolVector<uint8_t> ret; ret.resize(surface->array_byte_size); + glBindBuffer(GL_ARRAY_BUFFER, surface->vertex_id); +#if defined(GLES_OVER_GL) || defined(__EMSCRIPTEN__) + { + PoolVector<uint8_t>::Write w = ret.write(); + glGetBufferSubData(GL_ARRAY_BUFFER, 0, surface->array_byte_size, w.ptr()); + } +#else + void *data = glMapBufferRange(GL_ARRAY_BUFFER, 0, surface->array_byte_size, GL_MAP_READ_BIT); + ERR_FAIL_NULL_V(data, PoolVector<uint8_t>()); { - PoolVector<uint8_t>::Write w = ret.write(); copymem(w.ptr(), data, surface->array_byte_size); } glUnmapBuffer(GL_ARRAY_BUFFER); +#endif + glBindBuffer(GL_ARRAY_BUFFER, 0); return ret; } @@ -3499,22 +3618,26 @@ PoolVector<uint8_t> RasterizerStorageGLES3::mesh_surface_get_index_array(RID p_m ERR_FAIL_COND_V(surface->index_array_len == 0, PoolVector<uint8_t>()); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, surface->index_id); - void *data = glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER, 0, surface->index_array_byte_size, GL_MAP_READ_BIT); - - ERR_FAIL_COND_V(!data, PoolVector<uint8_t>()); - PoolVector<uint8_t> ret; ret.resize(surface->index_array_byte_size); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, surface->index_id); +#if defined(GLES_OVER_GL) || defined(__EMSCRIPTEN__) + { + PoolVector<uint8_t>::Write w = ret.write(); + glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, surface->index_array_byte_size, w.ptr()); + } +#else + void *data = glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER, 0, surface->index_array_byte_size, GL_MAP_READ_BIT); + ERR_FAIL_NULL_V(data, PoolVector<uint8_t>()); { - PoolVector<uint8_t>::Write w = ret.write(); copymem(w.ptr(), data, surface->index_array_byte_size); } - glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); +#endif + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); return ret; } @@ -3555,23 +3678,26 @@ Vector<PoolVector<uint8_t> > RasterizerStorageGLES3::mesh_surface_get_blend_shap for (int i = 0; i < mesh->surfaces[p_surface]->blend_shapes.size(); i++) { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->surfaces[p_surface]->blend_shapes[i].vertex_id); - void *data = glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER, 0, mesh->surfaces[p_surface]->array_byte_size, GL_MAP_READ_BIT); - - ERR_FAIL_COND_V(!data, Vector<PoolVector<uint8_t> >()); - PoolVector<uint8_t> ret; ret.resize(mesh->surfaces[p_surface]->array_byte_size); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->surfaces[p_surface]->blend_shapes[i].vertex_id); +#if defined(GLES_OVER_GL) || defined(__EMSCRIPTEN__) + { + PoolVector<uint8_t>::Write w = ret.write(); + glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, mesh->surfaces[p_surface]->array_byte_size, w.ptr()); + } +#else + void *data = glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER, 0, mesh->surfaces[p_surface]->array_byte_size, GL_MAP_READ_BIT); + ERR_FAIL_COND_V(!data, Vector<PoolVector<uint8_t> >()); { - PoolVector<uint8_t>::Write w = ret.write(); copymem(w.ptr(), data, mesh->surfaces[p_surface]->array_byte_size); } + glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); +#endif bsarr.push_back(ret); - - glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); } return bsarr; @@ -4044,35 +4170,37 @@ void RasterizerStorageGLES3::multimesh_allocate(RID p_multimesh, int p_instances multimesh->data.resize(format_floats * p_instances); + float *dataptr = multimesh->data.ptrw(); + 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; + dataptr[i + 0] = 1.0; + dataptr[i + 1] = 0.0; + dataptr[i + 2] = 0.0; + dataptr[i + 3] = 0.0; + dataptr[i + 4] = 0.0; + dataptr[i + 5] = 1.0; + dataptr[i + 6] = 0.0; + dataptr[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; + dataptr[i + 0] = 1.0; + dataptr[i + 1] = 0.0; + dataptr[i + 2] = 0.0; + dataptr[i + 3] = 0.0; + dataptr[i + 4] = 0.0; + dataptr[i + 5] = 1.0; + dataptr[i + 6] = 0.0; + dataptr[i + 7] = 0.0; + dataptr[i + 8] = 0.0; + dataptr[i + 9] = 0.0; + dataptr[i + 10] = 1.0; + dataptr[i + 11] = 0.0; color_from = 12; custom_data_from = 12; } @@ -4087,14 +4215,14 @@ void RasterizerStorageGLES3::multimesh_allocate(RID p_multimesh, int p_instances } cu; cu.colu = 0xFFFFFFFF; - multimesh->data.write[i + color_from + 0] = cu.colf; + dataptr[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; + dataptr[i + color_from + 0] = 1.0; + dataptr[i + color_from + 1] = 1.0; + dataptr[i + color_from + 2] = 1.0; + dataptr[i + color_from + 3] = 1.0; custom_data_from = color_from + 4; } @@ -4108,13 +4236,13 @@ void RasterizerStorageGLES3::multimesh_allocate(RID p_multimesh, int p_instances } cu; cu.colu = 0; - multimesh->data.write[i + custom_data_from + 0] = cu.colf; + dataptr[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; + dataptr[i + custom_data_from + 0] = 0.0; + dataptr[i + custom_data_from + 1] = 0.0; + dataptr[i + custom_data_from + 2] = 0.0; + dataptr[i + custom_data_from + 3] = 0.0; } } @@ -4948,6 +5076,7 @@ void RasterizerStorageGLES3::light_set_param(RID p_light, VS::LightParam p_param light->version++; light->instance_change_notify(); } break; + default: {} } light->param[p_param] = p_value; @@ -5001,6 +5130,9 @@ void RasterizerStorageGLES3::light_set_reverse_cull_face_mode(RID p_light, bool ERR_FAIL_COND(!light); light->reverse_cull = p_enabled; + + light->version++; + light->instance_change_notify(); } void RasterizerStorageGLES3::light_omni_set_shadow_mode(RID p_light, VS::LightOmniShadowMode p_mode) { @@ -5272,6 +5404,9 @@ void RasterizerStorageGLES3::reflection_probe_set_cull_mask(RID p_probe, uint32_ reflection_probe->instance_change_notify(); } +void RasterizerStorageGLES3::reflection_probe_set_resolution(RID p_probe, int p_resolution) { +} + AABB RasterizerStorageGLES3::reflection_probe_get_aabb(RID p_probe) const { const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); ERR_FAIL_COND_V(!reflection_probe, AABB()); @@ -5970,9 +6105,21 @@ AABB RasterizerStorageGLES3::particles_get_current_aabb(RID p_particles) { const Particles *particles = particles_owner.getornull(p_particles); ERR_FAIL_COND_V(!particles, AABB()); + const float *data; glBindBuffer(GL_ARRAY_BUFFER, particles->particle_buffers[0]); - float *data = (float *)glMapBufferRange(GL_ARRAY_BUFFER, 0, particles->amount * 16 * 6, GL_MAP_READ_BIT); +#if defined(GLES_OVER_GL) || defined(__EMSCRIPTEN__) + PoolVector<uint8_t> vector; + vector.resize(particles->amount * 16 * 6); + { + PoolVector<uint8_t>::Write w = vector.write(); + glGetBufferSubData(GL_ARRAY_BUFFER, 0, particles->amount * 16 * 6, w.ptr()); + } + PoolVector<uint8_t>::Read r = vector.read(); + data = reinterpret_cast<const float *>(r.ptr()); +#else + data = (float *)glMapBufferRange(GL_ARRAY_BUFFER, 0, particles->amount * 16 * 6, GL_MAP_READ_BIT); +#endif AABB aabb; Transform inv = particles->emission_transform.affine_inverse(); @@ -5989,7 +6136,13 @@ AABB RasterizerStorageGLES3::particles_get_current_aabb(RID p_particles) { aabb.expand_to(pos); } +#if defined(GLES_OVER_GL) || defined(__EMSCRIPTEN__) + r = PoolVector<uint8_t>::Read(); + vector = PoolVector<uint8_t>(); +#else glUnmapBuffer(GL_ARRAY_BUFFER); +#endif + glBindBuffer(GL_ARRAY_BUFFER, 0); float longest_axis = 0; @@ -6812,7 +6965,7 @@ void RasterizerStorageGLES3::_render_target_allocate(RenderTarget *rt) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level); glDisable(GL_SCISSOR_TEST); glColorMask(1, 1, 1, 1); - if (rt->buffers.active == false) { + if (!rt->buffers.active) { glDepthMask(GL_TRUE); } @@ -6998,7 +7151,10 @@ RID RasterizerStorageGLES3::canvas_light_shadow_buffer_create(int p_width) { //printf("errnum: %x\n",status); glBindFramebuffer(GL_FRAMEBUFFER, RasterizerStorageGLES3::system_fbo); - ERR_FAIL_COND_V(status != GL_FRAMEBUFFER_COMPLETE, RID()); + if (status != GL_FRAMEBUFFER_COMPLETE) { + memdelete(cls); + ERR_FAIL_COND_V(status != GL_FRAMEBUFFER_COMPLETE, RID()); + } return canvas_light_shadow_owner.make_rid(cls); } @@ -7335,7 +7491,7 @@ bool RasterizerStorageGLES3::free(RID p_rid) { GIProbeData *gi_probe_data = gi_probe_data_owner.get(p_rid); glDeleteTextures(1, &gi_probe_data->tex_id); - gi_probe_owner.free(p_rid); + gi_probe_data_owner.free(p_rid); memdelete(gi_probe_data); } else if (lightmap_capture_data_owner.owns(p_rid)) { @@ -7343,7 +7499,7 @@ bool RasterizerStorageGLES3::free(RID p_rid) { LightmapCapture *lightmap_capture = lightmap_capture_data_owner.get(p_rid); lightmap_capture->instance_remove_deps(); - gi_probe_owner.free(p_rid); + lightmap_capture_data_owner.free(p_rid); memdelete(lightmap_capture); } else if (canvas_occluder_owner.owns(p_rid)) { @@ -7652,6 +7808,8 @@ void RasterizerStorageGLES3::initialize() { { //transform feedback buffers uint32_t xf_feedback_size = GLOBAL_DEF_RST("rendering/limits/buffers/blend_shape_max_buffer_size_kb", 4096); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/buffers/blend_shape_max_buffer_size_kb", PropertyInfo(Variant::INT, "rendering/limits/buffers/blend_shape_max_buffer_size_kb", PROPERTY_HINT_RANGE, "0,8192,1,or_greater")); + for (int i = 0; i < 2; i++) { glGenBuffers(1, &resources.transform_feedback_buffers[i]); diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h index b74dd77e26..773b149e0b 100644 --- a/drivers/gles3/rasterizer_storage_gles3.h +++ b/drivers/gles3/rasterizer_storage_gles3.h @@ -31,17 +31,24 @@ #ifndef RASTERIZERSTORAGEGLES3_H #define RASTERIZERSTORAGEGLES3_H -#include "self_list.h" +#include "core/self_list.h" #include "servers/visual/rasterizer.h" #include "servers/visual/shader_language.h" #include "shader_compiler_gles3.h" #include "shader_gles3.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" +// WebGL 2.0 has no MapBufferRange/UnmapBuffer, but offers a non-ES style BufferSubData API instead. +#ifdef __EMSCRIPTEN__ +void glGetBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, GLvoid *data); +void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data); +#endif + class RasterizerCanvasGLES3; class RasterizerSceneGLES3; @@ -442,6 +449,7 @@ public: }; int light_mode; + bool uses_screen_texture; bool uses_screen_uv; bool uses_time; @@ -582,6 +590,7 @@ public: virtual void material_set_param(RID p_material, const StringName &p_param, const Variant &p_value); virtual Variant material_get_param(RID p_material, const StringName &p_param) const; + virtual Variant material_get_param_default(RID p_material, const StringName &p_param) const; virtual void material_set_line_width(RID p_material, float p_width); virtual void material_set_next_pass(RID p_material, RID p_next_material); @@ -689,7 +698,7 @@ public: } }; - class MultiMesh; + struct MultiMesh; struct Mesh : public GeometryOwner { @@ -1003,6 +1012,7 @@ public: virtual void reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable); virtual void reflection_probe_set_enable_shadows(RID p_probe, bool p_enable); virtual void reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers); + virtual void reflection_probe_set_resolution(RID p_probe, int p_resolution); virtual AABB reflection_probe_get_aabb(RID p_probe) const; virtual VS::ReflectionProbeUpdateMode reflection_probe_get_update_mode(RID p_probe) const; diff --git a/drivers/gles3/shader_compiler_gles3.cpp b/drivers/gles3/shader_compiler_gles3.cpp index a78a392cbd..be36b41417 100644 --- a/drivers/gles3/shader_compiler_gles3.cpp +++ b/drivers/gles3/shader_compiler_gles3.cpp @@ -30,7 +30,8 @@ #include "shader_compiler_gles3.h" -#include "os/os.h" +#include "core/os/os.h" +#include "core/project_settings.h" #define SL ShaderLanguage @@ -78,6 +79,12 @@ static int _get_datatype_size(SL::DataType p_type) { case SL::TYPE_SAMPLER2D: return 16; case SL::TYPE_ISAMPLER2D: return 16; case SL::TYPE_USAMPLER2D: return 16; + case SL::TYPE_SAMPLER2DARRAY: return 16; + case SL::TYPE_ISAMPLER2DARRAY: return 16; + case SL::TYPE_USAMPLER2DARRAY: return 16; + case SL::TYPE_SAMPLER3D: return 16; + case SL::TYPE_ISAMPLER3D: return 16; + case SL::TYPE_USAMPLER3D: return 16; case SL::TYPE_SAMPLERCUBE: return 16; } @@ -111,6 +118,12 @@ static int _get_datatype_alignment(SL::DataType p_type) { case SL::TYPE_SAMPLER2D: return 16; case SL::TYPE_ISAMPLER2D: return 16; case SL::TYPE_USAMPLER2D: return 16; + case SL::TYPE_SAMPLER2DARRAY: return 16; + case SL::TYPE_ISAMPLER2DARRAY: return 16; + case SL::TYPE_USAMPLER2DARRAY: return 16; + case SL::TYPE_SAMPLER3D: return 16; + case SL::TYPE_ISAMPLER3D: return 16; + case SL::TYPE_USAMPLER3D: return 16; case SL::TYPE_SAMPLERCUBE: return 16; } @@ -859,7 +872,9 @@ ShaderCompilerGLES3::ShaderCompilerGLES3() { actions[VS::SHADER_SPATIAL].renames["SCREEN_UV"] = "screen_uv"; actions[VS::SHADER_SPATIAL].renames["SCREEN_TEXTURE"] = "screen_texture"; actions[VS::SHADER_SPATIAL].renames["DEPTH_TEXTURE"] = "depth_buffer"; + actions[VS::SHADER_SPATIAL].renames["DEPTH"] = "gl_FragDepth"; actions[VS::SHADER_SPATIAL].renames["ALPHA_SCISSOR"] = "alpha_scissor"; + actions[VS::SHADER_SPATIAL].renames["OUTPUT_IS_SRGB"] = "SHADER_IS_SRGB"; //for light actions[VS::SHADER_SPATIAL].renames["VIEW"] = "view"; @@ -901,12 +916,24 @@ ShaderCompilerGLES3::ShaderCompilerGLES3() { actions[VS::SHADER_SPATIAL].render_mode_defines["cull_front"] = "#define DO_SIDE_CHECK\n"; actions[VS::SHADER_SPATIAL].render_mode_defines["cull_disabled"] = "#define DO_SIDE_CHECK\n"; - actions[VS::SHADER_SPATIAL].render_mode_defines["diffuse_burley"] = "#define DIFFUSE_BURLEY\n"; + bool force_lambert = GLOBAL_GET("rendering/quality/shading/force_lambert_over_burley"); + + if (!force_lambert) { + actions[VS::SHADER_SPATIAL].render_mode_defines["diffuse_burley"] = "#define DIFFUSE_BURLEY\n"; + } + actions[VS::SHADER_SPATIAL].render_mode_defines["diffuse_oren_nayar"] = "#define DIFFUSE_OREN_NAYAR\n"; actions[VS::SHADER_SPATIAL].render_mode_defines["diffuse_lambert_wrap"] = "#define DIFFUSE_LAMBERT_WRAP\n"; actions[VS::SHADER_SPATIAL].render_mode_defines["diffuse_toon"] = "#define DIFFUSE_TOON\n"; - actions[VS::SHADER_SPATIAL].render_mode_defines["specular_schlick_ggx"] = "#define SPECULAR_SCHLICK_GGX\n"; + bool force_blinn = GLOBAL_GET("rendering/quality/shading/force_blinn_over_ggx"); + + if (!force_blinn) { + actions[VS::SHADER_SPATIAL].render_mode_defines["specular_schlick_ggx"] = "#define SPECULAR_SCHLICK_GGX\n"; + } else { + actions[VS::SHADER_SPATIAL].render_mode_defines["specular_schlick_ggx"] = "#define SPECULAR_BLINN\n"; + } + actions[VS::SHADER_SPATIAL].render_mode_defines["specular_blinn"] = "#define SPECULAR_BLINN\n"; actions[VS::SHADER_SPATIAL].render_mode_defines["specular_phong"] = "#define SPECULAR_PHONG\n"; actions[VS::SHADER_SPATIAL].render_mode_defines["specular_toon"] = "#define SPECULAR_TOON\n"; diff --git a/drivers/gles3/shader_compiler_gles3.h b/drivers/gles3/shader_compiler_gles3.h index 7a32057741..1f903b8935 100644 --- a/drivers/gles3/shader_compiler_gles3.h +++ b/drivers/gles3/shader_compiler_gles3.h @@ -31,7 +31,7 @@ #ifndef SHADERCOMPILERGLES3_H #define SHADERCOMPILERGLES3_H -#include "pair.h" +#include "core/pair.h" #include "servers/visual/shader_language.h" #include "servers/visual/shader_types.h" #include "servers/visual_server.h" diff --git a/drivers/gles3/shader_gles3.cpp b/drivers/gles3/shader_gles3.cpp index 007600bb42..799179e8d4 100644 --- a/drivers/gles3/shader_gles3.cpp +++ b/drivers/gles3/shader_gles3.cpp @@ -30,7 +30,7 @@ #include "shader_gles3.h" -#include "print_string.h" +#include "core/print_string.h" //#define DEBUG_OPENGL @@ -122,6 +122,11 @@ bool ShaderGLES3::bind() { ERR_FAIL_COND_V(!version, false); + if (!version->ok) { //broken, unable to bind (do not throw error, you saw it before already when it failed compilation). + glUseProgram(0); + return false; + } + glUseProgram(version->id); DEBUG_TEST_ERROR("Use Program"); diff --git a/drivers/gles3/shader_gles3.h b/drivers/gles3/shader_gles3.h index 996655615e..0d360e8453 100644 --- a/drivers/gles3/shader_gles3.h +++ b/drivers/gles3/shader_gles3.h @@ -31,7 +31,10 @@ #ifndef SHADER_GLES3_H #define SHADER_GLES3_H -#include <stdio.h> +#include "core/hash_map.h" +#include "core/map.h" +#include "core/math/camera_matrix.h" +#include "core/variant.h" #include "platform_config.h" #ifndef GLES3_INCLUDE_H @@ -40,10 +43,7 @@ #include GLES3_INCLUDE_H #endif -#include "camera_matrix.h" -#include "hash_map.h" -#include "map.h" -#include "variant.h" +#include <stdio.h> /** @author Juan Linietsky <reduzio@gmail.com> @@ -128,11 +128,13 @@ private: Vector<GLint> texture_uniform_locations; uint32_t code_version; bool ok; - Version() { - code_version = 0; - ok = false; - uniform_location = NULL; - } + Version() : + id(0), + vert_id(0), + frag_id(0), + uniform_location(NULL), + code_version(0), + ok(false) {} }; Version *version; @@ -336,6 +338,7 @@ public: } uint32_t get_version() const { return new_conditional_version.version; } + _FORCE_INLINE_ bool is_version_valid() const { return version && version->ok; } void set_uniform_camera(int p_idx, const CameraMatrix &p_mat) { diff --git a/drivers/gles3/shaders/SCsub b/drivers/gles3/shaders/SCsub index f1811fa7b5..27fd1514e7 100644 --- a/drivers/gles3/shaders/SCsub +++ b/drivers/gles3/shaders/SCsub @@ -20,3 +20,4 @@ if 'GLES3_GLSL' in env['BUILDERS']: env.GLES3_GLSL('exposure.glsl'); env.GLES3_GLSL('tonemap.glsl'); env.GLES3_GLSL('particles.glsl'); + env.GLES3_GLSL('lens_distorted.glsl'); diff --git a/drivers/gles3/shaders/blend_shape.glsl b/drivers/gles3/shaders/blend_shape.glsl index d019062ba0..a1e954e33d 100644 --- a/drivers/gles3/shaders/blend_shape.glsl +++ b/drivers/gles3/shaders/blend_shape.glsl @@ -1,3 +1,4 @@ +/* clang-format off */ [vertex] /* @@ -23,6 +24,7 @@ ARRAY_INDEX=8, /* INPUT ATTRIBS */ layout(location = 0) in highp VFORMAT vertex_attrib; +/* clang-format on */ layout(location = 1) in vec3 normal_attrib; #ifdef ENABLE_TANGENT @@ -183,8 +185,10 @@ void main() { gl_Position = vec4(0.0); } +/* clang-format off */ [fragment] void main() { } +/* clang-format on */ diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl index 9e99305fe7..52746e0862 100644 --- a/drivers/gles3/shaders/canvas.glsl +++ b/drivers/gles3/shaders/canvas.glsl @@ -1,6 +1,8 @@ +/* clang-format off */ [vertex] layout(location = 0) in highp vec2 vertex; +/* clang-format on */ layout(location = 3) in vec4 color_attrib; #ifdef USE_SKELETON @@ -90,34 +92,35 @@ const bool at_light_pass = true; const bool at_light_pass = false; #endif -#ifdef USE_PARTICLES -uniform int h_frames; -uniform int v_frames; -#endif - #if defined(USE_MATERIAL) +/* clang-format off */ layout(std140) uniform UniformData { //ubo:2 MATERIAL_UNIFORMS }; +/* clang-format on */ #endif +/* clang-format off */ + VERTEX_SHADER_GLOBALS +/* clang-format on */ + void main() { vec4 color = color_attrib; #ifdef USE_INSTANCING - mat4 extra_matrix2 = extra_matrix * transpose(mat4(instance_xform0, instance_xform1, instance_xform2, vec4(0.0, 0.0, 0.0, 1.0))); + mat4 extra_matrix_instance = extra_matrix * transpose(mat4(instance_xform0, instance_xform1, instance_xform2, vec4(0.0, 0.0, 0.0, 1.0))); color *= instance_color; vec4 instance_custom = instance_custom_data; #else - mat4 extra_matrix2 = extra_matrix; + mat4 extra_matrix_instance = extra_matrix; vec4 instance_custom = vec4(0.0); #endif @@ -135,27 +138,15 @@ void main() { highp vec4 outvec = vec4(vertex, 0.0, 1.0); #endif -#ifdef USE_PARTICLES - //scale by texture size - outvec.xy /= color_texpixel_size; - - //compute h and v frames and adjust UV interp for animation - int total_frames = h_frames * v_frames; - int frame = min(int(float(total_frames) * instance_custom.z), total_frames - 1); - float frame_w = 1.0 / float(h_frames); - float frame_h = 1.0 / float(v_frames); - uv_interp.x = uv_interp.x * frame_w + frame_w * float(frame % h_frames); - uv_interp.y = uv_interp.y * frame_h + frame_h * float(frame / h_frames); +#define extra_matrix extra_matrix_instance -#endif - -#define extra_matrix extra_matrix2 - -{ + { + /* clang-format off */ VERTEX_SHADER_CODE -} + /* clang-format on */ + } #ifdef USE_NINEPATCH @@ -172,7 +163,6 @@ VERTEX_SHADER_CODE color_interp = color; #ifdef USE_PIXEL_SNAP - outvec.xy = floor(outvec + 0.5).xy; #endif @@ -188,29 +178,29 @@ VERTEX_SHADER_CODE highp mat2x4 m; m = mat2x4( texelFetch(skeleton_texture, tex_ofs, 0), - texelFetch(skeleton_texture, tex_ofs + ivec2(0, 1), 0)) - * bone_weights.x; + texelFetch(skeleton_texture, tex_ofs + ivec2(0, 1), 0)) * + bone_weights.x; tex_ofs = ivec2(bone_indicesi.y % 256, (bone_indicesi.y / 256) * 2); m += mat2x4( - texelFetch(skeleton_texture, tex_ofs, 0), - texelFetch(skeleton_texture, tex_ofs + ivec2(0, 1), 0)) - * bone_weights.y; + texelFetch(skeleton_texture, tex_ofs, 0), + texelFetch(skeleton_texture, tex_ofs + ivec2(0, 1), 0)) * + bone_weights.y; tex_ofs = ivec2(bone_indicesi.z % 256, (bone_indicesi.z / 256) * 2); m += mat2x4( - texelFetch(skeleton_texture, tex_ofs, 0), - texelFetch(skeleton_texture, tex_ofs + ivec2(0, 1), 0)) - * bone_weights.z; + texelFetch(skeleton_texture, tex_ofs, 0), + texelFetch(skeleton_texture, tex_ofs + ivec2(0, 1), 0)) * + bone_weights.z; tex_ofs = ivec2(bone_indicesi.w % 256, (bone_indicesi.w / 256) * 2); m += mat2x4( - texelFetch(skeleton_texture, tex_ofs, 0), - texelFetch(skeleton_texture, tex_ofs + ivec2(0, 1), 0)) - * bone_weights.w; + texelFetch(skeleton_texture, tex_ofs, 0), + texelFetch(skeleton_texture, tex_ofs + ivec2(0, 1), 0)) * + bone_weights.w; mat4 bone_matrix = skeleton_transform * transpose(mat4(m[0], m[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))) * skeleton_transform_inverse; @@ -236,8 +226,8 @@ VERTEX_SHADER_CODE pos = outvec.xy; #endif - local_rot.xy = normalize((modelview_matrix * (extra_matrix * vec4(1.0, 0.0, 0.0, 0.0))).xy); - local_rot.zw = normalize((modelview_matrix * (extra_matrix * vec4(0.0, 1.0, 0.0, 0.0))).xy); + local_rot.xy = normalize((modelview_matrix * (extra_matrix_instance * vec4(1.0, 0.0, 0.0, 0.0))).xy); + local_rot.zw = normalize((modelview_matrix * (extra_matrix_instance * vec4(0.0, 1.0, 0.0, 0.0))).xy); #ifdef USE_TEXTURE_RECT local_rot.xy *= sign(src_rect.z); local_rot.zw *= sign(src_rect.w); @@ -246,9 +236,11 @@ VERTEX_SHADER_CODE #endif } +/* clang-format off */ [fragment] uniform mediump sampler2D color_texture; // texunit:0 +/* clang-format on */ uniform highp vec2 color_texpixel_size; uniform mediump sampler2D normal_texture; // texunit:1 @@ -313,16 +305,22 @@ layout(location = 0) out mediump vec4 frag_color; #if defined(USE_MATERIAL) +/* clang-format off */ layout(std140) uniform UniformData { MATERIAL_UNIFORMS }; +/* clang-format on */ #endif +/* clang-format off */ + FRAGMENT_SHADER_GLOBALS +/* clang-format on */ + void light_compute( inout vec4 light, inout vec2 light_vec, @@ -339,8 +337,12 @@ void light_compute( #if defined(USE_LIGHT_SHADER_CODE) + /* clang-format off */ + LIGHT_SHADER_CODE + /* clang-format on */ + #endif } @@ -472,8 +474,12 @@ void main() { vec3 normal_map = vec3(0.0, 0.0, 1.0); #endif + /* clang-format off */ + FRAGMENT_SHADER_CODE + /* clang-format on */ + #if defined(NORMALMAP_USED) normal = mix(vec3(0.0, 0.0, 1.0), normal_map * vec3(2.0, -2.0, 1.0) - vec3(1.0, -1.0, 0.0), normal_depth); #endif diff --git a/drivers/gles3/shaders/canvas_shadow.glsl b/drivers/gles3/shaders/canvas_shadow.glsl index b06e9076d9..68d0713385 100644 --- a/drivers/gles3/shaders/canvas_shadow.glsl +++ b/drivers/gles3/shaders/canvas_shadow.glsl @@ -1,6 +1,8 @@ +/* clang-format off */ [vertex] uniform highp mat4 projection_matrix; +/* clang-format on */ uniform highp mat4 light_matrix; uniform highp mat4 world_matrix; uniform highp float distance_norm; @@ -15,9 +17,11 @@ void main() { position_interp = gl_Position; } +/* clang-format off */ [fragment] in highp vec4 position_interp; +/* clang-format on */ #ifdef USE_RGBA_SHADOWS layout(location = 0) out lowp vec4 distance_buf; diff --git a/drivers/gles3/shaders/copy.glsl b/drivers/gles3/shaders/copy.glsl index e17b71df27..a5637537d2 100644 --- a/drivers/gles3/shaders/copy.glsl +++ b/drivers/gles3/shaders/copy.glsl @@ -1,6 +1,8 @@ +/* clang-format off */ [vertex] layout(location = 0) in highp vec4 vertex_attrib; +/* clang-format on */ #if defined(USE_CUBEMAP) || defined(USE_PANORAMA) layout(location = 4) in vec3 cube_in; #else @@ -45,6 +47,7 @@ void main() { #endif } +/* clang-format off */ [fragment] #define M_PI 3.14159265359 @@ -58,6 +61,7 @@ in vec3 cube_interp; #else in vec2 uv_interp; #endif +/* clang-format on */ #ifdef USE_ASYM_PANO uniform highp mat4 pano_transform; diff --git a/drivers/gles3/shaders/cube_to_dp.glsl b/drivers/gles3/shaders/cube_to_dp.glsl index 2911746bb7..2b74f054f9 100644 --- a/drivers/gles3/shaders/cube_to_dp.glsl +++ b/drivers/gles3/shaders/cube_to_dp.glsl @@ -1,6 +1,8 @@ +/* clang-format off */ [vertex] layout(location = 0) in highp vec4 vertex_attrib; +/* clang-format on */ layout(location = 4) in vec2 uv_in; out vec2 uv_interp; @@ -11,9 +13,11 @@ void main() { gl_Position = vertex_attrib; } +/* clang-format off */ [fragment] uniform highp samplerCube source_cube; //texunit:0 +/* clang-format on */ in vec2 uv_interp; uniform bool z_flip; diff --git a/drivers/gles3/shaders/cubemap_filter.glsl b/drivers/gles3/shaders/cubemap_filter.glsl index 7f2dc5057c..f65f798ff0 100644 --- a/drivers/gles3/shaders/cubemap_filter.glsl +++ b/drivers/gles3/shaders/cubemap_filter.glsl @@ -1,6 +1,8 @@ +/* clang-format off */ [vertex] layout(location = 0) in highp vec2 vertex; +/* clang-format on */ layout(location = 4) in highp vec2 uv; @@ -12,9 +14,11 @@ void main() { gl_Position = vec4(vertex, 0, 1); } +/* clang-format off */ [fragment] precision highp float; +/* clang-format on */ precision highp int; #ifdef USE_SOURCE_PANORAMA diff --git a/drivers/gles3/shaders/effect_blur.glsl b/drivers/gles3/shaders/effect_blur.glsl index 3872ee8d1d..b67d06bc10 100644 --- a/drivers/gles3/shaders/effect_blur.glsl +++ b/drivers/gles3/shaders/effect_blur.glsl @@ -1,6 +1,8 @@ +/* clang-format off */ [vertex] layout(location = 0) in highp vec4 vertex_attrib; +/* clang-format on */ layout(location = 4) in vec2 uv_in; out vec2 uv_interp; @@ -22,11 +24,13 @@ void main() { #endif } +/* clang-format off */ [fragment] #if !defined(GLES_OVER_GL) precision mediump float; #endif +/* clang-format on */ in vec2 uv_interp; uniform sampler2D source_color; //texunit:0 diff --git a/drivers/gles3/shaders/exposure.glsl b/drivers/gles3/shaders/exposure.glsl index 18fff1ae36..759adcda06 100644 --- a/drivers/gles3/shaders/exposure.glsl +++ b/drivers/gles3/shaders/exposure.glsl @@ -1,15 +1,19 @@ +/* clang-format off */ [vertex] layout(location = 0) in highp vec4 vertex_attrib; +/* clang-format on */ void main() { gl_Position = vertex_attrib; } +/* clang-format off */ [fragment] uniform highp sampler2D source_exposure; //texunit:0 +/* clang-format on */ #ifdef EXPOSURE_BEGIN diff --git a/drivers/gles3/shaders/lens_distorted.glsl b/drivers/gles3/shaders/lens_distorted.glsl new file mode 100644 index 0000000000..7b9d0b347f --- /dev/null +++ b/drivers/gles3/shaders/lens_distorted.glsl @@ -0,0 +1,64 @@ +/* clang-format off */ +[vertex] + +layout(location = 0) in highp vec4 vertex_attrib; +/* clang-format on */ + +uniform vec2 offset; +uniform vec2 scale; + +out vec2 uv_interp; + +void main() { + + uv_interp = vertex_attrib.xy * 2.0 - 1.0; + + vec2 v = vertex_attrib.xy * scale + offset; + gl_Position = vec4(v, 0.0, 1.0); +} + +/* clang-format off */ +[fragment] + +uniform sampler2D source; //texunit:0 +/* clang-format on */ + +uniform vec2 eye_center; +uniform float k1; +uniform float k2; +uniform float upscale; +uniform float aspect_ratio; + +in vec2 uv_interp; + +layout(location = 0) out vec4 frag_color; + +void main() { + vec2 coords = uv_interp; + vec2 offset = coords - eye_center; + + // take aspect ratio into account + offset.y /= aspect_ratio; + + // distort + vec2 offset_sq = offset * offset; + float radius_sq = offset_sq.x + offset_sq.y; + float radius_s4 = radius_sq * radius_sq; + float distortion_scale = 1.0 + (k1 * radius_sq) + (k2 * radius_s4); + offset *= distortion_scale; + + // reapply aspect ratio + offset.y *= aspect_ratio; + + // add our eye center back in + coords = offset + eye_center; + coords /= upscale; + + // and check our color + if (coords.x < -1.0 || coords.y < -1.0 || coords.x > 1.0 || coords.y > 1.0) { + frag_color = vec4(0.0, 0.0, 0.0, 1.0); + } else { + coords = (coords + vec2(1.0)) / vec2(2.0); + frag_color = textureLod(source, coords, 0.0); + } +} diff --git a/drivers/gles3/shaders/particles.glsl b/drivers/gles3/shaders/particles.glsl index 56e5545efe..8523c08597 100644 --- a/drivers/gles3/shaders/particles.glsl +++ b/drivers/gles3/shaders/particles.glsl @@ -1,6 +1,8 @@ +/* clang-format off */ [vertex] layout(location = 0) in highp vec4 color; +/* clang-format on */ layout(location = 1) in highp vec4 velocity_active; layout(location = 2) in highp vec4 custom; layout(location = 3) in highp vec4 xform_1; @@ -45,16 +47,22 @@ out highp vec4 out_xform_3; //tfb: #if defined(USE_MATERIAL) +/* clang-format off */ layout(std140) uniform UniformData { //ubo:0 MATERIAL_UNIFORMS }; +/* clang-format on */ #endif +/* clang-format off */ + VERTEX_SHADER_GLOBALS +/* clang-format on */ + uint hash(uint x) { x = ((x >> uint(16)) ^ x) * uint(0x45d9f3b); @@ -165,7 +173,11 @@ void main() { //execute shader { + /* clang-format off */ + VERTEX_SHADER_CODE + + /* clang-format on */ } #if !defined(DISABLE_FORCE) @@ -221,6 +233,7 @@ VERTEX_SHADER_CODE #endif //PARTICLES_COPY } +/* clang-format off */ [fragment] // any code here is never executed, stuff is filled just so it works @@ -240,10 +253,15 @@ FRAGMENT_SHADER_GLOBALS void main() { { + LIGHT_SHADER_CODE + } { + FRAGMENT_SHADER_CODE + } } +/* clang-format on */ diff --git a/drivers/gles3/shaders/resolve.glsl b/drivers/gles3/shaders/resolve.glsl index d860fa544f..d64d8308c1 100644 --- a/drivers/gles3/shaders/resolve.glsl +++ b/drivers/gles3/shaders/resolve.glsl @@ -1,6 +1,8 @@ +/* clang-format off */ [vertex] layout(location = 0) in highp vec4 vertex_attrib; +/* clang-format on */ layout(location = 4) in vec2 uv_in; out vec2 uv_interp; @@ -11,11 +13,13 @@ void main() { gl_Position = vertex_attrib; } +/* clang-format off */ [fragment] #if !defined(GLES_OVER_GL) precision mediump float; #endif +/* clang-format on */ in vec2 uv_interp; uniform sampler2D source_specular; // texunit:0 diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index cacce93dc5..91ab34f775 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -1,7 +1,10 @@ +/* clang-format off */ [vertex] #define M_PI 3.14159265359 +#define SHADER_IS_SRGB false + /* from VisualServer: @@ -21,6 +24,7 @@ ARRAY_INDEX=8, /* INPUT ATTRIBS */ layout(location = 0) in highp vec4 vertex_attrib; +/* clang-format on */ 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; @@ -226,16 +230,22 @@ out vec3 binormal_interp; #if defined(USE_MATERIAL) +/* clang-format off */ layout(std140) uniform UniformData { // ubo:1 MATERIAL_UNIFORMS }; +/* clang-format on */ #endif +/* clang-format off */ + VERTEX_SHADER_GLOBALS +/* clang-format on */ + #ifdef RENDER_DEPTH_DUAL_PARABOLOID out highp float dp_clip; @@ -340,32 +350,32 @@ void main() { 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; + texelFetch(skeleton_texture, tex_ofs + ivec2(0, 2), 0)) * + bone_weights.x; tex_ofs = ivec2(bone_indicesi.y % 256, (bone_indicesi.y / 256) * 3); 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; + 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; tex_ofs = ivec2(bone_indicesi.z % 256, (bone_indicesi.z / 256) * 3); 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; + 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; tex_ofs = ivec2(bone_indicesi.w % 256, (bone_indicesi.w / 256) * 3); 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; + 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; mat4 bone_matrix = transpose(mat4(m[0], m[1], m[2], vec4(0.0, 0.0, 0.0, 1.0))); @@ -374,11 +384,13 @@ void main() { #endif mat4 modelview = camera_inverse_matrix * world_matrix; -{ + { + /* clang-format off */ VERTEX_SHADER_CODE -} + /* clang-format on */ + } // using local coordinates (default) #if !defined(SKIP_TRANSFORM_USED) && !defined(VERTEX_WORLD_COORDS_USED) @@ -501,8 +513,10 @@ VERTEX_SHADER_CODE #endif // USE_VERTEX_LIGHTING } +/* clang-format off */ [fragment] + /* texture unit usage, N is max_texture_unity-N 1-skeleton @@ -519,8 +533,10 @@ VERTEX_SHADER_CODE */ uniform highp mat4 world_transform; +/* clang-format on */ #define M_PI 3.14159265359 +#define SHADER_IS_SRGB false /* Varyings */ @@ -552,7 +568,6 @@ layout(std140) uniform Radiance { // ubo:2 mat4 radiance_inverse_xform; float radiance_ambient_contribution; - }; #define RADIANCE_MAX_LOD 5.0 @@ -608,16 +623,22 @@ vec3 textureDualParaboloid(sampler2D p_tex, vec3 p_vec, float p_roughness) { #if defined(USE_MATERIAL) +/* clang-format off */ layout(std140) uniform UniformData { MATERIAL_UNIFORMS }; +/* clang-format on */ #endif +/* clang-format off */ + FRAGMENT_SHADER_GLOBALS +/* clang-format on */ + layout(std140) uniform SceneData { highp mat4 projection_matrix; @@ -663,7 +684,7 @@ layout(std140) uniform SceneData { highp float fog_height_curve; }; -//directional light data + //directional light data #ifdef USE_LIGHT_DIRECTIONAL @@ -701,7 +722,6 @@ struct LightData { mediump vec4 light_clamp; mediump vec4 shadow_color_contact; highp mat4 shadow_matrix; - }; layout(std140) uniform OmniLightData { // ubo:4 @@ -900,13 +920,14 @@ float GTR1(float NdotH, float a) { return (a2 - 1.0) / (M_PI * log(a2) * t); } -vec3 metallic_to_specular_color(float metallic, float specular, vec3 albedo) { - float dielectric = (0.034 * 2.0) * specular; - // energy conservation - return mix(vec3(dielectric), albedo, metallic); // TODO: reference? +vec3 F0(float metallic, float specular, vec3 albedo) { + float dielectric = 0.16 * specular * specular; + // use albedo * metallic as colored specular reflectance at 0 angle for metallic materials; + // see https://google.github.io/filament/Filament.md.html + return mix(vec3(dielectric), albedo, vec3(metallic)); } -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) { +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 specular, 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 @@ -916,8 +937,12 @@ void light_compute(vec3 N, vec3 L, vec3 V, vec3 B, vec3 T, vec3 light_color, vec vec3 light = L; vec3 view = V; + /* clang-format off */ + LIGHT_SHADER_CODE + /* clang-format on */ + #else float NdotL = dot(N, L); float cNdotL = max(NdotL, 0.0); // clamped NdotL @@ -1000,16 +1025,27 @@ LIGHT_SHADER_CODE #if defined(SPECULAR_BLINN) + //normalized blinn vec3 H = normalize(V + L); float cNdotH = max(dot(N, H), 0.0); - float intensity = pow(cNdotH, (1.0 - roughness) * 256.0); + float cVdotH = max(dot(V, H), 0.0); + float cLdotH = max(dot(L, H), 0.0); + float shininess = exp2(15.0 * (1.0 - roughness) + 1.0) * 0.25; + float blinn = pow(cNdotH, shininess); + blinn *= (shininess + 8.0) / (8.0 * 3.141592654); + float intensity = (blinn) / max(4.0 * cNdotV * cNdotL, 0.75); + 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); + float shininess = exp2(15.0 * (1.0 - roughness) + 1.0) * 0.25; + float phong = pow(cRdotV, shininess); + phong *= (shininess + 8.0) / (8.0 * 3.141592654); + float intensity = (phong) / max(4.0 * cNdotV * cNdotL, 0.75); + specular_light += light_color * intensity * specular_blob_intensity * attenuation; #elif defined(SPECULAR_TOON) @@ -1034,11 +1070,10 @@ LIGHT_SHADER_CODE #if defined(LIGHT_USE_ANISOTROPY) + float alpha = roughness * roughness; 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 ax = alpha / aspect; + float ay = alpha * aspect; float XdotH = dot(T, H); float YdotH = dot(B, H); float D = D_GGX_anisotropic(cNdotH, ax, ay, XdotH, YdotH); @@ -1050,11 +1085,11 @@ LIGHT_SHADER_CODE float G = G_GGX_2cos(cNdotL, alpha) * G_GGX_2cos(cNdotV, alpha); #endif // F - float F0 = 1.0; // FIXME + vec3 f0 = F0(metallic, specular, diffuse_color); float cLdotH5 = SchlickFresnel(cLdotH); - float F = mix(cLdotH5, 1.0, F0); + vec3 F = mix(vec3(cLdotH5), vec3(1.0), f0); - float specular_brdf_NL = cNdotL * D * F * G; + vec3 specular_brdf_NL = cNdotL * D * F * G; specular_light += specular_brdf_NL * light_color * specular_blob_intensity * attenuation; #endif @@ -1101,8 +1136,9 @@ float sample_shadow(highp sampler2DShadow shadow, vec2 shadow_pixel_size, vec2 p 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); +#endif -#elif defined(SHADOW_MODE_PCF_5) +#ifdef 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)); @@ -1111,7 +1147,9 @@ float sample_shadow(highp sampler2DShadow shadow, vec2 shadow_pixel_size, vec2 p avg += textureProj(shadow, vec4(pos + vec2(0.0, -shadow_pixel_size.y), depth, 1.0)); return avg * (1.0 / 5.0); -#else +#endif + +#if !defined(SHADOW_MODE_PCF_5) || !defined(SHADOW_MODE_PCF_13) return textureProj(shadow, vec4(pos, depth, 1.0)); @@ -1153,7 +1191,7 @@ vec3 light_transmittance(float translucency,vec3 light_vec, vec3 normal, vec3 po } #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) { +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 specular, 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); @@ -1207,10 +1245,10 @@ void light_process_omni(int idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 bi light_attenuation *= mix(omni_lights[idx].shadow_color_contact.rgb, vec3(1.0), shadow); } #endif //SHADOWS_DISABLED - 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 * omni_attenuation, rim_tint, clearcoat, clearcoat_gloss, anisotropy, diffuse_light, specular_light); + 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, specular, rim * omni_attenuation, 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) { +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 specular, 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); @@ -1242,7 +1280,7 @@ void light_process_spot(int idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 bi } #endif //SHADOWS_DISABLED - 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 * spot_attenuation, rim_tint, clearcoat, clearcoat_gloss, anisotropy, diffuse_light, specular_light); + 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, specular, rim * spot_attenuation, 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) { @@ -1302,7 +1340,7 @@ void reflection_process(int idx, vec3 vertex, vec3 normal, vec3 binormal, vec3 t reflection_accum += reflection; } -#ifndef USE_LIGHTMAP +#if !defined(USE_LIGHTMAP) && !defined(USE_LIGHTMAP_CAPTURE) if (reflections[idx].ambient.a > 0.0) { //compute ambient using skybox vec3 local_amb_vec = (reflections[idx].local_matrix * vec4(normal, 0.0)).xyz; @@ -1599,11 +1637,13 @@ void main() { float sss_strength = 0.0; #endif -{ + { + /* clang-format off */ FRAGMENT_SHADER_CODE -} + /* clang-format on */ + } #if defined(ALPHA_SCISSOR_USED) if (alpha < alpha_scissor) { @@ -1855,7 +1895,7 @@ FRAGMENT_SHADER_CODE 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); + 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, specular, rim, rim_tint, clearcoat, clearcoat_gloss, anisotropy, diffuse_light, specular_light); #endif #endif //#USE_LIGHT_DIRECTIONAL @@ -1917,7 +1957,7 @@ FRAGMENT_SHADER_CODE } else { specular_light += env_reflection_light; } -#ifndef USE_LIGHTMAP +#if !defined(USE_LIGHTMAP) && !defined(USE_LIGHTMAP_CAPTURE) if (ambient_accum.a > 0.0) { ambient_light = ambient_accum.rgb / ambient_accum.a; } @@ -1929,11 +1969,11 @@ FRAGMENT_SHADER_CODE #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); + light_process_omni(omni_light_indices[i], vertex, eye_vec, normal, binormal, tangent, albedo, transmission, roughness, metallic, specular, rim, rim_tint, clearcoat, clearcoat_gloss, anisotropy, specular_blob_intensity, diffuse_light, specular_light); } 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); + light_process_spot(spot_light_indices[i], vertex, eye_vec, normal, binormal, tangent, albedo, transmission, roughness, metallic, specular, rim, rim_tint, clearcoat, clearcoat_gloss, anisotropy, specular_blob_intensity, diffuse_light, specular_light); } #endif //USE_VERTEX_LIGHTING @@ -1954,7 +1994,7 @@ FRAGMENT_SHADER_CODE diffuse_light *= ao_light_affect; #endif - //energy conservation + // base color remapping 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; @@ -1971,10 +2011,10 @@ FRAGMENT_SHADER_CODE vec4 r = roughness * c0 + c1; float ndotv = clamp(dot(normal, eye_vec), 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; + vec2 env = 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; + vec3 f0 = F0(metallic, specular, albedo); + specular_light *= env.x * f0 + env.y; #endif } @@ -2013,7 +2053,7 @@ FRAGMENT_SHADER_CODE emission = emission * rev_amount + fog_color * fog_amount; ambient_light *= rev_amount; - specular_light *rev_amount; + specular_light *= rev_amount; diffuse_light *= rev_amount; } diff --git a/drivers/gles3/shaders/screen_space_reflection.glsl b/drivers/gles3/shaders/screen_space_reflection.glsl index 73b1ddbb0e..86546319a0 100644 --- a/drivers/gles3/shaders/screen_space_reflection.glsl +++ b/drivers/gles3/shaders/screen_space_reflection.glsl @@ -1,6 +1,8 @@ +/* clang-format off */ [vertex] layout(location = 0) in highp vec4 vertex_attrib; +/* clang-format on */ layout(location = 4) in vec2 uv_in; out vec2 uv_interp; @@ -13,9 +15,11 @@ void main() { pos_interp.xy = gl_Position.xy; } +/* clang-format off */ [fragment] in vec2 uv_interp; +/* clang-format on */ in vec2 pos_interp; uniform sampler2D source_diffuse; //texunit:0 diff --git a/drivers/gles3/shaders/ssao.glsl b/drivers/gles3/shaders/ssao.glsl index 2eeeac31c3..be44365169 100644 --- a/drivers/gles3/shaders/ssao.glsl +++ b/drivers/gles3/shaders/ssao.glsl @@ -1,6 +1,8 @@ +/* clang-format off */ [vertex] layout(location = 0) in highp vec4 vertex_attrib; +/* clang-format on */ void main() { @@ -8,6 +10,7 @@ void main() { gl_Position.z = 1.0; } +/* clang-format off */ [fragment] #define TWO_PI 6.283185307179586476925286766559 @@ -46,8 +49,8 @@ const int ROTATIONS[] = int[]( 13, 17, 11, 17, 19, 18, 25, 18, 19, 19, 29, 21, 19, 27, 31, 29, 21, 18, 17, 29, 31, 31, 23, 18, 25, 26, 25, 23, 19, 34, - 19, 27, 21, 25, 39, 29, 17, 21, 27 -); + 19, 27, 21, 25, 39, 29, 17, 21, 27); +/* clang-format on */ //#define NUM_SPIRAL_TURNS (7) const int NUM_SPIRAL_TURNS = ROTATIONS[NUM_SAMPLES - 1]; diff --git a/drivers/gles3/shaders/ssao_blur.glsl b/drivers/gles3/shaders/ssao_blur.glsl index 5526d0de18..c49ea1e957 100644 --- a/drivers/gles3/shaders/ssao_blur.glsl +++ b/drivers/gles3/shaders/ssao_blur.glsl @@ -1,6 +1,8 @@ +/* clang-format off */ [vertex] layout(location = 0) in highp vec4 vertex_attrib; +/* clang-format on */ void main() { @@ -8,9 +10,11 @@ void main() { gl_Position.z = 1.0; } +/* clang-format off */ [fragment] uniform sampler2D source_ssao; //texunit:0 +/* clang-format on */ uniform sampler2D source_depth; //texunit:1 uniform sampler2D source_normal; //texunit:3 @@ -36,16 +40,14 @@ uniform int filter_scale; /** Filter radius in pixels. This will be multiplied by SCALE. */ #define R (4) - ////////////////////////////////////////////////////////////////////////////////////////////// - // Gaussian coefficients const float gaussian[R + 1] = -// float[](0.356642, 0.239400, 0.072410, 0.009869); -// float[](0.398943, 0.241971, 0.053991, 0.004432, 0.000134); // stddev = 1.0 - float[](0.153170, 0.144893, 0.122649, 0.092902, 0.062970); // stddev = 2.0 -// float[](0.111220, 0.107798, 0.098151, 0.083953, 0.067458, 0.050920, 0.036108); // stddev = 3.0 + //float[](0.356642, 0.239400, 0.072410, 0.009869); + //float[](0.398943, 0.241971, 0.053991, 0.004432, 0.000134); // stddev = 1.0 + float[](0.153170, 0.144893, 0.122649, 0.092902, 0.062970); // stddev = 2.0 +//float[](0.111220, 0.107798, 0.098151, 0.083953, 0.067458, 0.050920, 0.036108); // stddev = 3.0 /** (1, 0) or (0, 1) */ uniform ivec2 axis; diff --git a/drivers/gles3/shaders/ssao_minify.glsl b/drivers/gles3/shaders/ssao_minify.glsl index 777a0069fc..1696648dae 100644 --- a/drivers/gles3/shaders/ssao_minify.glsl +++ b/drivers/gles3/shaders/ssao_minify.glsl @@ -1,18 +1,22 @@ +/* clang-format off */ [vertex] layout(location = 0) in highp vec4 vertex_attrib; +/* clang-format on */ void main() { gl_Position = vertex_attrib; } +/* clang-format off */ [fragment] #ifdef MINIFY_START #define SDEPTH_TYPE highp sampler2D uniform float camera_z_far; +/* clang-format on */ uniform float camera_z_near; #else diff --git a/drivers/gles3/shaders/subsurf_scattering.glsl b/drivers/gles3/shaders/subsurf_scattering.glsl index af0d5a0e62..f40fb3a244 100644 --- a/drivers/gles3/shaders/subsurf_scattering.glsl +++ b/drivers/gles3/shaders/subsurf_scattering.glsl @@ -1,6 +1,8 @@ +/* clang-format off */ [vertex] layout(location = 0) in highp vec4 vertex_attrib; +/* clang-format on */ layout(location = 4) in vec2 uv_in; out vec2 uv_interp; @@ -11,6 +13,7 @@ void main() { gl_Position = vertex_attrib; } +/* clang-format off */ [fragment] //#define QUALIFIER uniform // some guy on the interweb says it may be faster with this @@ -18,7 +21,8 @@ void main() { #ifdef USE_25_SAMPLES const int kernel_size = 25; -QUALIFIER vec2 kernel[25] = vec2[] ( +/* clang-format on */ +QUALIFIER vec2 kernel[25] = vec2[]( vec2(0.530605, 0.0), vec2(0.000973794, -3.0), vec2(0.00333804, -2.52083), @@ -43,8 +47,7 @@ QUALIFIER vec2 kernel[25] = vec2[] ( vec2(0.00700976, 1.6875), vec2(0.00500364, 2.08333), vec2(0.00333804, 2.52083), - vec2(0.000973794, 3.0) -); + vec2(0.000973794, 3.0)); #endif //USE_25_SAMPLES #ifdef USE_17_SAMPLES @@ -66,11 +69,9 @@ QUALIFIER vec2 kernel[17] = vec2[]( vec2(0.0216301, 0.78125), vec2(0.0144609, 1.125), vec2(0.0100386, 1.53125), - vec2(0.00317394, 2.0) -); + vec2(0.00317394, 2.0)); #endif //USE_17_SAMPLES - #ifdef USE_11_SAMPLES const int kernel_size = 11; QUALIFIER vec2 kernel[11] = vec2[]( @@ -84,8 +85,7 @@ QUALIFIER vec2 kernel[11] = vec2[]( vec2(0.0821904, 0.32), vec2(0.03639, 0.72), vec2(0.0192831, 1.28), - vec2(0.00471691, 2.0) -); + vec2(0.00471691, 2.0)); #endif //USE_11_SAMPLES uniform float max_radius; diff --git a/drivers/gles3/shaders/tonemap.glsl b/drivers/gles3/shaders/tonemap.glsl index 56876bdb72..80ad003c80 100644 --- a/drivers/gles3/shaders/tonemap.glsl +++ b/drivers/gles3/shaders/tonemap.glsl @@ -1,6 +1,8 @@ +/* clang-format off */ [vertex] layout(location = 0) in highp vec4 vertex_attrib; +/* clang-format on */ layout(location = 4) in vec2 uv_in; out vec2 uv_interp; @@ -15,11 +17,13 @@ void main() { #endif } +/* clang-format off */ [fragment] #if !defined(GLES_OVER_GL) precision mediump float; #endif +/* clang-format on */ in vec2 uv_interp; @@ -110,10 +114,8 @@ vec4 texture2D_bicubic(sampler2D tex, vec2 uv, int p_lod) { vec2 p2 = (vec2(iuv.x + h0x, iuv.y + h1y) - vec2(0.5f)) * pixel_size; vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - vec2(0.5f)) * pixel_size; - return g0(fuv.y) * (g0x * textureLod(tex, p0, lod) + - g1x * textureLod(tex, p1, lod)) + - g1(fuv.y) * (g0x * textureLod(tex, p2, lod) + - g1x * textureLod(tex, p3, lod)); + return (g0(fuv.y) * (g0x * textureLod(tex, p0, lod) + g1x * textureLod(tex, p1, lod))) + + (g1(fuv.y) * (g0x * textureLod(tex, p2, lod) + g1x * textureLod(tex, p3, lod))); } #define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) texture2D_bicubic(m_tex, m_uv, m_lod) @@ -122,13 +124,16 @@ vec4 texture2D_bicubic(sampler2D tex, vec2 uv, int p_lod) { #endif vec3 tonemap_filmic(vec3 color, float white) { - const float A = 0.15f; - const float B = 0.50f; + // exposure bias: input scale (color *= bias, white *= bias) to make the brighness consistent with other tonemappers + // also useful to scale the input to the range that the tonemapper is designed for (some require very high input values) + // has no effect on the curve's general shape or visual properties + const float exposure_bias = 2.0f; + const float A = 0.22f * exposure_bias * exposure_bias; // bias baked into constants for performance + const float B = 0.30f * exposure_bias; const float C = 0.10f; const float D = 0.20f; - const float E = 0.02f; + const float E = 0.01f; const float F = 0.30f; - const float W = 11.2f; vec3 color_tonemapped = ((color * (A * color + C * B) + D * E) / (color * (A * color + B) + D * F)) - E / F; float white_tonemapped = ((white * (A * white + C * B) + D * E) / (white * (A * white + B) + D * F)) - E / F; @@ -137,10 +142,11 @@ vec3 tonemap_filmic(vec3 color, float white) { } vec3 tonemap_aces(vec3 color, float white) { - const float A = 2.51f; - const float B = 0.03f; - const float C = 2.43f; - const float D = 0.59f; + const float exposure_bias = 0.85f; + const float A = 2.51f * exposure_bias * exposure_bias; + const float B = 0.03f * exposure_bias; + const float C = 2.43f * exposure_bias * exposure_bias; + const float D = 0.59f * exposure_bias; const float E = 0.14f; vec3 color_tonemapped = (color * (A * color + B)) / (color * (C * color + D) + E); @@ -149,8 +155,8 @@ vec3 tonemap_aces(vec3 color, float white) { return clamp(color_tonemapped / white_tonemapped, vec3(0.0f), vec3(1.0f)); } -vec3 tonemap_reindhart(vec3 color, float white) { - return clamp((color) / (1.0f + color) * (1.0f + (color / (white))), vec3(0.0f), vec3(1.0f)); // whitepoint is probably not in linear space here! +vec3 tonemap_reinhard(vec3 color, float white) { + return clamp((white * color + color) / (color * white + white), vec3(0.0f), vec3(1.0f)); } vec3 linear_to_srgb(vec3 color) { // convert linear rgb to srgb, assumes clamped input in range [0;1] @@ -159,8 +165,8 @@ vec3 linear_to_srgb(vec3 color) { // convert linear rgb to srgb, assumes clamped } vec3 apply_tonemapping(vec3 color, float white) { // inputs are LINEAR, always outputs clamped [0;1] color -#ifdef USE_REINDHART_TONEMAPPER - return tonemap_reindhart(color, white); +#ifdef USE_REINHARD_TONEMAPPER + return tonemap_reinhard(color, white); #endif #ifdef USE_FILMIC_TONEMAPPER @@ -171,7 +177,7 @@ vec3 apply_tonemapping(vec3 color, float white) { // inputs are LINEAR, always o return tonemap_aces(color, white); #endif - return clamp(color, vec3(0.0f), vec3(1.0f)); // no other seleced -> linear + return clamp(color, vec3(0.0f), vec3(1.0f)); // no other selected -> linear } vec3 gather_glow(sampler2D tex, vec2 uv) { // sample all selected glow levels diff --git a/drivers/png/SCsub b/drivers/png/SCsub index 39480351a6..22fb1817d1 100644 --- a/drivers/png/SCsub +++ b/drivers/png/SCsub @@ -26,14 +26,24 @@ if env['builtin_libpng']: ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_png.add_source_files(env.drivers_sources, thirdparty_sources) env_png.Append(CPPPATH=[thirdparty_dir]) + # Needed for drivers includes and in platform/javascript + env.Append(CPPPATH=[thirdparty_dir]) # Currently .ASM filter_neon.S does not compile on NT. import os - if ("neon_enabled" in env and env["neon_enabled"]) and os.name != "nt": + use_neon = "neon_enabled" in env and env["neon_enabled"] and os.name != "nt" + if use_neon: env_png.Append(CPPFLAGS=["-DPNG_ARM_NEON_OPT=2"]) - env_neon = env_png.Clone() + else: + env_png.Append(CPPFLAGS=["-DPNG_ARM_NEON_OPT=0"]) + + env_thirdparty = env_png.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.drivers_sources, thirdparty_sources) + + if use_neon: + env_neon = env_thirdparty.Clone() if "S_compiler" in env: env_neon['CC'] = env['S_compiler'] neon_sources = [] @@ -41,8 +51,6 @@ if env['builtin_libpng']: neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/filter_neon_intrinsics.c")) neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/filter_neon.S")) env.drivers_sources += neon_sources - else: - env_png.Append(CPPFLAGS=["-DPNG_ARM_NEON_OPT=0"]) # Godot source files env_png.add_source_files(env.drivers_sources, "*.cpp") diff --git a/drivers/png/image_loader_png.cpp b/drivers/png/image_loader_png.cpp index b08688892e..a4ea889d3b 100644 --- a/drivers/png/image_loader_png.cpp +++ b/drivers/png/image_loader_png.cpp @@ -30,8 +30,8 @@ #include "image_loader_png.h" -#include "os/os.h" -#include "print_string.h" +#include "core/os/os.h" +#include "core/print_string.h" #include <string.h> @@ -227,10 +227,7 @@ static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t p_len PNGReadStatus *rstatus; rstatus = (PNGReadStatus *)png_get_io_ptr(png_ptr); - png_size_t to_read = p_length; - if (rstatus->size >= 0) { - to_read = MIN(p_length, rstatus->size - rstatus->offset); - } + png_size_t to_read = MIN(p_length, rstatus->size - rstatus->offset); memcpy(data, &rstatus->image[rstatus->offset], to_read); rstatus->offset += to_read; diff --git a/drivers/png/image_loader_png.h b/drivers/png/image_loader_png.h index 48f48e6bea..5dff7e3902 100644 --- a/drivers/png/image_loader_png.h +++ b/drivers/png/image_loader_png.h @@ -31,7 +31,7 @@ #ifndef IMAGE_LOADER_PNG_H #define IMAGE_LOADER_PNG_H -#include "io/image_loader.h" +#include "core/io/image_loader.h" #include <png.h> diff --git a/drivers/png/resource_saver_png.cpp b/drivers/png/resource_saver_png.cpp index 270ae36e1e..c5729f70b2 100644 --- a/drivers/png/resource_saver_png.cpp +++ b/drivers/png/resource_saver_png.cpp @@ -31,8 +31,8 @@ #include "resource_saver_png.h" #include "core/image.h" -#include "os/file_access.h" -#include "project_settings.h" +#include "core/os/file_access.h" +#include "core/project_settings.h" #include "scene/resources/texture.h" #include <png.h> diff --git a/drivers/png/resource_saver_png.h b/drivers/png/resource_saver_png.h index 109b4801da..34950f6723 100644 --- a/drivers/png/resource_saver_png.h +++ b/drivers/png/resource_saver_png.h @@ -31,8 +31,8 @@ #ifndef RESOURCE_SAVER_PNG_H #define RESOURCE_SAVER_PNG_H -#include "image.h" -#include "io/resource_saver.h" +#include "core/image.h" +#include "core/io/resource_saver.h" class ResourceSaverPNG : public ResourceFormatSaver { public: diff --git a/drivers/pulseaudio/SCsub b/drivers/pulseaudio/SCsub index ee39fd2631..28b315ae66 100644 --- a/drivers/pulseaudio/SCsub +++ b/drivers/pulseaudio/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.drivers_sources, "*.cpp") - -Export('env') diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp index 744b3a35e6..d78316945f 100644 --- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp +++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp @@ -32,10 +32,8 @@ #ifdef PULSEAUDIO_ENABLED -#include <pulse/pulseaudio.h> - -#include "os/os.h" -#include "project_settings.h" +#include "core/os/os.h" +#include "core/project_settings.h" void AudioDriverPulseAudio::pa_state_cb(pa_context *c, void *userdata) { AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata; @@ -45,10 +43,13 @@ void AudioDriverPulseAudio::pa_state_cb(pa_context *c, void *userdata) { case PA_CONTEXT_FAILED: ad->pa_ready = -1; break; - case PA_CONTEXT_READY: ad->pa_ready = 1; break; + default: + // TODO: Check if we want to handle some of the other + // PA context states like PA_CONTEXT_UNCONNECTED. + break; } } @@ -342,7 +343,7 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) { 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++) { + for (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++]; @@ -612,20 +613,18 @@ Error AudioDriverPulseAudio::capture_init_device() { break; } - print_verbose("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; + int input_latency = 30; + int input_buffer_frames = closest_power_of_2(input_latency * mix_rate / 1000); + int input_buffer_size = input_buffer_frames * spec.channels; pa_buffer_attr attr; - attr.fragsize = buffer_size * sizeof(int16_t); + attr.fragsize = input_buffer_size * sizeof(int16_t); pa_rec_str = pa_stream_new(pa_ctx, "Record", &spec, &pa_rec_map); if (pa_rec_str == NULL) { @@ -641,9 +640,10 @@ Error AudioDriverPulseAudio::capture_init_device() { ERR_FAIL_V(ERR_CANT_OPEN); } - input_buffer.resize(input_buffer_frames * 8); - input_position = 0; - input_size = 0; + input_buffer_init(input_buffer_frames); + + print_verbose("PulseAudio: detected " + itos(pa_rec_map.channels) + " input channels"); + print_verbose("PulseAudio: input buffer frames: " + itos(input_buffer_frames) + " calculated latency: " + itos(input_buffer_frames * 1000 / mix_rate) + "ms"); return OK; } @@ -759,7 +759,6 @@ 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 f8358a452b..d8bab841ff 100644 --- a/drivers/pulseaudio/audio_driver_pulseaudio.h +++ b/drivers/pulseaudio/audio_driver_pulseaudio.h @@ -64,7 +64,6 @@ class AudioDriverPulseAudio : public AudioDriver { unsigned int mix_rate; unsigned int buffer_frames; - unsigned int input_buffer_frames; unsigned int pa_buffer_size; int channels; int pa_ready; diff --git a/drivers/register_driver_types.cpp b/drivers/register_driver_types.cpp index c6d36a5749..9f5d9c1abf 100644 --- a/drivers/register_driver_types.cpp +++ b/drivers/register_driver_types.cpp @@ -31,11 +31,11 @@ #include "register_driver_types.h" #include "core/math/geometry.h" -#include "png/image_loader_png.h" -#include "png/resource_saver_png.h" +#include "drivers/png/image_loader_png.h" +#include "drivers/png/resource_saver_png.h" #ifdef TOOLS_ENABLED -#include "convex_decomp/b2d_decompose.h" +#include "drivers/convex_decomp/b2d_decompose.h" #endif #ifdef TOOLS_ENABLED diff --git a/drivers/rtaudio/SCsub b/drivers/rtaudio/SCsub index 2b0a602965..285658073c 100644 --- a/drivers/rtaudio/SCsub +++ b/drivers/rtaudio/SCsub @@ -11,9 +11,12 @@ thirdparty_sources = [ ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env.add_source_files(env.drivers_sources, thirdparty_sources) env.Append(CPPPATH=[thirdparty_dir]) +env_thirdparty = env.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(env.drivers_sources, thirdparty_sources) + # Driver source files env.add_source_files(env.drivers_sources, "*.cpp") diff --git a/drivers/rtaudio/audio_driver_rtaudio.cpp b/drivers/rtaudio/audio_driver_rtaudio.cpp index 69830b542b..bc6ceb1e7e 100644 --- a/drivers/rtaudio/audio_driver_rtaudio.cpp +++ b/drivers/rtaudio/audio_driver_rtaudio.cpp @@ -30,8 +30,8 @@ #include "audio_driver_rtaudio.h" -#include "os/os.h" -#include "project_settings.h" +#include "core/os/os.h" +#include "core/project_settings.h" #ifdef RTAUDIO_ENABLED @@ -114,11 +114,12 @@ Error AudioDriverRtAudio::init() { unsigned int buffer_frames = closest_power_of_2(latency * mix_rate / 1000); print_verbose("Audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms"); - short int tries = 2; + short int tries = 4; - while (tries >= 0) { + while (tries > 0) { switch (speaker_mode) { case SPEAKER_MODE_STEREO: parameters.nChannels = 2; break; + case SPEAKER_SURROUND_31: parameters.nChannels = 4; break; case SPEAKER_SURROUND_51: parameters.nChannels = 6; break; case SPEAKER_SURROUND_71: parameters.nChannels = 8; break; }; @@ -128,12 +129,14 @@ Error AudioDriverRtAudio::init() { active = true; break; - } catch (RtAudioError &e) { + } catch (RtAudioError) { // try with less channels ERR_PRINT("Unable to open audio, retrying with fewer channels..."); switch (speaker_mode) { - case SPEAKER_SURROUND_51: speaker_mode = SPEAKER_MODE_STEREO; break; + case SPEAKER_MODE_STEREO: break; // Required to silence unhandled enum value warning. + case SPEAKER_SURROUND_31: speaker_mode = SPEAKER_MODE_STEREO; break; + case SPEAKER_SURROUND_51: speaker_mode = SPEAKER_SURROUND_31; break; case SPEAKER_SURROUND_71: speaker_mode = SPEAKER_SURROUND_51; break; } diff --git a/drivers/unix/SCsub b/drivers/unix/SCsub index ada8255580..4888f56099 100644 --- a/drivers/unix/SCsub +++ b/drivers/unix/SCsub @@ -5,5 +5,3 @@ Import('env') env.add_source_files(env.drivers_sources, "*.cpp") env["check_c_headers"] = [ [ "mntent.h", "HAVE_MNTENT" ] ] - -Export('env') diff --git a/drivers/unix/dir_access_unix.cpp b/drivers/unix/dir_access_unix.cpp index 5a4be6df4f..929e67faa9 100644 --- a/drivers/unix/dir_access_unix.cpp +++ b/drivers/unix/dir_access_unix.cpp @@ -32,18 +32,19 @@ #if defined(UNIX_ENABLED) || defined(LIBC_FILEIO_ENABLED) -#ifndef ANDROID_ENABLED -#include <sys/statvfs.h> -#endif - #include "core/list.h" -#include "os/memory.h" -#include "print_string.h" +#include "core/os/memory.h" +#include "core/print_string.h" + #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#ifndef ANDROID_ENABLED +#include <sys/statvfs.h> +#endif + #ifdef HAVE_MNTENT #include <mntent.h> #endif @@ -59,7 +60,7 @@ Error DirAccessUnix::list_dir_begin() { //char real_current_dir_name[2048]; //is this enough?! //getcwd(real_current_dir_name,2048); - //chdir(curent_path.utf8().get_data()); + //chdir(current_path.utf8().get_data()); dir_stream = opendir(current_dir.utf8().get_data()); //chdir(real_current_dir_name); if (!dir_stream) @@ -308,7 +309,7 @@ Error DirAccessUnix::change_dir(String p_dir) { // prev_dir is the directory we are changing out of String prev_dir; char real_current_dir_name[2048]; - getcwd(real_current_dir_name, 2048); + ERR_FAIL_COND_V(getcwd(real_current_dir_name, 2048) == NULL, ERR_BUG); if (prev_dir.parse_utf8(real_current_dir_name)) prev_dir = real_current_dir_name; //no utf8, maybe latin? @@ -329,7 +330,7 @@ Error DirAccessUnix::change_dir(String p_dir) { // the directory exists, so set current_dir to try_dir current_dir = try_dir; - chdir(prev_dir.utf8().get_data()); + ERR_FAIL_COND_V(chdir(prev_dir.utf8().get_data()) != 0, ERR_BUG); return OK; } @@ -390,7 +391,7 @@ size_t DirAccessUnix::get_space_left() { return vfs.f_bfree * vfs.f_bsize; #else -#warning THIS IS BROKEN + // FIXME: Implement this. return 0; #endif }; @@ -404,7 +405,7 @@ DirAccessUnix::DirAccessUnix() { // set current directory to an absolute path of the current directory char real_current_dir_name[2048]; - getcwd(real_current_dir_name, 2048); + ERR_FAIL_COND(getcwd(real_current_dir_name, 2048) == NULL); if (current_dir.parse_utf8(real_current_dir_name)) current_dir = real_current_dir_name; diff --git a/drivers/unix/dir_access_unix.h b/drivers/unix/dir_access_unix.h index a55acdbd34..26978930bd 100644 --- a/drivers/unix/dir_access_unix.h +++ b/drivers/unix/dir_access_unix.h @@ -33,16 +33,17 @@ #if defined(UNIX_ENABLED) || defined(LIBC_FILEIO_ENABLED) +#include "core/os/dir_access.h" + #include <dirent.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> -#include "os/dir_access.h" - /** @author Juan Linietsky <reduzio@gmail.com> */ + class DirAccessUnix : public DirAccess { DIR *dir_stream; diff --git a/drivers/unix/file_access_unix.cpp b/drivers/unix/file_access_unix.cpp index ca16c6fcae..3b97b95f7c 100644 --- a/drivers/unix/file_access_unix.cpp +++ b/drivers/unix/file_access_unix.cpp @@ -33,7 +33,8 @@ #if defined(UNIX_ENABLED) || defined(LIBC_FILEIO_ENABLED) #include "core/os/os.h" -#include "print_string.h" +#include "core/print_string.h" + #include <sys/stat.h> #include <sys/types.h> diff --git a/drivers/unix/file_access_unix.h b/drivers/unix/file_access_unix.h index 88bb39fbd1..d4a4f8230c 100644 --- a/drivers/unix/file_access_unix.h +++ b/drivers/unix/file_access_unix.h @@ -31,8 +31,9 @@ #ifndef FILE_ACCESS_UNIX_H #define FILE_ACCESS_UNIX_H -#include "os/file_access.h" -#include "os/memory.h" +#include "core/os/file_access.h" +#include "core/os/memory.h" + #include <stdio.h> #if defined(UNIX_ENABLED) || defined(LIBC_FILEIO_ENABLED) diff --git a/drivers/unix/ip_unix.h b/drivers/unix/ip_unix.h index d3d1ccfa85..83535045b1 100644 --- a/drivers/unix/ip_unix.h +++ b/drivers/unix/ip_unix.h @@ -31,7 +31,7 @@ #ifndef IP_UNIX_H #define IP_UNIX_H -#include "io/ip.h" +#include "core/io/ip.h" #if defined(UNIX_ENABLED) || defined(WINDOWS_ENABLED) diff --git a/drivers/unix/mutex_posix.cpp b/drivers/unix/mutex_posix.cpp index 1f13720f1e..e0004c5730 100644 --- a/drivers/unix/mutex_posix.cpp +++ b/drivers/unix/mutex_posix.cpp @@ -29,7 +29,8 @@ /*************************************************************************/ #include "mutex_posix.h" -#include "os/memory.h" + +#include "core/os/memory.h" #if defined(UNIX_ENABLED) || defined(PTHREAD_ENABLED) diff --git a/drivers/unix/mutex_posix.h b/drivers/unix/mutex_posix.h index a4de1603f3..80d85eee61 100644 --- a/drivers/unix/mutex_posix.h +++ b/drivers/unix/mutex_posix.h @@ -33,7 +33,8 @@ #if defined(UNIX_ENABLED) || defined(PTHREAD_ENABLED) -#include "os/mutex.h" +#include "core/os/mutex.h" + #include <pthread.h> class MutexPosix : public Mutex { diff --git a/drivers/unix/net_socket_posix.cpp b/drivers/unix/net_socket_posix.cpp new file mode 100644 index 0000000000..f981be66ce --- /dev/null +++ b/drivers/unix/net_socket_posix.cpp @@ -0,0 +1,617 @@ +/*************************************************************************/ +/* net_socket_posix.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 "net_socket_posix.h" + +#if defined(UNIX_ENABLED) + +#include <errno.h> +#include <netdb.h> +#include <poll.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <unistd.h> +#ifndef NO_FCNTL +#include <fcntl.h> +#else +#include <sys/ioctl.h> +#endif +#include <netinet/in.h> + +#include <sys/socket.h> +#ifdef JAVASCRIPT_ENABLED +#include <arpa/inet.h> +#endif + +#include <netinet/tcp.h> + +#if defined(__APPLE__) +#define MSG_NOSIGNAL SO_NOSIGPIPE +#endif + +// Some custom defines to minimize ifdefs +#define SOCK_EMPTY -1 +#define SOCK_BUF(x) x +#define SOCK_CBUF(x) x +#define SOCK_IOCTL ioctl +#define SOCK_CLOSE ::close + +/* Windows */ +#elif defined(WINDOWS_ENABLED) +#include <winsock2.h> +#include <ws2tcpip.h> + +#include <mswsock.h> +// Some custom defines to minimize ifdefs +#define SOCK_EMPTY INVALID_SOCKET +#define SOCK_BUF(x) (char *)(x) +#define SOCK_CBUF(x) (const char *)(x) +#define SOCK_IOCTL ioctlsocket +#define SOCK_CLOSE closesocket + +// Windows doesn't have this flag +#ifndef MSG_NOSIGNAL +#define MSG_NOSIGNAL 0 +#endif +// Workaround missing flag in MinGW +#if defined(__MINGW32__) && !defined(SIO_UDP_NETRESET) +#define SIO_UDP_NETRESET _WSAIOW(IOC_VENDOR, 15) +#endif + +#endif + +size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const IP_Address &p_ip, uint16_t p_port, IP::Type p_ip_type) { + + memset(p_addr, 0, sizeof(struct sockaddr_storage)); + if (p_ip_type == IP::TYPE_IPV6 || p_ip_type == IP::TYPE_ANY) { // IPv6 socket + + // IPv6 only socket with IPv4 address + ERR_FAIL_COND_V(!p_ip.is_wildcard() && p_ip_type == IP::TYPE_IPV6 && p_ip.is_ipv4(), 0); + + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)p_addr; + addr6->sin6_family = AF_INET6; + addr6->sin6_port = htons(p_port); + if (p_ip.is_valid()) { + copymem(&addr6->sin6_addr.s6_addr, p_ip.get_ipv6(), 16); + } else { + addr6->sin6_addr = in6addr_any; + } + return sizeof(sockaddr_in6); + } else { // IPv4 socket + + // IPv4 socket with IPv6 address + ERR_FAIL_COND_V(!p_ip.is_ipv4(), 0); + + struct sockaddr_in *addr4 = (struct sockaddr_in *)p_addr; + addr4->sin_family = AF_INET; + addr4->sin_port = htons(p_port); // short, network byte order + + if (p_ip.is_valid()) { + copymem(&addr4->sin_addr.s_addr, p_ip.get_ipv4(), 4); + } else { + addr4->sin_addr.s_addr = INADDR_ANY; + } + + copymem(&addr4->sin_addr.s_addr, p_ip.get_ipv4(), 4); + return sizeof(sockaddr_in); + } +} + +void NetSocketPosix::_set_ip_port(struct sockaddr_storage *p_addr, IP_Address &r_ip, uint16_t &r_port) { + + if (p_addr->ss_family == AF_INET) { + + struct sockaddr_in *addr4 = (struct sockaddr_in *)p_addr; + r_ip.set_ipv4((uint8_t *)&(addr4->sin_addr.s_addr)); + + r_port = ntohs(addr4->sin_port); + + } else if (p_addr->ss_family == AF_INET6) { + + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)p_addr; + r_ip.set_ipv6(addr6->sin6_addr.s6_addr); + + r_port = ntohs(addr6->sin6_port); + }; +} + +NetSocket *NetSocketPosix::_create_func() { + return memnew(NetSocketPosix); +} + +void NetSocketPosix::make_default() { +#if defined(WINDOWS_ENABLED) + if (_create == NULL) { + WSADATA data; + WSAStartup(MAKEWORD(2, 2), &data); + } +#endif + _create = _create_func; +} + +void NetSocketPosix::cleanup() { +#if defined(WINDOWS_ENABLED) + if (_create != NULL) { + WSACleanup(); + } + _create = NULL; +#endif +} + +NetSocketPosix::NetSocketPosix() { + _sock = SOCK_EMPTY; + _ip_type = IP::TYPE_NONE; + _is_stream = false; +} + +NetSocketPosix::~NetSocketPosix() { + close(); +} + +NetSocketPosix::NetError NetSocketPosix::_get_socket_error() { +#if defined(WINDOWS_ENABLED) + int err = WSAGetLastError(); + + if (err == WSAEISCONN) + return ERR_NET_IS_CONNECTED; + if (err == WSAEINPROGRESS || err == WSAEALREADY) + return ERR_NET_IN_PROGRESS; + if (err == WSAEWOULDBLOCK) + return ERR_NET_WOULD_BLOCK; + ERR_PRINTS("Socket error: " + itos(err)); + return ERR_NET_OTHER; +#else + if (errno == EISCONN) + return ERR_NET_IS_CONNECTED; + if (errno == EINPROGRESS || errno == EALREADY) + return ERR_NET_IN_PROGRESS; + if (errno == EAGAIN || errno == EWOULDBLOCK) + return ERR_NET_WOULD_BLOCK; + ERR_PRINTS("Socket error: " + itos(errno)); + return ERR_NET_OTHER; +#endif +} + +bool NetSocketPosix::_can_use_ip(const IP_Address p_ip, const bool p_for_bind) const { + + if (p_for_bind && !(p_ip.is_valid() || p_ip.is_wildcard())) { + return false; + } else if (!p_for_bind && !p_ip.is_valid()) { + return false; + } + // Check if socket support this IP type. + IP::Type type = p_ip.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; + if (_ip_type != IP::TYPE_ANY && !p_ip.is_wildcard() && _ip_type != type) { + return false; + } + return true; +} + +void NetSocketPosix::_set_socket(SOCKET_TYPE p_sock, IP::Type p_ip_type, bool p_is_stream) { + _sock = p_sock; + _ip_type = p_ip_type; + _is_stream = p_is_stream; +} + +Error NetSocketPosix::open(Type p_sock_type, IP::Type &ip_type) { + ERR_FAIL_COND_V(is_open(), ERR_ALREADY_IN_USE); + ERR_FAIL_COND_V(ip_type > IP::TYPE_ANY || ip_type < IP::TYPE_NONE, ERR_INVALID_PARAMETER); + +#if defined(__OpenBSD__) + // OpenBSD does not support dual stacking, fallback to IPv4 only. + if (ip_type == IP::TYPE_ANY) + ip_type = IP::TYPE_IPV4; +#endif + + int family = ip_type == IP::TYPE_IPV4 ? AF_INET : AF_INET6; + int protocol = p_sock_type == TYPE_TCP ? IPPROTO_TCP : IPPROTO_UDP; + int type = p_sock_type == TYPE_TCP ? SOCK_STREAM : SOCK_DGRAM; + _sock = socket(family, type, protocol); + + if (_sock == SOCK_EMPTY && ip_type == IP::TYPE_ANY) { + // Careful here, changing the referenced parameter so the caller knows that we are using an IPv4 socket + // in place of a dual stack one, and further calls to _set_sock_addr will work as expected. + ip_type = IP::TYPE_IPV4; + family = AF_INET; + _sock = socket(family, type, protocol); + } + + ERR_FAIL_COND_V(_sock == SOCK_EMPTY, FAILED); + _ip_type = ip_type; + + if (family == AF_INET6) { + // Select IPv4 over IPv6 mapping + set_ipv6_only_enabled(ip_type != IP::TYPE_ANY); + } + + if (protocol == IPPROTO_UDP && ip_type != IP::TYPE_IPV6) { + // Enable broadcasting for UDP sockets if it's not IPv6 only (IPv6 has no broadcast option). + set_broadcasting_enabled(true); + } + + _is_stream = p_sock_type == TYPE_TCP; + +#if defined(WINDOWS_ENABLED) + if (!_is_stream) { + // Disable windows feature/bug reporting WSAECONNRESET/WSAENETRESET when + // recv/recvfrom and an ICMP reply was received from a previous send/sendto. + unsigned long disable = 0; + if (ioctlsocket(_sock, SIO_UDP_CONNRESET, &disable) == SOCKET_ERROR) { + print_verbose("Unable to turn off UDP WSAECONNRESET behaviour on Windows"); + } + if (ioctlsocket(_sock, SIO_UDP_NETRESET, &disable) == SOCKET_ERROR) { + // This feature seems not to be supported on wine. + print_verbose("Unable to turn off UDP WSAENETRESET behaviour on Windows"); + } + } +#endif + return OK; +} + +void NetSocketPosix::close() { + + if (_sock != SOCK_EMPTY) + SOCK_CLOSE(_sock); + + _sock = SOCK_EMPTY; + _ip_type = IP::TYPE_NONE; + _is_stream = false; +} + +Error NetSocketPosix::bind(IP_Address p_addr, uint16_t p_port) { + + ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); + ERR_FAIL_COND_V(!_can_use_ip(p_addr, true), ERR_INVALID_PARAMETER); + + sockaddr_storage addr; + size_t addr_size = _set_addr_storage(&addr, p_addr, p_port, _ip_type); + + if (::bind(_sock, (struct sockaddr *)&addr, addr_size) == SOCK_EMPTY) { + close(); + ERR_FAIL_V(ERR_UNAVAILABLE); + } + + return OK; +} + +Error NetSocketPosix::listen(int p_max_pending) { + ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); + + if (::listen(_sock, p_max_pending) == SOCK_EMPTY) { + + close(); + ERR_FAIL_V(FAILED); + }; + + return OK; +} + +Error NetSocketPosix::connect_to_host(IP_Address p_host, uint16_t p_port) { + + ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); + ERR_FAIL_COND_V(!_can_use_ip(p_host, false), ERR_INVALID_PARAMETER); + + struct sockaddr_storage addr; + size_t addr_size = _set_addr_storage(&addr, p_host, p_port, _ip_type); + + if (::connect(_sock, (struct sockaddr *)&addr, addr_size) == SOCK_EMPTY) { + + NetError err = _get_socket_error(); + + switch (err) { + // We are already connected + case ERR_NET_IS_CONNECTED: + return OK; + // Still waiting to connect, try again in a while + case ERR_NET_WOULD_BLOCK: + case ERR_NET_IN_PROGRESS: + return ERR_BUSY; + default: + ERR_PRINT("Connection to remote host failed!"); + close(); + return FAILED; + } + } + + return OK; +} + +Error NetSocketPosix::poll(PollType p_type, int p_timeout) const { + + ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); + +#if defined(WINDOWS_ENABLED) + bool ready = false; + fd_set rd, wr, ex; + fd_set *rdp = NULL; + fd_set *wrp = NULL; + FD_ZERO(&rd); + FD_ZERO(&wr); + FD_ZERO(&ex); + FD_SET(_sock, &ex); + struct timeval timeout = { p_timeout, 0 }; + // For blocking operation, pass NULL timeout pointer to select. + struct timeval *tp = NULL; + if (p_timeout >= 0) { + // If timeout is non-negative, we want to specify the timeout instead. + tp = &timeout; + } + + switch (p_type) { + case POLL_TYPE_IN: + FD_SET(_sock, &rd); + rdp = &rd; + break; + case POLL_TYPE_OUT: + FD_SET(_sock, &wr); + wrp = ≀ + break; + case POLL_TYPE_IN_OUT: + FD_SET(_sock, &rd); + FD_SET(_sock, &wr); + rdp = &rd; + wrp = ≀ + } + int ret = select(1, rdp, wrp, &ex, tp); + + ERR_FAIL_COND_V(ret == SOCKET_ERROR, FAILED); + + if (ret == 0) + return ERR_BUSY; + + ERR_FAIL_COND_V(FD_ISSET(_sock, &ex), FAILED); + + if (rdp && FD_ISSET(_sock, rdp)) + ready = true; + if (wrp && FD_ISSET(_sock, wrp)) + ready = true; + + return ready ? OK : ERR_BUSY; +#else + struct pollfd pfd; + pfd.fd = _sock; + pfd.events = POLLIN; + pfd.revents = 0; + + switch (p_type) { + case POLL_TYPE_IN: + pfd.events = POLLIN; + break; + case POLL_TYPE_OUT: + pfd.events = POLLOUT; + break; + case POLL_TYPE_IN_OUT: + pfd.events = POLLOUT || POLLIN; + } + + int ret = ::poll(&pfd, 1, p_timeout); + + ERR_FAIL_COND_V(ret < 0, FAILED); + ERR_FAIL_COND_V(pfd.revents & POLLERR, FAILED); + + if (ret == 0) + return ERR_BUSY; + + return OK; +#endif +} + +Error NetSocketPosix::recv(uint8_t *p_buffer, int p_len, int &r_read) { + ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); + + r_read = ::recv(_sock, SOCK_BUF(p_buffer), p_len, 0); + + if (r_read < 0) { + NetError err = _get_socket_error(); + if (err == ERR_NET_WOULD_BLOCK) + return ERR_BUSY; + + return FAILED; + } + + return OK; +} + +Error NetSocketPosix::recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IP_Address &r_ip, uint16_t &r_port) { + ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); + + struct sockaddr_storage from; + socklen_t len = sizeof(struct sockaddr_storage); + memset(&from, 0, len); + + r_read = ::recvfrom(_sock, SOCK_BUF(p_buffer), p_len, 0, (struct sockaddr *)&from, &len); + + if (r_read < 0) { + NetError err = _get_socket_error(); + if (err == ERR_NET_WOULD_BLOCK) + return ERR_BUSY; + + return FAILED; + } + + if (from.ss_family == AF_INET) { + struct sockaddr_in *sin_from = (struct sockaddr_in *)&from; + r_ip.set_ipv4((uint8_t *)&sin_from->sin_addr); + r_port = ntohs(sin_from->sin_port); + } else if (from.ss_family == AF_INET6) { + struct sockaddr_in6 *s6_from = (struct sockaddr_in6 *)&from; + r_ip.set_ipv6((uint8_t *)&s6_from->sin6_addr); + r_port = ntohs(s6_from->sin6_port); + } else { + // Unsupported socket family, should never happen. + ERR_FAIL_V(FAILED); + } + + return OK; +} + +Error NetSocketPosix::send(const uint8_t *p_buffer, int p_len, int &r_sent) { + ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); + + int flags = 0; + if (_is_stream) + flags = MSG_NOSIGNAL; + r_sent = ::send(_sock, SOCK_CBUF(p_buffer), p_len, flags); + + if (r_sent < 0) { + NetError err = _get_socket_error(); + if (err == ERR_NET_WOULD_BLOCK) + return ERR_BUSY; + + return FAILED; + } + + return OK; +} + +Error NetSocketPosix::sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IP_Address p_ip, uint16_t p_port) { + ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); + + struct sockaddr_storage addr; + size_t addr_size = _set_addr_storage(&addr, p_ip, p_port, _ip_type); + r_sent = ::sendto(_sock, SOCK_CBUF(p_buffer), p_len, 0, (struct sockaddr *)&addr, addr_size); + + if (r_sent < 0) { + NetError err = _get_socket_error(); + if (err == ERR_NET_WOULD_BLOCK) + return ERR_BUSY; + + return FAILED; + } + + return OK; +} + +void NetSocketPosix::set_broadcasting_enabled(bool p_enabled) { + ERR_FAIL_COND(!is_open()); + // IPv6 has no broadcast support. + ERR_FAIL_COND(_ip_type == IP::TYPE_IPV6); + + int par = p_enabled ? 1 : 0; + if (setsockopt(_sock, SOL_SOCKET, SO_BROADCAST, SOCK_CBUF(&par), sizeof(int)) != 0) { + WARN_PRINT("Unable to change broadcast setting"); + } +} + +void NetSocketPosix::set_blocking_enabled(bool p_enabled) { + ERR_FAIL_COND(!is_open()); + + int ret = 0; +#if defined(WINDOWS_ENABLED) || defined(NO_FCNTL) + unsigned long par = p_enabled ? 0 : 1; + ret = SOCK_IOCTL(_sock, FIONBIO, &par); +#else + int opts = fcntl(_sock, F_GETFL); + if (p_enabled) + ret = fcntl(_sock, F_SETFL, opts & ~O_NONBLOCK); + else + ret = fcntl(_sock, F_SETFL, opts | O_NONBLOCK); +#endif + + if (ret != 0) + WARN_PRINT("Unable to change non-block mode"); +} + +void NetSocketPosix::set_ipv6_only_enabled(bool p_enabled) { + ERR_FAIL_COND(!is_open()); + // This option is only avaiable in IPv6 sockets. + ERR_FAIL_COND(_ip_type == IP::TYPE_IPV4); + + int par = p_enabled ? 1 : 0; + if (setsockopt(_sock, IPPROTO_IPV6, IPV6_V6ONLY, SOCK_CBUF(&par), sizeof(int)) != 0) { + WARN_PRINT("Unable to change IPv4 address mapping over IPv6 option"); + } +} + +void NetSocketPosix::set_tcp_no_delay_enabled(bool p_enabled) { + ERR_FAIL_COND(!is_open()); + ERR_FAIL_COND(!_is_stream); // Not TCP + + int par = p_enabled ? 1 : 0; + if (setsockopt(_sock, IPPROTO_TCP, TCP_NODELAY, SOCK_CBUF(&par), sizeof(int)) < 0) { + ERR_PRINT("Unable to set TCP no delay option"); + } +} + +void NetSocketPosix::set_reuse_address_enabled(bool p_enabled) { + ERR_FAIL_COND(!is_open()); + + int par = p_enabled ? 1 : 0; + if (setsockopt(_sock, SOL_SOCKET, SO_REUSEADDR, SOCK_CBUF(&par), sizeof(int)) < 0) { + WARN_PRINT("Unable to set socket REUSEADDR option!"); + } +} + +void NetSocketPosix::set_reuse_port_enabled(bool p_enabled) { +// Windows does not have this option, as it is always ON when setting REUSEADDR. +#ifndef WINDOWS_ENABLED + ERR_FAIL_COND(!is_open()); + + int par = p_enabled ? 1 : 0; + if (setsockopt(_sock, SOL_SOCKET, SO_REUSEPORT, SOCK_CBUF(&par), sizeof(int)) < 0) { + WARN_PRINT("Unable to set socket REUSEPORT option!"); + } +#endif +} + +bool NetSocketPosix::is_open() const { + return _sock != SOCK_EMPTY; +} + +int NetSocketPosix::get_available_bytes() const { + + ERR_FAIL_COND_V(_sock == SOCK_EMPTY, -1); + + unsigned long len; + int ret = SOCK_IOCTL(_sock, FIONREAD, &len); + ERR_FAIL_COND_V(ret == -1, 0); + return len; +} + +Ref<NetSocket> NetSocketPosix::accept(IP_Address &r_ip, uint16_t &r_port) { + + Ref<NetSocket> out; + ERR_FAIL_COND_V(!is_open(), out); + + struct sockaddr_storage their_addr; + socklen_t size = sizeof(their_addr); + SOCKET_TYPE fd = ::accept(_sock, (struct sockaddr *)&their_addr, &size); + ERR_FAIL_COND_V(fd == SOCK_EMPTY, out); + + _set_ip_port(&their_addr, r_ip, r_port); + + NetSocketPosix *ns = memnew(NetSocketPosix); + ns->_set_socket(fd, _ip_type, _is_stream); + ns->set_blocking_enabled(false); + return Ref<NetSocket>(ns); +} diff --git a/drivers/windows/stream_peer_tcp_winsock.h b/drivers/unix/net_socket_posix.h index a0177d374e..010f2ea6e0 100644 --- a/drivers/windows/stream_peer_tcp_winsock.h +++ b/drivers/unix/net_socket_posix.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* stream_peer_tcp_winsock.h */ +/* net_socket_posix.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,65 +28,74 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef WINDOWS_ENABLED +#ifndef NET_SOCKET_UNIX_H +#define NET_SOCKET_UNIX_H -#ifndef STREAM_PEER_TCP_WINSOCK_H -#define STREAM_PEER_TCP_WINSOCK_H +#include "core/io/net_socket.h" -#include "error_list.h" +#if defined(WINDOWS_ENABLED) +#include <winsock2.h> +#include <ws2tcpip.h> +#define SOCKET_TYPE SOCKET -#include "core/io/ip_address.h" -#include "core/io/stream_peer_tcp.h" +#else +#include <sys/socket.h> +#define SOCKET_TYPE int -class StreamPeerTCPWinsock : public StreamPeerTCP { - -protected: - mutable Status status; - IP::Type sock_type; +#endif - int sockfd; +class NetSocketPosix : public NetSocket { - Error _block(int p_sockfd, bool p_read, bool p_write) const; +private: + SOCKET_TYPE _sock; + IP::Type _ip_type; + bool _is_stream; - Error _poll_connection() const; + enum NetError { + ERR_NET_WOULD_BLOCK, + ERR_NET_IS_CONNECTED, + ERR_NET_IN_PROGRESS, + ERR_NET_OTHER + }; - IP_Address peer_host; - int peer_port; + NetError _get_socket_error(); + void _set_socket(SOCKET_TYPE p_sock, IP::Type p_ip_type, bool p_is_stream); - Error write(const uint8_t *p_data, int p_bytes, int &r_sent, bool p_block); - Error read(uint8_t *p_buffer, int p_bytes, int &r_received, bool p_block); +protected: + static NetSocket *_create_func(); - static StreamPeerTCP *_create(); + bool _can_use_ip(const IP_Address p_ip, const bool p_for_bind) const; public: - virtual Error connect_to_host(const IP_Address &p_host, uint16_t p_port); - - virtual Error put_data(const uint8_t *p_data, int p_bytes); - virtual Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent); - - virtual Error get_data(uint8_t *p_buffer, int p_bytes); - virtual Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received); - - virtual int get_available_bytes() const; - - void set_socket(int p_sockfd, IP_Address p_host, int p_port, IP::Type p_sock_type); - - virtual IP_Address get_connected_host() const; - virtual uint16_t get_connected_port() const; - - virtual bool is_connected_to_host() const; - virtual Status get_status() const; - virtual void disconnect_from_host(); - static void make_default(); static void cleanup(); + static void _set_ip_port(struct sockaddr_storage *p_addr, IP_Address &r_ip, uint16_t &r_port); + static size_t _set_addr_storage(struct sockaddr_storage *p_addr, const IP_Address &p_ip, uint16_t p_port, IP::Type p_ip_type); + + virtual Error open(Type p_sock_type, IP::Type &ip_type); + virtual void close(); + virtual Error bind(IP_Address p_addr, uint16_t p_port); + virtual Error listen(int p_max_pending); + virtual Error connect_to_host(IP_Address p_addr, uint16_t p_port); + virtual Error poll(PollType p_type, int timeout) const; + virtual Error recv(uint8_t *p_buffer, int p_len, int &r_read); + virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IP_Address &r_ip, uint16_t &r_port); + virtual Error send(const uint8_t *p_buffer, int p_len, int &r_sent); + virtual Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IP_Address p_ip, uint16_t p_port); + virtual Ref<NetSocket> accept(IP_Address &r_ip, uint16_t &r_port); + + virtual bool is_open() const; + virtual int get_available_bytes() const; - virtual void set_no_delay(bool p_enabled); + virtual void set_broadcasting_enabled(bool p_enabled); + virtual void set_blocking_enabled(bool p_enabled); + virtual void set_ipv6_only_enabled(bool p_enabled); + virtual void set_tcp_no_delay_enabled(bool p_enabled); + virtual void set_reuse_address_enabled(bool p_enabled); + virtual void set_reuse_port_enabled(bool p_enabled); - StreamPeerTCPWinsock(); - ~StreamPeerTCPWinsock(); + NetSocketPosix(); + ~NetSocketPosix(); }; -#endif // STREAM_PEER_TCP_WINSOCK_H - #endif diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp index 8aab4cb521..279274734f 100644 --- a/drivers/unix/os_unix.cpp +++ b/drivers/unix/os_unix.cpp @@ -32,30 +32,27 @@ #ifdef UNIX_ENABLED -#include "servers/visual_server.h" - #include "core/os/thread_dummy.h" -#include "mutex_posix.h" -#include "rw_lock_posix.h" -#include "semaphore_posix.h" -#include "thread_posix.h" - -//#include "core/io/file_access_buffered_fa.h" -#include "dir_access_unix.h" -#include "file_access_unix.h" -#include "packet_peer_udp_posix.h" -#include "stream_peer_tcp_posix.h" -#include "tcp_server_posix.h" +#include "core/project_settings.h" +#include "drivers/unix/dir_access_unix.h" +#include "drivers/unix/file_access_unix.h" +#include "drivers/unix/mutex_posix.h" +#include "drivers/unix/net_socket_posix.h" +#include "drivers/unix/rw_lock_posix.h" +#include "drivers/unix/semaphore_posix.h" +#include "drivers/unix/thread_posix.h" +#include "servers/visual_server.h" #ifdef __APPLE__ #include <mach-o/dyld.h> +#include <mach/mach_time.h> #endif #if defined(__FreeBSD__) || defined(__OpenBSD__) #include <sys/param.h> #include <sys/sysctl.h> #endif -#include "project_settings.h" + #include <assert.h> #include <dlfcn.h> #include <errno.h> @@ -68,6 +65,32 @@ #include <sys/wait.h> #include <unistd.h> +/// Clock Setup function (used by get_ticks_usec) +static uint64_t _clock_start = 0; +#if defined(__APPLE__) +static double _clock_scale = 0; +static void _setup_clock() { + mach_timebase_info_data_t info; + kern_return_t ret = mach_timebase_info(&info); + ERR_EXPLAIN("OS CLOCK IS NOT WORKING!"); + ERR_FAIL_COND(ret != 0); + _clock_scale = ((double)info.numer / (double)info.denom) / 1000.0; + _clock_start = mach_absolute_time() * _clock_scale; +} +#else +#if defined(CLOCK_MONOTONIC_RAW) && !defined(JAVASCRIPT_ENABLED) // This is a better clock on Linux. +#define GODOT_CLOCK CLOCK_MONOTONIC_RAW +#else +#define GODOT_CLOCK CLOCK_MONOTONIC +#endif +static void _setup_clock() { + struct timespec tv_now = { 0, 0 }; + ERR_EXPLAIN("OS CLOCK IS NOT WORKING!"); + ERR_FAIL_COND(clock_gettime(GODOT_CLOCK, &tv_now) != 0); + _clock_start = ((uint64_t)tv_now.tv_nsec / 1000L) + (uint64_t)tv_now.tv_sec * 1000000L; +} +#endif + void OS_Unix::debug_break() { assert(false); @@ -126,14 +149,11 @@ void OS_Unix::initialize_core() { DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_FILESYSTEM); #ifndef NO_NETWORK - TCPServerPosix::make_default(); - StreamPeerTCPPosix::make_default(); - PacketPeerUDPPosix::make_default(); + NetSocketPosix::make_default(); IP_Unix::make_default(); #endif - ticks_start = 0; - ticks_start = get_ticks_usec(); + _setup_clock(); struct sigaction sa; sa.sa_handler = &handle_sigchld; @@ -145,6 +165,8 @@ void OS_Unix::initialize_core() { } void OS_Unix::finalize_core() { + + NetSocketPosix::cleanup(); } void OS_Unix::alert(const String &p_alert, const String &p_title) { @@ -250,17 +272,27 @@ void OS_Unix::delay_usec(uint32_t p_usec) const { } uint64_t OS_Unix::get_ticks_usec() const { - struct timeval tv_now; - gettimeofday(&tv_now, NULL); - - uint64_t longtime = (uint64_t)tv_now.tv_usec + (uint64_t)tv_now.tv_sec * 1000000L; - longtime -= ticks_start; +#if defined(__APPLE__) + uint64_t longtime = mach_absolute_time() * _clock_scale; +#else + // Unchecked return. Static analyzers might complain. + // If _setup_clock() succeded, we assume clock_gettime() works. + struct timespec tv_now = { 0, 0 }; + clock_gettime(GODOT_CLOCK, &tv_now); + uint64_t longtime = ((uint64_t)tv_now.tv_nsec / 1000L) + (uint64_t)tv_now.tv_sec * 1000000L; +#endif + longtime -= _clock_start; return longtime; } Error OS_Unix::execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode, bool read_stderr) { +#ifdef __EMSCRIPTEN__ + // Don't compile this code at all to avoid undefined references. + // Actual virtual call goes to OS_JavaScript. + ERR_FAIL_V(ERR_BUG); +#else if (p_blocking && r_pipe) { String argss; @@ -327,9 +359,10 @@ Error OS_Unix::execute(const String &p_path, const List<String> &p_arguments, bo } return OK; +#endif } -Error OS_Unix::kill(const ProcessID &p_pid, const int p_max_wait_msec) { +Error OS_Unix::kill(const ProcessID &p_pid) { int ret = ::kill(p_pid, SIGKILL); if (!ret) { @@ -460,9 +493,11 @@ String OS_Unix::get_executable_path() const { //fix for running from a symlink char buf[256]; memset(buf, 0, 256); - readlink("/proc/self/exe", buf, sizeof(buf)); + ssize_t len = readlink("/proc/self/exe", buf, sizeof(buf)); String b; - b.parse_utf8(buf); + if (len > 0) { + b.parse_utf8(buf, len); + } if (b == "") { WARN_PRINT("Couldn't get executable path from /proc/self/exe, using argv[0]"); return OS::get_executable_path(); diff --git a/drivers/unix/os_unix.h b/drivers/unix/os_unix.h index c5240231fa..b702454603 100644 --- a/drivers/unix/os_unix.h +++ b/drivers/unix/os_unix.h @@ -37,13 +37,11 @@ #ifdef UNIX_ENABLED +#include "core/os/os.h" #include "drivers/unix/ip_unix.h" -#include "os/os.h" class OS_Unix : public OS { - uint64_t ticks_start; - protected: // UNIX only handles the core functions. // inheriting platforms under unix (eg. X11) should handle the rest @@ -91,7 +89,7 @@ public: virtual uint64_t get_ticks_usec() const; virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false); - virtual Error kill(const ProcessID &p_pid, const int p_max_wait_msec = -1); + virtual Error kill(const ProcessID &p_pid); virtual int get_process_id() const; virtual bool has_environment(const String &p_var) const; diff --git a/drivers/unix/packet_peer_udp_posix.cpp b/drivers/unix/packet_peer_udp_posix.cpp deleted file mode 100644 index 1380c1d88b..0000000000 --- a/drivers/unix/packet_peer_udp_posix.cpp +++ /dev/null @@ -1,317 +0,0 @@ -/*************************************************************************/ -/* packet_peer_udp_posix.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 "packet_peer_udp_posix.h" - -#ifdef UNIX_ENABLED - -#include <errno.h> -#include <netdb.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <unistd.h> - -#include <netinet/in.h> -#include <stdio.h> - -#ifndef NO_FCNTL -#ifdef __HAIKU__ -#include <fcntl.h> -#else -#include <sys/fcntl.h> -#endif -#else -#include <sys/ioctl.h> -#endif - -#ifdef JAVASCRIPT_ENABLED -#include <arpa/inet.h> -#endif - -#include "drivers/unix/socket_helpers.h" - -int PacketPeerUDPPosix::get_available_packet_count() const { - - Error err = const_cast<PacketPeerUDPPosix *>(this)->_poll(false); - if (err != OK) - return 0; - - return queue_count; -} - -Error PacketPeerUDPPosix::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { - - Error err = const_cast<PacketPeerUDPPosix *>(this)->_poll(false); - if (err != OK) - return err; - if (queue_count == 0) - return ERR_UNAVAILABLE; - - uint32_t size = 0; - uint8_t type = IP::TYPE_NONE; - rb.read(&type, 1, true); - if (type == IP::TYPE_IPV4) { - uint8_t ip[4]; - rb.read(ip, 4, true); - packet_ip.set_ipv4(ip); - } else { - uint8_t ipv6[16]; - rb.read(ipv6, 16, true); - packet_ip.set_ipv6(ipv6); - }; - rb.read((uint8_t *)&packet_port, 4, true); - rb.read((uint8_t *)&size, 4, true); - rb.read(packet_buffer, size, true); - --queue_count; - *r_buffer = packet_buffer; - r_buffer_size = size; - return OK; -} -Error PacketPeerUDPPosix::put_packet(const uint8_t *p_buffer, int p_buffer_size) { - - ERR_FAIL_COND_V(!peer_addr.is_valid(), ERR_UNCONFIGURED); - - if (sock_type == IP::TYPE_NONE) - sock_type = peer_addr.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; - - int sock = _get_socket(); - ERR_FAIL_COND_V(sock == -1, FAILED); - struct sockaddr_storage addr; - size_t addr_size = _set_sockaddr(&addr, peer_addr, peer_port, sock_type); - - errno = 0; - int err; - - _set_sock_blocking(blocking); - - while ((err = sendto(sock, p_buffer, p_buffer_size, 0, (struct sockaddr *)&addr, addr_size)) != p_buffer_size) { - - if (errno != EAGAIN) { - return FAILED; - } else if (!blocking) { - return ERR_UNAVAILABLE; - } - } - - return OK; -} - -int PacketPeerUDPPosix::get_max_packet_size() const { - - return 512; // uhm maybe not -} - -Error PacketPeerUDPPosix::listen(int p_port, const IP_Address &p_bind_address, int p_recv_buffer_size) { - - ERR_FAIL_COND_V(sockfd != -1, ERR_ALREADY_IN_USE); - ERR_FAIL_COND_V(!p_bind_address.is_valid() && !p_bind_address.is_wildcard(), ERR_INVALID_PARAMETER); - -#ifdef __OpenBSD__ - sock_type = IP::TYPE_IPV4; // OpenBSD does not support dual stacking, fallback to IPv4 only. -#else - sock_type = IP::TYPE_ANY; -#endif - - if (p_bind_address.is_valid()) - sock_type = p_bind_address.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; - - int sock = _get_socket(); - - if (sock == -1) - return ERR_CANT_CREATE; - - sockaddr_storage addr = { 0 }; - size_t addr_size = _set_listen_sockaddr(&addr, p_port, sock_type, IP_Address()); - - if (bind(sock, (struct sockaddr *)&addr, addr_size) == -1) { - close(); - return ERR_UNAVAILABLE; - } - rb.resize(nearest_shift(p_recv_buffer_size)); - return OK; -} - -void PacketPeerUDPPosix::close() { - - if (sockfd != -1) - ::close(sockfd); - sockfd = -1; - sock_type = IP::TYPE_NONE; - rb.resize(16); - queue_count = 0; -} - -Error PacketPeerUDPPosix::wait() { - - return _poll(true); -} - -Error PacketPeerUDPPosix::_poll(bool p_block) { - - if (sockfd == -1) { - return FAILED; - } - - _set_sock_blocking(p_block); - - struct sockaddr_storage from = { 0 }; - socklen_t len = sizeof(struct sockaddr_storage); - int ret; - while ((ret = recvfrom(sockfd, recv_buffer, MIN((int)sizeof(recv_buffer), MAX(rb.space_left() - 24, 0)), 0, (struct sockaddr *)&from, &len)) > 0) { - - uint32_t port = 0; - - if (from.ss_family == AF_INET) { - uint8_t type = (uint8_t)IP::TYPE_IPV4; - rb.write(&type, 1); - struct sockaddr_in *sin_from = (struct sockaddr_in *)&from; - rb.write((uint8_t *)&sin_from->sin_addr, 4); - port = ntohs(sin_from->sin_port); - - } else if (from.ss_family == AF_INET6) { - - uint8_t type = (uint8_t)IP::TYPE_IPV6; - rb.write(&type, 1); - - struct sockaddr_in6 *s6_from = (struct sockaddr_in6 *)&from; - rb.write((uint8_t *)&s6_from->sin6_addr, 16); - - port = ntohs(s6_from->sin6_port); - - } else { - // WARN_PRINT("Ignoring packet with unknown address family"); - uint8_t type = (uint8_t)IP::TYPE_NONE; - rb.write(&type, 1); - }; - - rb.write((uint8_t *)&port, 4); - rb.write((uint8_t *)&ret, 4); - rb.write(recv_buffer, ret); - - len = sizeof(struct sockaddr_storage); - ++queue_count; - if (p_block) - break; - }; - - // TODO: Should ECONNRESET be handled here? - if (ret == 0 || (ret == -1 && errno != EAGAIN)) { - close(); - return FAILED; - }; - - return OK; -} -bool PacketPeerUDPPosix::is_listening() const { - - return sockfd != -1; -} - -IP_Address PacketPeerUDPPosix::get_packet_address() const { - - return packet_ip; -} - -int PacketPeerUDPPosix::get_packet_port() const { - - return packet_port; -} - -int PacketPeerUDPPosix::_get_socket() { - - ERR_FAIL_COND_V(sock_type == IP::TYPE_NONE, -1); - - if (sockfd != -1) - return sockfd; - - sockfd = _socket_create(sock_type, SOCK_DGRAM, IPPROTO_UDP); - - if (sockfd != -1) - _set_sock_blocking(false); - - return sockfd; -} - -void PacketPeerUDPPosix::_set_sock_blocking(bool p_blocking) { - - if (sock_blocking == p_blocking) - return; - - sock_blocking = p_blocking; - -#ifndef NO_FCNTL - int opts = fcntl(sockfd, F_GETFL); - int ret = 0; - if (sock_blocking) - ret = fcntl(sockfd, F_SETFL, opts & ~O_NONBLOCK); - else - ret = fcntl(sockfd, F_SETFL, opts | O_NONBLOCK); - if (ret == -1) - perror("setting non-block mode"); -#else - int bval = sock_blocking ? 0 : 1; - if (ioctl(sockfd, FIONBIO, &bval) == -1) - perror("setting non-block mode"); -#endif -} - -void PacketPeerUDPPosix::set_dest_address(const IP_Address &p_address, int p_port) { - - peer_addr = p_address; - peer_port = p_port; -} - -PacketPeerUDP *PacketPeerUDPPosix::_create() { - - return memnew(PacketPeerUDPPosix); -}; - -void PacketPeerUDPPosix::make_default() { - - PacketPeerUDP::_create = PacketPeerUDPPosix::_create; -}; - -PacketPeerUDPPosix::PacketPeerUDPPosix() { - - blocking = true; - sock_blocking = true; - sockfd = -1; - packet_port = 0; - queue_count = 0; - peer_port = 0; - sock_type = IP::TYPE_NONE; - rb.resize(16); -} - -PacketPeerUDPPosix::~PacketPeerUDPPosix() { - - close(); -} -#endif diff --git a/drivers/unix/rw_lock_posix.cpp b/drivers/unix/rw_lock_posix.cpp index 4df965cabb..27b19c30d5 100644 --- a/drivers/unix/rw_lock_posix.cpp +++ b/drivers/unix/rw_lock_posix.cpp @@ -32,8 +32,8 @@ #include "rw_lock_posix.h" -#include "error_macros.h" -#include "os/memory.h" +#include "core/error_macros.h" +#include "core/os/memory.h" #include <stdio.h> void RWLockPosix::read_lock() { diff --git a/drivers/unix/rw_lock_posix.h b/drivers/unix/rw_lock_posix.h index 617b9705da..897b617f98 100644 --- a/drivers/unix/rw_lock_posix.h +++ b/drivers/unix/rw_lock_posix.h @@ -33,7 +33,7 @@ #if defined(UNIX_ENABLED) || defined(PTHREAD_ENABLED) -#include "os/rw_lock.h" +#include "core/os/rw_lock.h" #include <pthread.h> class RWLockPosix : public RWLock { diff --git a/drivers/unix/semaphore_posix.cpp b/drivers/unix/semaphore_posix.cpp index 5cabfe4937..26c2aeab28 100644 --- a/drivers/unix/semaphore_posix.cpp +++ b/drivers/unix/semaphore_posix.cpp @@ -32,7 +32,7 @@ #if defined(UNIX_ENABLED) || defined(PTHREAD_ENABLED) -#include "os/memory.h" +#include "core/os/memory.h" #include <errno.h> #include <stdio.h> diff --git a/drivers/unix/semaphore_posix.h b/drivers/unix/semaphore_posix.h index 283174bb2e..025b87c0d7 100644 --- a/drivers/unix/semaphore_posix.h +++ b/drivers/unix/semaphore_posix.h @@ -31,7 +31,7 @@ #ifndef SEMAPHORE_POSIX_H #define SEMAPHORE_POSIX_H -#include "os/semaphore.h" +#include "core/os/semaphore.h" #if defined(UNIX_ENABLED) || defined(PTHREAD_ENABLED) diff --git a/drivers/unix/socket_helpers.h b/drivers/unix/socket_helpers.h deleted file mode 100644 index 5b42c13eae..0000000000 --- a/drivers/unix/socket_helpers.h +++ /dev/null @@ -1,156 +0,0 @@ -/*************************************************************************/ -/* socket_helpers.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 SOCKET_HELPERS_H -#define SOCKET_HELPERS_H - -#include <string.h> - -#if defined(__MINGW32__) && (!defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 4) -// Workaround for mingw-w64 < 4.0 -#ifndef IPV6_V6ONLY -#define IPV6_V6ONLY 27 -#endif -#endif - -// helpers for sockaddr -> IP_Address and back, should work for posix and winsock. All implementations should use this - -static size_t _set_sockaddr(struct sockaddr_storage *p_addr, const IP_Address &p_ip, int p_port, IP::Type p_sock_type = IP::TYPE_ANY) { - - memset(p_addr, 0, sizeof(struct sockaddr_storage)); - - ERR_FAIL_COND_V(!p_ip.is_valid(), 0); - - // IPv6 socket - if (p_sock_type == IP::TYPE_IPV6 || p_sock_type == IP::TYPE_ANY) { - - // IPv6 only socket with IPv4 address - ERR_FAIL_COND_V(p_sock_type == IP::TYPE_IPV6 && p_ip.is_ipv4(), 0); - - struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)p_addr; - addr6->sin6_family = AF_INET6; - addr6->sin6_port = htons(p_port); - copymem(&addr6->sin6_addr.s6_addr, p_ip.get_ipv6(), 16); - return sizeof(sockaddr_in6); - - } else { // IPv4 socket - - // IPv4 socket with IPv6 address - ERR_FAIL_COND_V(!p_ip.is_ipv4(), 0); - - struct sockaddr_in *addr4 = (struct sockaddr_in *)p_addr; - addr4->sin_family = AF_INET; - addr4->sin_port = htons(p_port); // short, network byte order - copymem(&addr4->sin_addr.s_addr, p_ip.get_ipv4(), 16); - return sizeof(sockaddr_in); - }; -}; - -static size_t _set_listen_sockaddr(struct sockaddr_storage *p_addr, int p_port, IP::Type p_sock_type, const IP_Address p_bind_address) { - - memset(p_addr, 0, sizeof(struct sockaddr_storage)); - if (p_sock_type == IP::TYPE_IPV4) { - struct sockaddr_in *addr4 = (struct sockaddr_in *)p_addr; - addr4->sin_family = AF_INET; - addr4->sin_port = htons(p_port); - if (p_bind_address.is_valid()) { - copymem(&addr4->sin_addr.s_addr, p_bind_address.get_ipv4(), 4); - } else { - addr4->sin_addr.s_addr = INADDR_ANY; - } - return sizeof(sockaddr_in); - } else { - struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)p_addr; - - addr6->sin6_family = AF_INET6; - addr6->sin6_port = htons(p_port); - if (p_bind_address.is_valid()) { - copymem(&addr6->sin6_addr.s6_addr, p_bind_address.get_ipv6(), 16); - } else { - addr6->sin6_addr = in6addr_any; - } - return sizeof(sockaddr_in6); - }; -}; - -static int _socket_create(IP::Type &p_type, int type, int protocol) { - - ERR_FAIL_COND_V(p_type > IP::TYPE_ANY || p_type < IP::TYPE_NONE, ERR_INVALID_PARAMETER); - - int family = p_type == IP::TYPE_IPV4 ? AF_INET : AF_INET6; - int sockfd = socket(family, type, protocol); - - if (sockfd == -1 && p_type == IP::TYPE_ANY) { - // Careful here, changing the referenced parameter so the caller knows that we are using an IPv4 socket - // in place of a dual stack one, and further calls to _set_sock_addr will work as expected. - p_type = IP::TYPE_IPV4; - family = AF_INET; - sockfd = socket(family, type, protocol); - } - - ERR_FAIL_COND_V(sockfd == -1, -1); - - if (family == AF_INET6) { - // Select IPv4 over IPv6 mapping - int opt = p_type != IP::TYPE_ANY; - if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&opt, sizeof(opt)) != 0) { - WARN_PRINT("Unable to set/unset IPv4 address mapping over IPv6"); - } - } - if (protocol == IPPROTO_UDP && p_type != IP::TYPE_IPV6) { - // Enable broadcasting for UDP sockets if it's not IPv6 only (IPv6 has no broadcast option). - int broadcast = 1; - if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, (char *)&broadcast, sizeof(broadcast)) != 0) { - WARN_PRINT("Error when enabling broadcasting"); - } - } - - return sockfd; -} - -static void _set_ip_addr_port(IP_Address &r_ip, int &r_port, struct sockaddr_storage *p_addr) { - - if (p_addr->ss_family == AF_INET) { - - struct sockaddr_in *addr4 = (struct sockaddr_in *)p_addr; - r_ip.set_ipv4((uint8_t *)&(addr4->sin_addr.s_addr)); - - r_port = ntohs(addr4->sin_port); - - } else if (p_addr->ss_family == AF_INET6) { - - struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)p_addr; - r_ip.set_ipv6(addr6->sin6_addr.s6_addr); - - r_port = ntohs(addr6->sin6_port); - }; -}; - -#endif diff --git a/drivers/unix/stream_peer_tcp_posix.cpp b/drivers/unix/stream_peer_tcp_posix.cpp deleted file mode 100644 index 44b9ef1d7c..0000000000 --- a/drivers/unix/stream_peer_tcp_posix.cpp +++ /dev/null @@ -1,411 +0,0 @@ -/*************************************************************************/ -/* stream_peer_tcp_posix.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 UNIX_ENABLED - -#include "stream_peer_tcp_posix.h" - -#include <errno.h> -#include <netdb.h> -#include <poll.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/ioctl.h> -#include <sys/types.h> -#include <unistd.h> -#ifndef NO_FCNTL -#ifdef __HAIKU__ -#include <fcntl.h> -#else -#include <sys/fcntl.h> -#endif -#else -#include <sys/ioctl.h> -#endif -#include <netinet/in.h> - -#include <sys/socket.h> -#ifdef JAVASCRIPT_ENABLED -#include <arpa/inet.h> -#endif - -#include <netinet/tcp.h> - -#if defined(OSX_ENABLED) || defined(IPHONE_ENABLED) -#define MSG_NOSIGNAL SO_NOSIGPIPE -#endif - -#include "drivers/unix/socket_helpers.h" - -StreamPeerTCP *StreamPeerTCPPosix::_create() { - - return memnew(StreamPeerTCPPosix); -}; - -void StreamPeerTCPPosix::make_default() { - - StreamPeerTCP::_create = StreamPeerTCPPosix::_create; -}; - -Error StreamPeerTCPPosix::_block(int p_sockfd, bool p_read, bool p_write) const { - - struct pollfd pfd; - pfd.fd = p_sockfd; - pfd.events = 0; - if (p_read) - pfd.events |= POLLIN; - if (p_write) - pfd.events |= POLLOUT; - pfd.revents = 0; - - int ret = poll(&pfd, 1, -1); - return ret < 0 ? FAILED : OK; -}; - -Error StreamPeerTCPPosix::_poll_connection() const { - - ERR_FAIL_COND_V(status != STATUS_CONNECTING || sockfd == -1, FAILED); - - struct sockaddr_storage their_addr; - size_t addr_size = _set_sockaddr(&their_addr, peer_host, peer_port, sock_type); - - if (::connect(sockfd, (struct sockaddr *)&their_addr, addr_size) == -1) { - - if (errno == EISCONN) { - status = STATUS_CONNECTED; - return OK; - }; - - if (errno == EINPROGRESS || errno == EALREADY) { - return OK; - } - - status = STATUS_ERROR; - return ERR_CONNECTION_ERROR; - } else { - - status = STATUS_CONNECTED; - return OK; - }; - - return OK; -}; - -void StreamPeerTCPPosix::set_socket(int p_sockfd, IP_Address p_host, int p_port, IP::Type p_sock_type) { - - sock_type = p_sock_type; - sockfd = p_sockfd; -#ifndef NO_FCNTL - if (fcntl(sockfd, F_SETFL, O_NONBLOCK) < 0) { - WARN_PRINT("Error setting socket as non blocking"); - } -#else - int bval = 1; - if (ioctl(sockfd, FIONBIO, &bval) < 0) { - WARN_PRINT("Error setting socket as non blocking"); - } -#endif - status = STATUS_CONNECTING; - - peer_host = p_host; - peer_port = p_port; -}; - -Error StreamPeerTCPPosix::connect_to_host(const IP_Address &p_host, uint16_t p_port) { - - ERR_FAIL_COND_V(!p_host.is_valid(), ERR_INVALID_PARAMETER); - - sock_type = p_host.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; - sockfd = _socket_create(sock_type, SOCK_STREAM, IPPROTO_TCP); - if (sockfd == -1) { - ERR_PRINT("Socket creation failed!"); - disconnect_from_host(); - //perror("socket"); - return FAILED; - }; - -#ifndef NO_FCNTL - if (fcntl(sockfd, F_SETFL, O_NONBLOCK) < 0) { - WARN_PRINT("Error setting socket as non blocking"); - } -#else - int bval = 1; - if (ioctl(sockfd, FIONBIO, &bval) < 0) { - WARN_PRINT("Error setting socket as non blocking"); - } -#endif - - struct sockaddr_storage their_addr; - size_t addr_size = _set_sockaddr(&their_addr, p_host, p_port, sock_type); - - errno = 0; - if (::connect(sockfd, (struct sockaddr *)&their_addr, addr_size) == -1 && errno != EINPROGRESS) { - - ERR_PRINT("Connection to remote host failed!"); - disconnect_from_host(); - return FAILED; - }; - - if (errno == EINPROGRESS) { - status = STATUS_CONNECTING; - } else { - status = STATUS_CONNECTED; - }; - - peer_host = p_host; - peer_port = p_port; - - return OK; -}; - -Error StreamPeerTCPPosix::write(const uint8_t *p_data, int p_bytes, int &r_sent, bool p_block) { - - if (status == STATUS_NONE || status == STATUS_ERROR) { - - return FAILED; - }; - - if (status != STATUS_CONNECTED) { - - if (_poll_connection() != OK) { - - return FAILED; - }; - - if (status != STATUS_CONNECTED) { - r_sent = 0; - return OK; - }; - }; - - int data_to_send = p_bytes; - const uint8_t *offset = p_data; - if (sockfd == -1) return FAILED; - errno = 0; - int total_sent = 0; - - while (data_to_send) { - - int sent_amount = send(sockfd, offset, data_to_send, MSG_NOSIGNAL); - //printf("Sent TCP data of %d bytes, errno %d\n", sent_amount, errno); - - if (sent_amount == -1) { - - if (errno != EAGAIN) { - - perror("Nothing sent"); - disconnect_from_host(); - ERR_PRINT("Server disconnected!\n"); - return FAILED; - }; - - if (!p_block) { - r_sent = total_sent; - return OK; - }; - - _block(sockfd, false, true); - } else { - - data_to_send -= sent_amount; - offset += sent_amount; - total_sent += sent_amount; - }; - } - - r_sent = total_sent; - - return OK; -}; - -Error StreamPeerTCPPosix::read(uint8_t *p_buffer, int p_bytes, int &r_received, bool p_block) { - - if (!is_connected_to_host()) { - - return FAILED; - }; - - if (status == STATUS_CONNECTING) { - - if (_poll_connection() != OK) { - - return FAILED; - }; - - if (status != STATUS_CONNECTED) { - r_received = 0; - return OK; - }; - }; - - int to_read = p_bytes; - int total_read = 0; - errno = 0; - - while (to_read) { - - int read = recv(sockfd, p_buffer + total_read, to_read, 0); - - if (read == -1) { - - if (errno != EAGAIN) { - - perror("Nothing read"); - disconnect_from_host(); - ERR_PRINT("Server disconnected!\n"); - return FAILED; - }; - - if (!p_block) { - - r_received = total_read; - return OK; - }; - _block(sockfd, true, false); - - } else if (read == 0) { - - sockfd = -1; - status = STATUS_NONE; - peer_port = 0; - peer_host = IP_Address(); - r_received = total_read; - return ERR_FILE_EOF; - - } else { - - to_read -= read; - total_read += read; - }; - }; - - r_received = total_read; - - return OK; -}; - -void StreamPeerTCPPosix::set_no_delay(bool p_enabled) { - - ERR_FAIL_COND(!is_connected_to_host()); - int flag = p_enabled ? 1 : 0; - if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int)) < 0) { - ERR_PRINT("Unable to set TCP no delay option"); - } -} - -bool StreamPeerTCPPosix::is_connected_to_host() const { - - if (status == STATUS_NONE || status == STATUS_ERROR) { - - return false; - }; - if (status != STATUS_CONNECTED) { - return true; - }; - - return (sockfd != -1); -}; - -StreamPeerTCP::Status StreamPeerTCPPosix::get_status() const { - - if (status == STATUS_CONNECTING) { - _poll_connection(); - }; - - return status; -}; - -void StreamPeerTCPPosix::disconnect_from_host() { - - if (sockfd != -1) - close(sockfd); - - sock_type = IP::TYPE_NONE; - sockfd = -1; - - status = STATUS_NONE; - peer_port = 0; - peer_host = IP_Address(); -}; - -Error StreamPeerTCPPosix::put_data(const uint8_t *p_data, int p_bytes) { - - int total; - return write(p_data, p_bytes, total, true); -}; - -Error StreamPeerTCPPosix::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) { - - return write(p_data, p_bytes, r_sent, false); -}; - -Error StreamPeerTCPPosix::get_data(uint8_t *p_buffer, int p_bytes) { - - int total; - return read(p_buffer, p_bytes, total, true); -}; - -Error StreamPeerTCPPosix::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) { - - return read(p_buffer, p_bytes, r_received, false); -}; - -int StreamPeerTCPPosix::get_available_bytes() const { - - unsigned long len; - int ret = ioctl(sockfd, FIONREAD, &len); - ERR_FAIL_COND_V(ret == -1, 0) - return len; -} -IP_Address StreamPeerTCPPosix::get_connected_host() const { - - return peer_host; -}; - -uint16_t StreamPeerTCPPosix::get_connected_port() const { - - return peer_port; -}; - -StreamPeerTCPPosix::StreamPeerTCPPosix() { - - sock_type = IP::TYPE_NONE; - sockfd = -1; - status = STATUS_NONE; - peer_port = 0; -}; - -StreamPeerTCPPosix::~StreamPeerTCPPosix() { - - disconnect_from_host(); -}; - -#endif diff --git a/drivers/unix/syslog_logger.cpp b/drivers/unix/syslog_logger.cpp index 727672458c..c7b4daf4ad 100644 --- a/drivers/unix/syslog_logger.cpp +++ b/drivers/unix/syslog_logger.cpp @@ -31,7 +31,7 @@ #ifdef UNIX_ENABLED #include "syslog_logger.h" -#include "print_string.h" +#include "core/print_string.h" #include <syslog.h> void SyslogLogger::logv(const char *p_format, va_list p_list, bool p_err) { diff --git a/drivers/unix/syslog_logger.h b/drivers/unix/syslog_logger.h index 40bf26cee1..745264ab6f 100644 --- a/drivers/unix/syslog_logger.h +++ b/drivers/unix/syslog_logger.h @@ -33,7 +33,7 @@ #ifdef UNIX_ENABLED -#include "io/logger.h" +#include "core/io/logger.h" class SyslogLogger : public Logger { public: @@ -45,4 +45,4 @@ public: #endif -#endif
\ No newline at end of file +#endif diff --git a/drivers/unix/tcp_server_posix.cpp b/drivers/unix/tcp_server_posix.cpp deleted file mode 100644 index 67ab981f46..0000000000 --- a/drivers/unix/tcp_server_posix.cpp +++ /dev/null @@ -1,207 +0,0 @@ -/*************************************************************************/ -/* tcp_server_posix.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 "tcp_server_posix.h" -#include "stream_peer_tcp_posix.h" - -#ifdef UNIX_ENABLED - -#include <poll.h> - -#include <errno.h> -#include <netdb.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> -#include <unistd.h> -#ifndef NO_FCNTL -#ifdef __HAIKU__ -#include <fcntl.h> -#else -#include <sys/fcntl.h> -#endif -#else -#include <sys/ioctl.h> -#endif -#ifdef JAVASCRIPT_ENABLED -#include <arpa/inet.h> -#endif -#include <assert.h> -#include <netinet/in.h> -#include <sys/socket.h> - -#include "drivers/unix/socket_helpers.h" - -TCP_Server *TCPServerPosix::_create() { - - return memnew(TCPServerPosix); -}; - -void TCPServerPosix::make_default() { - - TCP_Server::_create = TCPServerPosix::_create; -}; - -Error TCPServerPosix::listen(uint16_t p_port, const IP_Address &p_bind_address) { - - ERR_FAIL_COND_V(listen_sockfd != -1, ERR_ALREADY_IN_USE); - ERR_FAIL_COND_V(!p_bind_address.is_valid() && !p_bind_address.is_wildcard(), ERR_INVALID_PARAMETER); - - int sockfd; -#ifdef __OpenBSD__ - sock_type = IP::TYPE_IPV4; // OpenBSD does not support dual stacking, fallback to IPv4 only. -#else - sock_type = IP::TYPE_ANY; -#endif - - // If the bind address is valid use its type as the socket type - if (p_bind_address.is_valid()) - sock_type = p_bind_address.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; - - sockfd = _socket_create(sock_type, SOCK_STREAM, IPPROTO_TCP); - - ERR_FAIL_COND_V(sockfd == -1, FAILED); - -#ifndef NO_FCNTL - if (fcntl(sockfd, F_SETFL, O_NONBLOCK) < 0) { - WARN_PRINT("Error setting socket as non blocking"); - } -#else - int bval = 1; - if (ioctl(sockfd, FIONBIO, &bval) < 0) { - WARN_PRINT("Error setting socket as non blocking"); - } -#endif - - int reuse = 1; - if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) < 0) { - WARN_PRINT("REUSEADDR failed!") - } - - struct sockaddr_storage addr; - size_t addr_size = _set_listen_sockaddr(&addr, p_port, sock_type, p_bind_address); - - if (bind(sockfd, (struct sockaddr *)&addr, addr_size) != -1) { - - if (::listen(sockfd, 1) == -1) { - - close(sockfd); - ERR_FAIL_V(FAILED); - }; - } else { - close(sockfd); - return ERR_ALREADY_IN_USE; - }; - - if (listen_sockfd != -1) { - stop(); - }; - - listen_sockfd = sockfd; - - return OK; -}; - -bool TCPServerPosix::is_connection_available() const { - - if (listen_sockfd == -1) { - return false; - }; - - struct pollfd pfd; - pfd.fd = listen_sockfd; - pfd.events = POLLIN; - pfd.revents = 0; - - int ret = poll(&pfd, 1, 0); - ERR_FAIL_COND_V(ret < 0, FAILED); - - if (ret && (pfd.revents & POLLIN)) { - return true; - }; - - return false; -}; - -Ref<StreamPeerTCP> TCPServerPosix::take_connection() { - - if (!is_connection_available()) { - return Ref<StreamPeerTCP>(); - }; - - struct sockaddr_storage their_addr; - socklen_t size = sizeof(their_addr); - int fd = accept(listen_sockfd, (struct sockaddr *)&their_addr, &size); - ERR_FAIL_COND_V(fd == -1, Ref<StreamPeerTCP>()); -#ifndef NO_FCNTL - if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { - WARN_PRINT("Error setting socket as non blocking"); - } -#else - int bval = 1; - if (ioctl(fd, FIONBIO, &bval) < 0) { - WARN_PRINT("Error setting socket as non blocking"); - } -#endif - - Ref<StreamPeerTCPPosix> conn = memnew(StreamPeerTCPPosix); - IP_Address ip; - - int port = 0; - _set_ip_addr_port(ip, port, &their_addr); - - conn->set_socket(fd, ip, port, sock_type); - - return conn; -}; - -void TCPServerPosix::stop() { - - if (listen_sockfd != -1) { - int ret = close(listen_sockfd); - ERR_FAIL_COND(ret != 0); - }; - - listen_sockfd = -1; - sock_type = IP::TYPE_NONE; -}; - -TCPServerPosix::TCPServerPosix() { - - listen_sockfd = -1; - sock_type = IP::TYPE_NONE; -}; - -TCPServerPosix::~TCPServerPosix() { - - stop(); -}; -#endif diff --git a/drivers/unix/thread_posix.cpp b/drivers/unix/thread_posix.cpp index a73b40a6f2..54bbbf2dad 100644 --- a/drivers/unix/thread_posix.cpp +++ b/drivers/unix/thread_posix.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "thread_posix.h" -#include "script_language.h" +#include "core/script_language.h" #if (defined(UNIX_ENABLED) || defined(PTHREAD_ENABLED)) && !defined(NO_THREADS) @@ -37,8 +37,8 @@ #include <pthread_np.h> #endif +#include "core/os/memory.h" #include "core/safe_refcount.h" -#include "os/memory.h" static pthread_key_t _create_thread_id_key() { pthread_key_t key; @@ -103,8 +103,6 @@ void ThreadPosix::wait_to_finish_func_posix(Thread *p_thread) { Error ThreadPosix::set_name_func_posix(const String &p_name) { - pthread_t running_thread = pthread_self(); - #ifdef PTHREAD_NO_RENAME return ERR_UNAVAILABLE; @@ -117,6 +115,7 @@ Error ThreadPosix::set_name_func_posix(const String &p_name) { #else + pthread_t running_thread = pthread_self(); #ifdef PTHREAD_BSD_SET_NAME pthread_set_name_np(running_thread, p_name.utf8().get_data()); int err = 0; // Open/FreeBSD ignore errors in this function diff --git a/drivers/unix/thread_posix.h b/drivers/unix/thread_posix.h index ea2de61bd5..20d103232e 100644 --- a/drivers/unix/thread_posix.h +++ b/drivers/unix/thread_posix.h @@ -37,7 +37,7 @@ #if (defined(UNIX_ENABLED) || defined(PTHREAD_ENABLED)) && !defined(NO_THREADS) -#include "os/thread.h" +#include "core/os/thread.h" #include <pthread.h> #include <sys/types.h> diff --git a/drivers/wasapi/SCsub b/drivers/wasapi/SCsub index 233593b0f9..4c24925192 100644 --- a/drivers/wasapi/SCsub +++ b/drivers/wasapi/SCsub @@ -4,5 +4,3 @@ Import('env') # Driver source files env.add_source_files(env.drivers_sources, "*.cpp") - -Export('env') diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp index 11abe3256e..8665f701b1 100644 --- a/drivers/wasapi/audio_driver_wasapi.cpp +++ b/drivers/wasapi/audio_driver_wasapi.cpp @@ -32,8 +32,8 @@ #include "audio_driver_wasapi.h" -#include "os/os.h" -#include "project_settings.h" +#include "core/os/os.h" +#include "core/project_settings.h" #include <functiondiscoverykeys.h> @@ -65,7 +65,6 @@ const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient); #define CAPTURE_BUFFER_CHANNELS 2 -static StringName capture_device_id; static bool default_render_device_changed = false; static bool default_capture_device_changed = false; @@ -128,7 +127,6 @@ public: default_render_device_changed = true; } else if (flow == eCapture) { default_capture_device_changed = true; - capture_device_id = String(pwstrDeviceId); } } @@ -338,10 +336,7 @@ Error AudioDriverWASAPI::init_capture_device(bool reinit) { HRESULT hr = audio_input.audio_client->GetBufferSize(&max_frames); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); - // Set the buffer size - input_buffer.resize(max_frames * CAPTURE_BUFFER_CHANNELS); - input_position = 0; - input_size = 0; + input_buffer_init(max_frames); return OK; } @@ -659,6 +654,9 @@ void AudioDriverWASAPI::thread_func(void *p_udata) { if (err == OK) { ad->start(); } + + avail_frames = 0; + write_ofs = 0; } if (ad->audio_input.active) { @@ -795,19 +793,18 @@ Error AudioDriverWASAPI::capture_start() { return err; } - if (audio_input.active == false) { - audio_input.audio_client->Start(); - audio_input.active = true; - - return OK; + if (audio_input.active) { + return FAILED; } - return FAILED; + audio_input.audio_client->Start(); + audio_input.active = true; + return OK; } Error AudioDriverWASAPI::capture_stop() { - if (audio_input.active == true) { + if (audio_input.active) { audio_input.audio_client->Stop(); audio_input.active = false; diff --git a/drivers/windows/SCsub b/drivers/windows/SCsub index ee39fd2631..28b315ae66 100644 --- a/drivers/windows/SCsub +++ b/drivers/windows/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.drivers_sources, "*.cpp") - -Export('env') diff --git a/drivers/windows/dir_access_windows.cpp b/drivers/windows/dir_access_windows.cpp index cf4d82fb07..589e9e0870 100644 --- a/drivers/windows/dir_access_windows.cpp +++ b/drivers/windows/dir_access_windows.cpp @@ -32,9 +32,9 @@ #include "dir_access_windows.h" -#include "os/memory.h" +#include "core/os/memory.h" +#include "core/print_string.h" -#include "print_string.h" #include <stdio.h> #include <wchar.h> #include <windows.h> diff --git a/drivers/windows/dir_access_windows.h b/drivers/windows/dir_access_windows.h index b18c8f5b5d..9f5d0b6d93 100644 --- a/drivers/windows/dir_access_windows.h +++ b/drivers/windows/dir_access_windows.h @@ -33,7 +33,7 @@ #ifdef WINDOWS_ENABLED -#include "os/dir_access.h" +#include "core/os/dir_access.h" /** @author Juan Linietsky <reduz@gmail.com> diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp index b4492a2022..2582478259 100644 --- a/drivers/windows/file_access_windows.cpp +++ b/drivers/windows/file_access_windows.cpp @@ -114,7 +114,7 @@ Error FileAccessWindows::_open(const String &p_path, int p_mode_flags) { path = path + ".tmp"; } - f = _wfopen(path.c_str(), mode_string); + _wfopen_s(&f, path.c_str(), mode_string); if (f == NULL) { last_error = ERR_FILE_CANT_OPEN; @@ -278,7 +278,7 @@ bool FileAccessWindows::file_exists(const String &p_name) { FILE *g; //printf("opening file %s\n", p_fname.c_str()); String filename = fix_path(p_name); - g = _wfopen(filename.c_str(), L"rb"); + _wfopen_s(&g, filename.c_str(), L"rb"); if (g == NULL) { return false; diff --git a/drivers/windows/file_access_windows.h b/drivers/windows/file_access_windows.h index 0462c1e942..6f985e68b4 100644 --- a/drivers/windows/file_access_windows.h +++ b/drivers/windows/file_access_windows.h @@ -33,13 +33,15 @@ #ifdef WINDOWS_ENABLED -#include "os/file_access.h" -#include "os/memory.h" +#include "core/os/file_access.h" +#include "core/os/memory.h" + #include <stdio.h> /** @author Juan Linietsky <reduzio@gmail.com> */ + class FileAccessWindows : public FileAccess { FILE *f; diff --git a/drivers/windows/mutex_windows.cpp b/drivers/windows/mutex_windows.cpp index 359a79209c..9fc6485be3 100644 --- a/drivers/windows/mutex_windows.cpp +++ b/drivers/windows/mutex_windows.cpp @@ -29,7 +29,8 @@ /*************************************************************************/ #include "mutex_windows.h" -#include "os/memory.h" + +#include "core/os/memory.h" #ifdef WINDOWS_ENABLED diff --git a/drivers/windows/mutex_windows.h b/drivers/windows/mutex_windows.h index 4dff2c2456..5c3a8eb331 100644 --- a/drivers/windows/mutex_windows.h +++ b/drivers/windows/mutex_windows.h @@ -33,11 +33,14 @@ #ifdef WINDOWS_ENABLED -#include "os/mutex.h" +#include "core/os/mutex.h" + #include <windows.h> + /** @author Juan Linietsky <reduzio@gmail.com> */ + class MutexWindows : public Mutex { #ifdef WINDOWS_USE_MUTEX diff --git a/drivers/windows/packet_peer_udp_winsock.cpp b/drivers/windows/packet_peer_udp_winsock.cpp deleted file mode 100644 index 609096d02e..0000000000 --- a/drivers/windows/packet_peer_udp_winsock.cpp +++ /dev/null @@ -1,298 +0,0 @@ -/*************************************************************************/ -/* packet_peer_udp_winsock.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 WINDOWS_ENABLED - -#include "packet_peer_udp_winsock.h" - -#include <winsock2.h> -#include <ws2tcpip.h> - -#include "drivers/unix/socket_helpers.h" - -int PacketPeerUDPWinsock::get_available_packet_count() const { - - Error err = const_cast<PacketPeerUDPWinsock *>(this)->_poll(false); - if (err != OK) - return 0; - - return queue_count; -} - -Error PacketPeerUDPWinsock::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { - - Error err = const_cast<PacketPeerUDPWinsock *>(this)->_poll(false); - if (err != OK) - return err; - if (queue_count == 0) - return ERR_UNAVAILABLE; - - uint32_t size; - uint8_t type; - rb.read(&type, 1, true); - if (type == IP::TYPE_IPV4) { - uint8_t ip[4]; - rb.read(ip, 4, true); - packet_ip.set_ipv4(ip); - } else { - uint8_t ip[16]; - rb.read(ip, 16, true); - packet_ip.set_ipv6(ip); - }; - rb.read((uint8_t *)&packet_port, 4, true); - rb.read((uint8_t *)&size, 4, true); - rb.read(packet_buffer, size, true); - --queue_count; - *r_buffer = packet_buffer; - r_buffer_size = size; - return OK; -} -Error PacketPeerUDPWinsock::put_packet(const uint8_t *p_buffer, int p_buffer_size) { - - ERR_FAIL_COND_V(!peer_addr.is_valid(), ERR_UNCONFIGURED); - - if (sock_type == IP::TYPE_NONE) - sock_type = peer_addr.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; - - int sock = _get_socket(); - ERR_FAIL_COND_V(sock == -1, FAILED); - struct sockaddr_storage addr; - size_t addr_size = _set_sockaddr(&addr, peer_addr, peer_port, sock_type); - - _set_sock_blocking(blocking); - - errno = 0; - int err; - while ((err = sendto(sock, (const char *)p_buffer, p_buffer_size, 0, (struct sockaddr *)&addr, addr_size)) != p_buffer_size) { - - if (WSAGetLastError() != WSAEWOULDBLOCK) { - return FAILED; - } else if (!blocking) { - return ERR_UNAVAILABLE; - } - } - - return OK; -} - -int PacketPeerUDPWinsock::get_max_packet_size() const { - - return 512; // uhm maybe not -} - -void PacketPeerUDPWinsock::_set_sock_blocking(bool p_blocking) { - - if (sock_blocking == p_blocking) - return; - - sock_blocking = p_blocking; - unsigned long par = sock_blocking ? 0 : 1; - if (ioctlsocket(sockfd, FIONBIO, &par)) { - perror("setting non-block mode"); - //close(); - //return -1; - }; -} - -Error PacketPeerUDPWinsock::listen(int p_port, const IP_Address &p_bind_address, int p_recv_buffer_size) { - - ERR_FAIL_COND_V(sockfd != -1, ERR_ALREADY_IN_USE); - ERR_FAIL_COND_V(!p_bind_address.is_valid() && !p_bind_address.is_wildcard(), ERR_INVALID_PARAMETER); - - sock_type = IP::TYPE_ANY; - - if (p_bind_address.is_valid()) - sock_type = p_bind_address.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; - - int sock = _get_socket(); - if (sock == -1) - return ERR_CANT_CREATE; - - struct sockaddr_storage addr = { 0 }; - size_t addr_size = _set_listen_sockaddr(&addr, p_port, sock_type, IP_Address()); - - if (bind(sock, (struct sockaddr *)&addr, addr_size) == -1) { - close(); - return ERR_UNAVAILABLE; - } - - printf("UDP Connection listening on port %i\n", p_port); - rb.resize(nearest_shift(p_recv_buffer_size)); - return OK; -} - -void PacketPeerUDPWinsock::close() { - - if (sockfd != -1) - ::closesocket(sockfd); - sockfd = -1; - sock_type = IP::TYPE_NONE; - rb.resize(16); - queue_count = 0; -} - -Error PacketPeerUDPWinsock::wait() { - - return _poll(true); -} -Error PacketPeerUDPWinsock::_poll(bool p_wait) { - - if (sockfd == -1) { - return FAILED; - } - - _set_sock_blocking(p_wait); - - struct sockaddr_storage from = { 0 }; - int len = sizeof(struct sockaddr_storage); - int ret; - while ((ret = recvfrom(sockfd, (char *)recv_buffer, MIN((int)sizeof(recv_buffer), MAX(rb.space_left() - 24, 0)), 0, (struct sockaddr *)&from, &len)) > 0) { - - uint32_t port = 0; - - if (from.ss_family == AF_INET) { - uint8_t type = (uint8_t)IP::TYPE_IPV4; - rb.write(&type, 1); - struct sockaddr_in *sin_from = (struct sockaddr_in *)&from; - rb.write((uint8_t *)&sin_from->sin_addr, 4); - port = ntohs(sin_from->sin_port); - - } else if (from.ss_family == AF_INET6) { - - uint8_t type = (uint8_t)IP::TYPE_IPV6; - rb.write(&type, 1); - - struct sockaddr_in6 *s6_from = (struct sockaddr_in6 *)&from; - rb.write((uint8_t *)&s6_from->sin6_addr, 16); - - port = ntohs(s6_from->sin6_port); - - } else { - // WARN_PRINT("Ignoring packet with unknown address family"); - uint8_t type = (uint8_t)IP::TYPE_NONE; - rb.write(&type, 1); - }; - - rb.write((uint8_t *)&port, 4); - rb.write((uint8_t *)&ret, 4); - rb.write(recv_buffer, ret); - - len = sizeof(struct sockaddr_storage); - ++queue_count; - if (p_wait) - break; - }; - - if (ret == SOCKET_ERROR) { - int error = WSAGetLastError(); - - if (error == WSAEWOULDBLOCK) { - // Expected when doing non-blocking sockets, retry later. - } else if (error == WSAECONNRESET) { - // If the remote target does not accept messages, this error may occur, but is harmless. - // Once the remote target gets available, this message will disappear for new messages. - } else { - close(); - return FAILED; - } - } - - if (ret == 0) { - close(); - return FAILED; - }; - - return OK; -} - -bool PacketPeerUDPWinsock::is_listening() const { - - return sockfd != -1; -} - -IP_Address PacketPeerUDPWinsock::get_packet_address() const { - - return packet_ip; -} - -int PacketPeerUDPWinsock::get_packet_port() const { - - return packet_port; -} - -int PacketPeerUDPWinsock::_get_socket() { - - ERR_FAIL_COND_V(sock_type == IP::TYPE_NONE, -1); - - if (sockfd != -1) - return sockfd; - - sockfd = _socket_create(sock_type, SOCK_DGRAM, IPPROTO_UDP); - - if (sockfd != -1) - _set_sock_blocking(false); - - return sockfd; -} - -void PacketPeerUDPWinsock::set_dest_address(const IP_Address &p_address, int p_port) { - - peer_addr = p_address; - peer_port = p_port; -} - -void PacketPeerUDPWinsock::make_default() { - - PacketPeerUDP::_create = PacketPeerUDPWinsock::_create; -}; - -PacketPeerUDP *PacketPeerUDPWinsock::_create() { - - return memnew(PacketPeerUDPWinsock); -}; - -PacketPeerUDPWinsock::PacketPeerUDPWinsock() { - - blocking = true; - sock_blocking = true; - sockfd = -1; - packet_port = 0; - queue_count = 0; - peer_port = 0; - sock_type = IP::TYPE_NONE; - rb.resize(16); -} - -PacketPeerUDPWinsock::~PacketPeerUDPWinsock() { - - close(); -} - -#endif diff --git a/drivers/windows/rw_lock_windows.cpp b/drivers/windows/rw_lock_windows.cpp index 29c24d3d02..ef00141928 100644 --- a/drivers/windows/rw_lock_windows.cpp +++ b/drivers/windows/rw_lock_windows.cpp @@ -32,8 +32,9 @@ #include "rw_lock_windows.h" -#include "error_macros.h" -#include "os/memory.h" +#include "core/error_macros.h" +#include "core/os/memory.h" + #include <stdio.h> void RWLockWindows::read_lock() { diff --git a/drivers/windows/rw_lock_windows.h b/drivers/windows/rw_lock_windows.h index fdce28574a..742a0930d4 100644 --- a/drivers/windows/rw_lock_windows.h +++ b/drivers/windows/rw_lock_windows.h @@ -33,7 +33,8 @@ #if defined(WINDOWS_ENABLED) -#include "os/rw_lock.h" +#include "core/os/rw_lock.h" + #include <windows.h> class RWLockWindows : public RWLock { diff --git a/drivers/windows/semaphore_windows.cpp b/drivers/windows/semaphore_windows.cpp index 25fced93ce..34dd387705 100644 --- a/drivers/windows/semaphore_windows.cpp +++ b/drivers/windows/semaphore_windows.cpp @@ -32,7 +32,7 @@ #if defined(WINDOWS_ENABLED) -#include "os/memory.h" +#include "core/os/memory.h" Error SemaphoreWindows::wait() { diff --git a/drivers/windows/semaphore_windows.h b/drivers/windows/semaphore_windows.h index e099ee437a..1e2f9c152e 100644 --- a/drivers/windows/semaphore_windows.h +++ b/drivers/windows/semaphore_windows.h @@ -31,14 +31,16 @@ #ifndef SEMAPHORE_WINDOWS_H #define SEMAPHORE_WINDOWS_H -#include "os/semaphore.h" +#include "core/os/semaphore.h" #ifdef WINDOWS_ENABLED #include <windows.h> + /** @author Juan Linietsky <reduzio@gmail.com> */ + class SemaphoreWindows : public Semaphore { mutable HANDLE semaphore; diff --git a/drivers/windows/shell_windows.h b/drivers/windows/shell_windows.h index 41cc6b72a2..98972a9bb1 100644 --- a/drivers/windows/shell_windows.h +++ b/drivers/windows/shell_windows.h @@ -31,12 +31,14 @@ #ifndef SHELL_WINDOWS_H #define SHELL_WINDOWS_H -#include "os/shell.h" +#include "core/os/shell.h" #ifdef WINDOWS_ENABLED + /** @author Juan Linietsky <reduzio@gmail.com> */ + class ShellWindows : public Shell { public: virtual void execute(String p_path); diff --git a/drivers/windows/stream_peer_tcp_winsock.cpp b/drivers/windows/stream_peer_tcp_winsock.cpp deleted file mode 100644 index 19c937170b..0000000000 --- a/drivers/windows/stream_peer_tcp_winsock.cpp +++ /dev/null @@ -1,375 +0,0 @@ -/*************************************************************************/ -/* stream_peer_tcp_winsock.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 WINDOWS_ENABLED - -#include "stream_peer_tcp_winsock.h" - -#include <winsock2.h> -#include <ws2tcpip.h> - -#include "drivers/unix/socket_helpers.h" - -int winsock_refcount = 0; - -StreamPeerTCP *StreamPeerTCPWinsock::_create() { - - return memnew(StreamPeerTCPWinsock); -}; - -void StreamPeerTCPWinsock::make_default() { - - StreamPeerTCP::_create = StreamPeerTCPWinsock::_create; - - if (winsock_refcount == 0) { - WSADATA data; - WSAStartup(MAKEWORD(2, 2), &data); - }; - ++winsock_refcount; -}; - -void StreamPeerTCPWinsock::cleanup() { - - --winsock_refcount; - if (winsock_refcount == 0) { - - WSACleanup(); - }; -}; - -Error StreamPeerTCPWinsock::_block(int p_sockfd, bool p_read, bool p_write) const { - - fd_set read, write; - FD_ZERO(&read); - FD_ZERO(&write); - - if (p_read) - FD_SET(p_sockfd, &read); - if (p_write) - FD_SET(p_sockfd, &write); - - int ret = select(p_sockfd + 1, &read, &write, NULL, NULL); // block forever - return ret < 0 ? FAILED : OK; -}; - -Error StreamPeerTCPWinsock::_poll_connection() const { - - ERR_FAIL_COND_V(status != STATUS_CONNECTING || sockfd == INVALID_SOCKET, FAILED); - - struct sockaddr_storage their_addr; - size_t addr_size = _set_sockaddr(&their_addr, peer_host, peer_port, sock_type); - - if (::connect(sockfd, (struct sockaddr *)&their_addr, addr_size) == SOCKET_ERROR) { - - int err = WSAGetLastError(); - if (err == WSAEISCONN) { - status = STATUS_CONNECTED; - return OK; - }; - - if (err == WSAEINPROGRESS || err == WSAEALREADY) { - return OK; - } - - status = STATUS_ERROR; - return ERR_CONNECTION_ERROR; - } else { - - status = STATUS_CONNECTED; - return OK; - }; - - return OK; -}; - -Error StreamPeerTCPWinsock::write(const uint8_t *p_data, int p_bytes, int &r_sent, bool p_block) { - - if (status == STATUS_NONE || status == STATUS_ERROR) { - - return FAILED; - }; - - if (status != STATUS_CONNECTED) { - - if (_poll_connection() != OK) { - - return FAILED; - }; - - if (status != STATUS_CONNECTED) { - r_sent = 0; - return OK; - }; - }; - - int data_to_send = p_bytes; - const uint8_t *offset = p_data; - if (sockfd == -1) return FAILED; - int total_sent = 0; - - while (data_to_send) { - - int sent_amount = send(sockfd, (const char *)offset, data_to_send, 0); - - if (sent_amount == -1) { - - if (WSAGetLastError() != WSAEWOULDBLOCK) { - - perror("Nothing sent"); - disconnect_from_host(); - ERR_PRINT("Server disconnected!\n"); - return FAILED; - }; - - if (!p_block) { - r_sent = total_sent; - return OK; - }; - - _block(sockfd, false, true); - } else { - - data_to_send -= sent_amount; - offset += sent_amount; - total_sent += sent_amount; - }; - } - - r_sent = total_sent; - - return OK; -}; - -Error StreamPeerTCPWinsock::read(uint8_t *p_buffer, int p_bytes, int &r_received, bool p_block) { - - if (!is_connected_to_host()) { - - return FAILED; - }; - - if (status != STATUS_CONNECTED) { - - if (_poll_connection() != OK) { - - return FAILED; - }; - - if (status != STATUS_CONNECTED) { - r_received = 0; - return OK; - }; - }; - - int to_read = p_bytes; - int total_read = 0; - - while (to_read) { - - int read = recv(sockfd, (char *)p_buffer + total_read, to_read, 0); - - if (read == -1) { - - if (WSAGetLastError() != WSAEWOULDBLOCK) { - - perror("Nothing read"); - disconnect_from_host(); - ERR_PRINT("Server disconnected!\n"); - return FAILED; - }; - - if (!p_block) { - - r_received = total_read; - return OK; - }; - _block(sockfd, true, false); - } else if (read == 0) { - disconnect_from_host(); - r_received = total_read; - return ERR_FILE_EOF; - } else { - - to_read -= read; - total_read += read; - }; - }; - - r_received = total_read; - - return OK; -}; - -Error StreamPeerTCPWinsock::put_data(const uint8_t *p_data, int p_bytes) { - - int total; - return write(p_data, p_bytes, total, true); -}; - -Error StreamPeerTCPWinsock::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) { - - return write(p_data, p_bytes, r_sent, false); -}; - -Error StreamPeerTCPWinsock::get_data(uint8_t *p_buffer, int p_bytes) { - - int total; - return read(p_buffer, p_bytes, total, true); -}; - -Error StreamPeerTCPWinsock::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) { - - return read(p_buffer, p_bytes, r_received, false); -}; - -StreamPeerTCP::Status StreamPeerTCPWinsock::get_status() const { - - if (status == STATUS_CONNECTING) { - _poll_connection(); - }; - - return status; -}; - -bool StreamPeerTCPWinsock::is_connected_to_host() const { - - if (status == STATUS_NONE || status == STATUS_ERROR) { - - return false; - }; - if (status != STATUS_CONNECTED) { - return true; - }; - - return (sockfd != INVALID_SOCKET); -}; - -void StreamPeerTCPWinsock::disconnect_from_host() { - - if (sockfd != INVALID_SOCKET) - closesocket(sockfd); - sockfd = INVALID_SOCKET; - sock_type = IP::TYPE_NONE; - - status = STATUS_NONE; - - peer_host = IP_Address(); - peer_port = 0; -}; - -void StreamPeerTCPWinsock::set_socket(int p_sockfd, IP_Address p_host, int p_port, IP::Type p_sock_type) { - - sockfd = p_sockfd; - sock_type = p_sock_type; - status = STATUS_CONNECTING; - peer_host = p_host; - peer_port = p_port; -}; - -Error StreamPeerTCPWinsock::connect_to_host(const IP_Address &p_host, uint16_t p_port) { - - ERR_FAIL_COND_V(!p_host.is_valid(), ERR_INVALID_PARAMETER); - - sock_type = p_host.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; - sockfd = _socket_create(sock_type, SOCK_STREAM, IPPROTO_TCP); - if (sockfd == INVALID_SOCKET) { - ERR_PRINT("Socket creation failed!"); - disconnect_from_host(); - //perror("socket"); - return FAILED; - }; - - unsigned long par = 1; - if (ioctlsocket(sockfd, FIONBIO, &par)) { - perror("setting non-block mode"); - disconnect_from_host(); - return FAILED; - }; - - struct sockaddr_storage their_addr; - size_t addr_size = _set_sockaddr(&their_addr, p_host, p_port, sock_type); - - if (::connect(sockfd, (struct sockaddr *)&their_addr, addr_size) == SOCKET_ERROR) { - - if (WSAGetLastError() != WSAEWOULDBLOCK) { - ERR_PRINT("Connection to remote host failed!"); - disconnect_from_host(); - return FAILED; - }; - status = STATUS_CONNECTING; - } else { - status = STATUS_CONNECTED; - }; - - peer_host = p_host; - peer_port = p_port; - - return OK; -}; - -void StreamPeerTCPWinsock::set_no_delay(bool p_enabled) { - ERR_FAIL_COND(!is_connected_to_host()); - int flag = p_enabled ? 1 : 0; - if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int)) != 0) { - ERR_PRINT("Unable to set TCP no delay option"); - } -} - -int StreamPeerTCPWinsock::get_available_bytes() const { - - unsigned long len; - int ret = ioctlsocket(sockfd, FIONREAD, &len); - ERR_FAIL_COND_V(ret == -1, 0) - return len; -} - -IP_Address StreamPeerTCPWinsock::get_connected_host() const { - - return peer_host; -}; - -uint16_t StreamPeerTCPWinsock::get_connected_port() const { - - return peer_port; -}; - -StreamPeerTCPWinsock::StreamPeerTCPWinsock() { - - sock_type = IP::TYPE_NONE; - sockfd = INVALID_SOCKET; - status = STATUS_NONE; - peer_port = 0; -}; - -StreamPeerTCPWinsock::~StreamPeerTCPWinsock() { - - disconnect_from_host(); -}; - -#endif diff --git a/drivers/windows/tcp_server_winsock.cpp b/drivers/windows/tcp_server_winsock.cpp deleted file mode 100644 index ddb955549f..0000000000 --- a/drivers/windows/tcp_server_winsock.cpp +++ /dev/null @@ -1,189 +0,0 @@ -/*************************************************************************/ -/* tcp_server_winsock.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 WINDOWS_ENABLED - -#include "tcp_server_winsock.h" - -#include "stream_peer_tcp_winsock.h" - -#include <winsock2.h> -#include <ws2tcpip.h> - -#include "drivers/unix/socket_helpers.h" - -extern int winsock_refcount; - -TCP_Server *TCPServerWinsock::_create() { - - return memnew(TCPServerWinsock); -}; - -void TCPServerWinsock::make_default() { - - TCP_Server::_create = TCPServerWinsock::_create; - - if (winsock_refcount == 0) { - WSADATA data; - WSAStartup(MAKEWORD(2, 2), &data); - }; - ++winsock_refcount; -}; - -void TCPServerWinsock::cleanup() { - - --winsock_refcount; - if (winsock_refcount == 0) { - - WSACleanup(); - }; -}; - -Error TCPServerWinsock::listen(uint16_t p_port, const IP_Address &p_bind_address) { - - ERR_FAIL_COND_V(listen_sockfd != -1, ERR_ALREADY_IN_USE); - ERR_FAIL_COND_V(!p_bind_address.is_valid() && !p_bind_address.is_wildcard(), ERR_INVALID_PARAMETER); - - int sockfd; - sock_type = IP::TYPE_ANY; - - // If the bind address is valid use its type as the socket type - if (p_bind_address.is_valid()) - sock_type = p_bind_address.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; - - sockfd = _socket_create(sock_type, SOCK_STREAM, IPPROTO_TCP); - ERR_FAIL_COND_V(sockfd == INVALID_SOCKET, FAILED); - - unsigned long par = 1; - if (ioctlsocket(sockfd, FIONBIO, &par)) { - perror("setting non-block mode"); - stop(); - return FAILED; - }; - - struct sockaddr_storage my_addr; - size_t addr_size = _set_listen_sockaddr(&my_addr, p_port, sock_type, p_bind_address); - - int reuse = 1; - if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) < 0) { - - printf("REUSEADDR failed!"); - } - - if (bind(sockfd, (struct sockaddr *)&my_addr, addr_size) != SOCKET_ERROR) { - - if (::listen(sockfd, SOMAXCONN) == SOCKET_ERROR) { - - closesocket(sockfd); - ERR_FAIL_V(FAILED); - }; - } else { - closesocket(sockfd); - return ERR_ALREADY_IN_USE; - }; - - if (listen_sockfd != INVALID_SOCKET) { - - stop(); - }; - - listen_sockfd = sockfd; - - return OK; -}; - -bool TCPServerWinsock::is_connection_available() const { - - if (listen_sockfd == -1) { - return false; - }; - - timeval timeout; - timeout.tv_sec = 0; - timeout.tv_usec = 0; - - fd_set pfd; - FD_ZERO(&pfd); - FD_SET(listen_sockfd, &pfd); - - int ret = select(listen_sockfd + 1, &pfd, NULL, NULL, &timeout); - ERR_FAIL_COND_V(ret < 0, 0); - - if (ret && (FD_ISSET(listen_sockfd, &pfd))) { - - return true; - }; - - return false; -}; - -Ref<StreamPeerTCP> TCPServerWinsock::take_connection() { - - if (!is_connection_available()) { - return NULL; - }; - - struct sockaddr_storage their_addr; - int sin_size = sizeof(their_addr); - int fd = accept(listen_sockfd, (struct sockaddr *)&their_addr, &sin_size); - ERR_FAIL_COND_V(fd == INVALID_SOCKET, NULL); - - Ref<StreamPeerTCPWinsock> conn = memnew(StreamPeerTCPWinsock); - IP_Address ip; - int port; - _set_ip_addr_port(ip, port, &their_addr); - - conn->set_socket(fd, ip, port, sock_type); - - return conn; -}; - -void TCPServerWinsock::stop() { - - if (listen_sockfd != INVALID_SOCKET) { - closesocket(listen_sockfd); - }; - - listen_sockfd = -1; - sock_type = IP::TYPE_NONE; -}; - -TCPServerWinsock::TCPServerWinsock() { - - listen_sockfd = INVALID_SOCKET; - sock_type = IP::TYPE_NONE; -}; - -TCPServerWinsock::~TCPServerWinsock() { - - stop(); -}; - -#endif diff --git a/drivers/windows/thread_windows.cpp b/drivers/windows/thread_windows.cpp index 5e0b017a5c..52dcfacdf8 100644 --- a/drivers/windows/thread_windows.cpp +++ b/drivers/windows/thread_windows.cpp @@ -32,7 +32,7 @@ #if defined(WINDOWS_ENABLED) && !defined(UWP_ENABLED) -#include "os/memory.h" +#include "core/os/memory.h" Thread::ID ThreadWindows::get_id() const { diff --git a/drivers/windows/thread_windows.h b/drivers/windows/thread_windows.h index d7a8389d9e..5d2838e54f 100644 --- a/drivers/windows/thread_windows.h +++ b/drivers/windows/thread_windows.h @@ -31,16 +31,17 @@ #ifndef THREAD_WINDOWS_H #define THREAD_WINDOWS_H -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ - #ifdef WINDOWS_ENABLED -#include "os/thread.h" -#include "script_language.h" +#include "core/os/thread.h" +#include "core/script_language.h" + #include <windows.h> +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ + class ThreadWindows : public Thread { ThreadCreateCallback callback; diff --git a/drivers/winmidi/SCsub b/drivers/winmidi/SCsub index 233593b0f9..4c24925192 100644 --- a/drivers/winmidi/SCsub +++ b/drivers/winmidi/SCsub @@ -4,5 +4,3 @@ 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 index 63f7f13685..1d4bf1a1e2 100644 --- a/drivers/winmidi/win_midi.cpp +++ b/drivers/winmidi/win_midi.cpp @@ -31,7 +31,8 @@ #ifdef WINMIDI_ENABLED #include "win_midi.h" -#include "print_string.h" + +#include "core/print_string.h" void MIDIDriverWinMidi::read(HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) { diff --git a/drivers/winmidi/win_midi.h b/drivers/winmidi/win_midi.h index 1cf9b19b5d..87a349d5d1 100644 --- a/drivers/winmidi/win_midi.h +++ b/drivers/winmidi/win_midi.h @@ -33,14 +33,14 @@ #ifndef WIN_MIDI_H #define WIN_MIDI_H +#include "core/os/midi_driver.h" +#include "core/vector.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; diff --git a/drivers/xaudio2/SCsub b/drivers/xaudio2/SCsub index cb780a893b..3dca95b429 100644 --- a/drivers/xaudio2/SCsub +++ b/drivers/xaudio2/SCsub @@ -5,5 +5,3 @@ Import('env') env.add_source_files(env.drivers_sources, "*.cpp") env.Append(CXXFLAGS=['-DXAUDIO2_ENABLED']) env.Append(LINKFLAGS=['xaudio2_8.lib']) - -Export('env') diff --git a/drivers/xaudio2/audio_driver_xaudio2.cpp b/drivers/xaudio2/audio_driver_xaudio2.cpp index a1002ef4f9..452a1105ca 100644 --- a/drivers/xaudio2/audio_driver_xaudio2.cpp +++ b/drivers/xaudio2/audio_driver_xaudio2.cpp @@ -30,8 +30,8 @@ #include "audio_driver_xaudio2.h" -#include "os/os.h" -#include "project_settings.h" +#include "core/os/os.h" +#include "core/project_settings.h" const char *AudioDriverXAudio2::get_name() const { return "XAudio2"; diff --git a/drivers/zlib/SCsub b/drivers/zlib/SCsub deleted file mode 100644 index 407deb5f6e..0000000000 --- a/drivers/zlib/SCsub +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python - -Import('env') - -# Not cloning the env, the includes need to be accessible for core/ - -# Thirdparty source files -# No check here as already done in drivers/SCsub -thirdparty_dir = "#thirdparty/zlib/" -thirdparty_sources = [ - "adler32.c", - "compress.c", - "crc32.c", - "deflate.c", - "infback.c", - "inffast.c", - "inflate.c", - "inftrees.c", - "trees.c", - "uncompr.c", - "zutil.c", -] -thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - -env.add_source_files(env.drivers_sources, thirdparty_sources) -env.Append(CPPPATH=[thirdparty_dir]) diff --git a/editor/SCsub b/editor/SCsub index 4fa287c33b..82a4ecb6c0 100644 --- a/editor/SCsub +++ b/editor/SCsub @@ -1,6 +1,7 @@ #!/usr/bin/env python Import('env') + env.editor_sources = [] import os @@ -61,10 +62,6 @@ if env['tools']: env.Depends("#editor/doc_data_compressed.gen.h", docs) 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", run_in_subprocess(editor_builders.make_certs_header)) - import glob path = env.Dir('.').abspath @@ -76,24 +73,15 @@ if env['tools']: # Fonts flist = glob.glob(path + "/../thirdparty/fonts/*.ttf") - flist.append(glob.glob(path + "/../thirdparty/fonts/*.otf")) + flist.extend(glob.glob(path + "/../thirdparty/fonts/*.otf")) + flist.sort() env.Depends('#editor/builtin_fonts.gen.h', flist) 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"]) + env_thirdparty = env.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.editor_sources, ["#thirdparty/misc/clipper.cpp"]) SConscript('collada/SCsub') SConscript('doc/SCsub') @@ -104,5 +92,3 @@ if env['tools']: lib = env.add_library("editor", env.editor_sources) env.Prepend(LIBS=[lib]) - - Export('env') diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp index 197599442b..f0dc3ce305 100644 --- a/editor/animation_bezier_editor.cpp +++ b/editor/animation_bezier_editor.cpp @@ -1,5 +1,37 @@ +/*************************************************************************/ +/* animation_bezier_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 "animation_bezier_editor.h" +#include "editor/editor_node.h" + float AnimationBezierTrackEdit::_bezier_h_to_pixel(float p_h) { float h = p_h; h = (h - v_scroll) / v_zoom; @@ -258,12 +290,7 @@ void AnimationBezierTrackEdit::_notification(int p_what) { int h = font->get_height(); if (node) { - Ref<Texture> icon; - if (has_icon(node->get_class(), "EditorIcons")) { - icon = get_icon(node->get_class(), "EditorIcons"); - } else { - icon = get_icon("Node", "EditorIcons"); - } + Ref<Texture> icon = EditorNode::get_singleton()->get_object_icon(node, "Node"); h = MAX(h, icon->get_height()); diff --git a/editor/animation_bezier_editor.h b/editor/animation_bezier_editor.h index 544690844a..1c701cc8f4 100644 --- a/editor/animation_bezier_editor.h +++ b/editor/animation_bezier_editor.h @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* animation_bezier_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 ANIMATION_BEZIER_EDITOR_H #define ANIMATION_BEZIER_EDITOR_H diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 4c4830ad7a..37d26d8e0f 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -29,12 +29,13 @@ /*************************************************************************/ #include "animation_track_editor.h" + #include "animation_track_editor_plugins.h" +#include "core/os/keyboard.h" #include "editor/animation_bezier_editor.h" #include "editor/plugins/animation_player_editor_plugin.h" #include "editor_node.h" #include "editor_scale.h" -#include "os/keyboard.h" #include "scene/main/viewport.h" #include "servers/audio/audio_stream.h" @@ -772,9 +773,6 @@ void AnimationTimelineEdit::_notification(int p_what) { hsize_rect = Rect2(get_name_limit() - hsize_icon->get_width() - 2 * EDSCALE, (get_size().height - hsize_icon->get_height()) / 2, hsize_icon->get_width(), hsize_icon->get_height()); draw_texture(hsize_icon, hsize_rect.position); - float keys_from = get_value(); - float keys_to = keys_from + zoomw / scale; - { float time_min = 0; float time_max = animation->get_length(); @@ -1687,15 +1685,10 @@ void AnimationTrackEdit::_zoom_changed() { } void AnimationTrackEdit::_path_entered(const String &p_text) { - - *block_animation_update_ptr = true; undo_redo->create_action("Change Track Path"); undo_redo->add_do_method(animation.ptr(), "track_set_path", track, p_text); undo_redo->add_undo_method(animation.ptr(), "track_set_path", track, animation->track_get_path(track)); undo_redo->commit_action(); - *block_animation_update_ptr = false; - update(); - path->hide(); } String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const { @@ -4088,6 +4081,8 @@ void AnimationTrackEditor::_move_selection_commit() { for (int i = 0; i < track_edits.size(); i++) { track_edits[i]->update(); } + + _update_key_edit(); } void AnimationTrackEditor::_move_selection_cancel() { @@ -4923,8 +4918,8 @@ AnimationTrackEditor::AnimationTrackEditor() { //this shortcut will be checked from the track itself. so no need to enable it here (will conflict with scenetree dock) edit->get_popup()->add_separator(); - edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/goto_next_step", TTR("Goto Next Step"), KEY_MASK_CMD | KEY_RIGHT), EDIT_GOTO_NEXT_STEP); - edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/goto_prev_step", TTR("Goto Prev Step"), KEY_MASK_CMD | KEY_LEFT), EDIT_GOTO_PREV_STEP); + edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/goto_next_step", TTR("Go to Next Step"), KEY_MASK_CMD | KEY_RIGHT), EDIT_GOTO_NEXT_STEP); + edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/goto_prev_step", TTR("Go to Previous Step"), KEY_MASK_CMD | KEY_LEFT), EDIT_GOTO_PREV_STEP); edit->get_popup()->add_separator(); edit->get_popup()->add_item(TTR("Optimize Animation"), EDIT_OPTIMIZE_ANIMATION); edit->get_popup()->add_item(TTR("Clean-Up Animation"), EDIT_CLEAN_UP_ANIMATION); diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h index 0692c88bea..deefe6c6fd 100644 --- a/editor/animation_track_editor.h +++ b/editor/animation_track_editor.h @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* animation_track_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 ANIMATION_TRACK_EDITOR_H #define ANIMATION_TRACK_EDITOR_H diff --git a/editor/animation_track_editor_plugins.cpp b/editor/animation_track_editor_plugins.cpp index 590621816e..a0ce8dd0a6 100644 --- a/editor/animation_track_editor_plugins.cpp +++ b/editor/animation_track_editor_plugins.cpp @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* animation_track_editor_plugins.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 "animation_track_editor_plugins.h" #include "editor/audio_stream_preview.h" #include "editor_resource_preview.h" diff --git a/editor/animation_track_editor_plugins.h b/editor/animation_track_editor_plugins.h index 59604412d9..dd8ff7c8c9 100644 --- a/editor/animation_track_editor_plugins.h +++ b/editor/animation_track_editor_plugins.h @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* animation_track_editor_plugins.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 ANIMATION_TRACK_EDITOR_PLUGINS_H #define ANIMATION_TRACK_EDITOR_PLUGINS_H diff --git a/editor/audio_stream_preview.cpp b/editor/audio_stream_preview.cpp index 6e6a7d7935..5a94d41aba 100644 --- a/editor/audio_stream_preview.cpp +++ b/editor/audio_stream_preview.cpp @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* audio_stream_preview.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_stream_preview.h" ///////////////////// @@ -20,7 +50,7 @@ float AudioStreamPreview::get_max(float p_time, float p_time_next) const { time_to = time_from + 1; } - uint8_t vmax; + uint8_t vmax = 0; for (int i = time_from; i < time_to; i++) { @@ -47,7 +77,7 @@ float AudioStreamPreview::get_min(float p_time, float p_time_next) const { time_to = time_from + 1; } - uint8_t vmin; + uint8_t vmin = 0; for (int i = time_from; i < time_to; i++) { diff --git a/editor/audio_stream_preview.h b/editor/audio_stream_preview.h index cfe1667e9d..1a8f2eaa15 100644 --- a/editor/audio_stream_preview.h +++ b/editor/audio_stream_preview.h @@ -1,7 +1,37 @@ +/*************************************************************************/ +/* audio_stream_preview.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 AUDIO_STREAM_PREVIEW_H #define AUDIO_STREAM_PREVIEW_H -#include "os/thread.h" +#include "core/os/thread.h" #include "scene/main/node.h" #include "servers/audio/audio_stream.h" diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index 2fecf24d7d..3136b0f012 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -30,10 +30,10 @@ #include "code_editor.h" +#include "core/os/keyboard.h" #include "editor/editor_scale.h" #include "editor_node.h" #include "editor_settings.h" -#include "os/keyboard.h" #include "scene/gui/margin_container.h" #include "scene/gui/separator.h" #include "scene/resources/dynamic_font.h" @@ -44,7 +44,7 @@ void GotoLineDialog::popup_find_line(TextEdit *p_edit) { line->set_text(itos(text_editor->cursor_get_line())); line->select_all(); - popup_centered(Size2(180, 80)); + popup_centered(Size2(180, 80) * EDSCALE); line->grab_focus(); } @@ -65,16 +65,20 @@ void GotoLineDialog::ok_pressed() { GotoLineDialog::GotoLineDialog() { set_title(TTR("Go to Line")); + + VBoxContainer *vbc = memnew(VBoxContainer); + vbc->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_BEGIN, 8 * EDSCALE); + vbc->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 8 * EDSCALE); + vbc->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, -8 * EDSCALE); + vbc->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, -8 * EDSCALE); + add_child(vbc); + Label *l = memnew(Label); l->set_text(TTR("Line Number:")); - l->set_position(Point2(5, 5)); - add_child(l); + vbc->add_child(l); line = memnew(LineEdit); - line->set_anchor(MARGIN_RIGHT, ANCHOR_END); - line->set_begin(Point2(15, 22)); - line->set_end(Point2(-15, 35)); - add_child(line); + vbc->add_child(line); register_text_enter(line); text_editor = NULL; @@ -374,7 +378,7 @@ void FindReplaceBar::_hide_bar() { void FindReplaceBar::_show_search() { show(); - search_text->grab_focus(); + search_text->call_deferred("grab_focus"); if (text_edit->is_selection_active() && !selection_only->is_pressed()) { search_text->set_text(text_edit->get_selection_text()); @@ -1035,6 +1039,8 @@ void CodeTextEditor::delete_lines() { 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; + + text_editor->cursor_set_line(to_line, false); while (count) { text_editor->set_line(text_editor->cursor_get_line(), ""); text_editor->backspace_at_cursor(); @@ -1054,7 +1060,7 @@ void CodeTextEditor::delete_lines() { text_editor->end_complex_operation(); } -void CodeTextEditor::code_lines_down() { +void CodeTextEditor::clone_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(); @@ -1066,22 +1072,21 @@ void CodeTextEditor::code_lines_down() { } 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"); - } - + bool caret_at_start = text_editor->cursor_get_line() == from_line; 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); + text_editor->set_line(next_line - 1, text_editor->get_line(next_line - 1) + "\n"); + text_editor->set_line(next_line, text_editor->get_line(i)); next_line++; } + if (caret_at_start) { + text_editor->cursor_set_line(to_line + 1); + } else { + text_editor->cursor_set_line(next_line - 1); + } + 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()); diff --git a/editor/code_editor.h b/editor/code_editor.h index ee47eff9a8..2f9403843e 100644 --- a/editor/code_editor.h +++ b/editor/code_editor.h @@ -203,7 +203,7 @@ public: void move_lines_up(); void move_lines_down(); void delete_lines(); - void code_lines_down(); + void clone_lines_down(); void goto_line(int p_line); void goto_line_selection(int p_line, int p_begin, int p_end); diff --git a/editor/collada/SCsub b/editor/collada/SCsub index 04c9a827ef..2b1e889fb0 100644 --- a/editor/collada/SCsub +++ b/editor/collada/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.editor_sources, "*.cpp") - -Export('env') diff --git a/editor/collada/collada.h b/editor/collada/collada.h index 7535162f74..b777fa04c2 100644 --- a/editor/collada/collada.h +++ b/editor/collada/collada.h @@ -33,9 +33,9 @@ #ifndef COLLADA_H #define COLLADA_H -#include "io/xml_parser.h" -#include "map.h" -#include "project_settings.h" +#include "core/io/xml_parser.h" +#include "core/map.h" +#include "core/project_settings.h" #include "scene/resources/material.h" class Collada { diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index da73a3930a..a1337268ba 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -30,10 +30,10 @@ #include "connections_dialog.h" +#include "core/print_string.h" #include "editor_node.h" #include "editor_settings.h" #include "plugins/script_editor_plugin.h" -#include "print_string.h" #include "scene/gui/label.h" #include "scene/gui/popup_menu.h" @@ -341,8 +341,7 @@ ConnectDialog::ConnectDialog() { vbc_right->add_margin_child(TTR("Add Extra Call Argument:"), add_bind_hb); - bind_editor = memnew(PropertyEditor); - bind_editor->hide_top_label(); + bind_editor = memnew(EditorInspector); vbc_right->add_margin_child(TTR("Extra Call Arguments:"), bind_editor, true); @@ -433,6 +432,9 @@ void ConnectionsDock::_make_or_edit_connection() { if (add_script_function) { // pick up args here before "it" is deleted by update_tree script_function_args = it->get_metadata(0).operator Dictionary()["args"]; + for (int i = 0; i < cToMake.binds.size(); i++) { + script_function_args.append("extra_arg_" + itos(i)); + } } if (connect_dialog->is_editing()) { @@ -498,7 +500,7 @@ void ConnectionsDock::_disconnect(TreeItem &item) { } /* -Break all conections of currently selected signal. +Break all connections of currently selected signal. Can undo-redo as a single action. */ void ConnectionsDock::_disconnect_all() { @@ -650,8 +652,8 @@ void ConnectionsDock::_handle_signal_menu_option(int option) { _open_connection_dialog(*item); } break; case DISCONNECT_ALL: { - StringName signalName = item->get_metadata(0).operator Dictionary()["name"]; - disconnect_all_dialog->set_text(TTR("Are you sure you want to remove all connections from the \"") + signalName + "\" signal?"); + StringName signal_name = item->get_metadata(0).operator Dictionary()["name"]; + disconnect_all_dialog->set_text(vformat(TTR("Are you sure you want to remove all connections from the \"%s\" signal?"), signal_name)); disconnect_all_dialog->popup_centered(); } break; } diff --git a/editor/connections_dialog.h b/editor/connections_dialog.h index 932ff693e4..c2fd1f1d09 100644 --- a/editor/connections_dialog.h +++ b/editor/connections_dialog.h @@ -35,7 +35,8 @@ #ifndef CONNECTIONS_DIALOG_H #define CONNECTIONS_DIALOG_H -#include "editor/property_editor.h" +#include "core/undo_redo.h" +#include "editor/editor_inspector.h" #include "editor/scene_tree_editor.h" #include "scene/gui/button.h" #include "scene/gui/check_button.h" @@ -44,7 +45,6 @@ #include "scene/gui/menu_button.h" #include "scene/gui/popup.h" #include "scene/gui/tree.h" -#include "undo_redo.h" class PopupMenu; class ConnectDialogBinds; @@ -62,7 +62,7 @@ class ConnectDialog : public ConfirmationDialog { SceneTreeEditor *tree; ConfirmationDialog *error; - PropertyEditor *bind_editor; + EditorInspector *bind_editor; OptionButton *type_list; CheckButton *deferred; CheckButton *oneshot; diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp index 8433f4ff7b..c4516c1f17 100644 --- a/editor/create_dialog.cpp +++ b/editor/create_dialog.cpp @@ -30,12 +30,12 @@ #include "create_dialog.h" -#include "class_db.h" +#include "core/class_db.h" +#include "core/os/keyboard.h" +#include "core/print_string.h" #include "editor_help.h" #include "editor_node.h" #include "editor_settings.h" -#include "os/keyboard.h" -#include "print_string.h" #include "scene/gui/box_container.h" void CreateDialog::popup_create(bool p_dont_clear, bool p_replace_mode) { @@ -55,12 +55,12 @@ void CreateDialog::popup_create(bool p_dont_clear, bool p_replace_mode) { while (!f->eof_reached()) { String l = f->get_line().strip_edges(); + String name = l.split(" ")[0]; - if (l != String()) { - + if (ClassDB::class_exists(name) || ScriptServer::is_global_class(name)) { TreeItem *ti = recent->create_item(root); ti->set_text(0, l); - ti->set_icon(0, _get_editor_icon(l)); + ti->set_icon(0, EditorNode::get_singleton()->get_class_icon(l, base_type)); } } @@ -151,41 +151,6 @@ void CreateDialog::_sbox_input(const Ref<InputEvent> &p_ie) { } } -Ref<Texture> CreateDialog::_get_editor_icon(const String &p_type) const { - - if (has_icon(p_type, "EditorIcons")) { - return get_icon(p_type, "EditorIcons"); - } - - if (ScriptServer::is_global_class(p_type)) { - String icon_path = EditorNode::get_editor_data().script_class_get_icon_path(p_type); - RES icon; - if (FileAccess::exists(icon_path)) { - icon = ResourceLoader::load(icon_path); - } - if (!icon.is_valid()) { - icon = get_icon(ScriptServer::get_global_class_base(p_type), "EditorIcons"); - } - return icon; - } - - const Map<String, Vector<EditorData::CustomType> > &p_map = EditorNode::get_editor_data().get_custom_types(); - for (const Map<String, Vector<EditorData::CustomType> >::Element *E = p_map.front(); E; E = E->next()) { - const Vector<EditorData::CustomType> &ct = E->value(); - for (int i = 0; i < ct.size(); ++i) { - if (ct[i].name == p_type) { - if (ct[i].icon.is_valid()) { - return ct[i].icon; - } else { - return get_icon("Object", "EditorIcons"); - } - } - } - } - - return get_icon("Object", "EditorIcons"); -} - void CreateDialog::add_type(const String &p_type, HashMap<String, TreeItem *> &p_types, TreeItem *p_root, TreeItem **to_select) { if (p_types.has(p_type)) @@ -244,17 +209,20 @@ void CreateDialog::add_type(const String &p_type, HashMap<String, TreeItem *> &p bool is_search_subsequence = search_box->get_text().is_subsequence_ofi(p_type); String to_select_type = *to_select ? (*to_select)->get_text(0) : ""; to_select_type = to_select_type.split(" ")[0]; - bool current_item_is_preffered; + bool current_item_is_preferred; 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); + String cpp_to_select_type = to_select_type; + if (ScriptServer::is_global_class(to_select_type)) + cpp_to_select_type = ScriptServer::get_global_class_base(to_select_type); + current_item_is_preferred = ClassDB::is_parent_class(p_type, preferred_search_result_type) && !ClassDB::is_parent_class(cpp_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); + current_item_is_preferred = ed.script_class_is_parent(p_type, preferred_search_result_type) && !ed.script_class_is_parent(to_select_type, preferred_search_result_type) && search_box->get_text() != to_select_type; } if (*to_select && p_type.length() < (*to_select)->get_text(0).length()) { - current_item_is_preffered = true; + current_item_is_preferred = true; } - if (((!*to_select || current_item_is_preffered) && is_search_subsequence) || search_box->get_text() == p_type) { + if (((!*to_select || current_item_is_preferred) && is_search_subsequence) || search_box->get_text() == p_type) { *to_select = item; } } @@ -274,7 +242,7 @@ void CreateDialog::add_type(const String &p_type, HashMap<String, TreeItem *> &p const String &description = EditorHelp::get_doc_data()->class_list[p_type].brief_description; item->set_tooltip(0, description); - item->set_icon(0, _get_editor_icon(p_type)); + item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_type, base_type)); p_types[p_type] = item; } @@ -497,7 +465,11 @@ Object *CreateDialog::instance_selected() { if (custom != String()) { if (ScriptServer::is_global_class(custom)) { - return EditorNode::get_editor_data().script_class_instance(custom); + Object *obj = EditorNode::get_editor_data().script_class_instance(custom); + Node *n = Object::cast_to<Node>(obj); + if (n) + n->set_name(custom); + return obj; } return EditorNode::get_editor_data().instance_custom_type(selected->get_text(0), custom); } else { @@ -574,7 +546,7 @@ void CreateDialog::_update_favorite_list() { continue; TreeItem *ti = favorites->create_item(root); ti->set_text(0, l); - ti->set_icon(0, _get_editor_icon(l)); + ti->set_icon(0, EditorNode::get_singleton()->get_class_icon(l, base_type)); } emit_signal("favorites_updated"); } @@ -586,6 +558,7 @@ void CreateDialog::_history_selected() { return; search_box->set_text(item->get_text(0).get_slicec(' ', 0)); + favorites->deselect_all(); _update_search(); } @@ -596,6 +569,7 @@ void CreateDialog::_favorite_selected() { return; search_box->set_text(item->get_text(0).get_slicec(' ', 0)); + recent->deselect_all(); _update_search(); } diff --git a/editor/dependency_editor.cpp b/editor/dependency_editor.cpp index 62ae14c988..99b6955160 100644 --- a/editor/dependency_editor.cpp +++ b/editor/dependency_editor.cpp @@ -30,9 +30,9 @@ #include "dependency_editor.h" +#include "core/io/resource_loader.h" +#include "core/os/file_access.h" #include "editor_node.h" -#include "io/resource_loader.h" -#include "os/file_access.h" #include "scene/gui/margin_container.h" void DependencyEditor::_notification(int p_what) { @@ -198,12 +198,7 @@ void DependencyEditor::_update_list() { } String name = path.get_file(); - Ref<Texture> icon; - if (has_icon(type, "EditorIcons")) { - icon = get_icon(type, "EditorIcons"); - } else { - icon = get_icon("Object", "EditorIcons"); - } + Ref<Texture> icon = EditorNode::get_singleton()->get_class_icon(type); item->set_text(0, name); item->set_icon(0, icon); item->set_metadata(0, type); @@ -346,13 +341,7 @@ void DependencyEditorOwners::_fill_owners(EditorFileSystemDirectory *efsd) { if (!found) continue; - Ref<Texture> icon; - String type = efsd->get_file_type(i); - if (!has_icon(type, "EditorIcons")) { - icon = get_icon("Object", "EditorIcons"); - } else { - icon = get_icon(type, "EditorIcons"); - } + Ref<Texture> icon = EditorNode::get_singleton()->get_class_icon(efsd->get_file_type(i)); owners->add_item(efsd->get_file_path(i), icon); } @@ -460,7 +449,7 @@ void DependencyRemoveDialog::_build_removed_dependency_tree(const Vector<Removed } //List this file under this dependency - Ref<Texture> icon = has_icon(rd.file_type, "EditorIcons") ? get_icon(rd.file_type, "EditorIcons") : get_icon("Object", "EditorIcons"); + Ref<Texture> icon = EditorNode::get_singleton()->get_class_icon(rd.file_type); TreeItem *file_item = owners->create_item(tree_items[rd.dependency]); file_item->set_text(0, rd.file); file_item->set_icon(0, icon); @@ -515,7 +504,7 @@ void DependencyRemoveDialog::ok_pressed() { } if (dirs_to_delete.size() == 0) { - //If we only deleted files we should only need to tell the file system about the files we touched. + // If we only deleted files we should only need to tell the file system about the files we touched. for (int i = 0; i < files_to_delete.size(); ++i) EditorFileSystem::get_singleton()->update_file(files_to_delete[i]); } else { @@ -529,21 +518,25 @@ void DependencyRemoveDialog::ok_pressed() { } } - // if some dirs would be deleted, favorite dirs need to be updated - Vector<String> previous_favorite_dirs = EditorSettings::get_singleton()->get_favorite_dirs(); - Vector<String> new_favorite_dirs; + EditorFileSystem::get_singleton()->scan_changes(); + } - for (int i = 0; i < previous_favorite_dirs.size(); ++i) { - if (dirs_to_delete.find(previous_favorite_dirs[i] + "/") < 0) { - new_favorite_dirs.push_back(previous_favorite_dirs[i]); - } - } + // If some files/dirs would be deleted, favorite dirs need to be updated + Vector<String> previous_favorites = EditorSettings::get_singleton()->get_favorites(); + Vector<String> new_favorites; - if (new_favorite_dirs.size() < previous_favorite_dirs.size()) { - EditorSettings::get_singleton()->set_favorite_dirs(new_favorite_dirs); + for (int i = 0; i < previous_favorites.size(); ++i) { + if (previous_favorites[i].ends_with("/")) { + if (dirs_to_delete.find(previous_favorites[i]) < 0) + new_favorites.push_back(previous_favorites[i]); + } else { + if (files_to_delete.find(previous_favorites[i]) < 0) + new_favorites.push_back(previous_favorites[i]); } + } - EditorFileSystem::get_singleton()->scan_changes(); + if (new_favorites.size() < previous_favorites.size()) { + EditorSettings::get_singleton()->set_favorites(new_favorites); } } @@ -564,8 +557,9 @@ DependencyRemoveDialog::DependencyRemoveDialog() { ////////////// -void DependencyErrorDialog::show(const String &p_for_file, const Vector<String> &report) { +void DependencyErrorDialog::show(Mode p_mode, const String &p_for_file, const Vector<String> &report) { + mode = p_mode; for_file = p_for_file; set_title(TTR("Error loading:") + " " + p_for_file.get_file()); files->clear(); @@ -579,12 +573,7 @@ void DependencyErrorDialog::show(const String &p_for_file, const Vector<String> if (report[i].get_slice_count("::") > 0) type = report[i].get_slice("::", 1); - Ref<Texture> icon; - if (!has_icon(type, "EditorIcons")) { - icon = get_icon("Object", "EditorIcons"); - } else { - icon = get_icon(type, "EditorIcons"); - } + Ref<Texture> icon = EditorNode::get_singleton()->get_class_icon(type); TreeItem *ti = files->create_item(root); ti->set_text(0, dep); @@ -596,7 +585,14 @@ void DependencyErrorDialog::show(const String &p_for_file, const Vector<String> void DependencyErrorDialog::ok_pressed() { - EditorNode::get_singleton()->load_scene(for_file, true); + switch (mode) { + case MODE_SCENE: + EditorNode::get_singleton()->load_scene(for_file, true); + break; + case MODE_RESOURCE: + EditorNode::get_singleton()->load_resource(for_file, true); + break; + } } void DependencyErrorDialog::custom_action(const String &) { @@ -611,7 +607,7 @@ DependencyErrorDialog::DependencyErrorDialog() { files = memnew(Tree); files->set_hide_root(true); - vb->add_margin_child(TTR("Scene failed to load due to missing dependencies:"), files, true); + vb->add_margin_child(TTR("Load failed due to missing dependencies:"), files, true); files->set_v_size_flags(SIZE_EXPAND_FILL); files->set_custom_minimum_size(Size2(1, 200)); get_ok()->set_text(TTR("Open Anyway")); @@ -687,12 +683,7 @@ bool OrphanResourcesDialog::_fill_owners(EditorFileSystemDirectory *efsd, HashMa String type = efsd->get_file_type(i); - Ref<Texture> icon; - if (has_icon(type, "EditorIcons")) { - icon = get_icon(type, "EditorIcons"); - } else { - icon = get_icon("Object", "EditorIcons"); - } + Ref<Texture> icon = EditorNode::get_singleton()->get_class_icon(type); ti->set_icon(0, icon); int ds = efsd->get_file_deps(i).size(); ti->set_text(1, itos(ds)); diff --git a/editor/dependency_editor.h b/editor/dependency_editor.h index 4f268de748..e46df4c837 100644 --- a/editor/dependency_editor.h +++ b/editor/dependency_editor.h @@ -134,7 +134,15 @@ public: class DependencyErrorDialog : public ConfirmationDialog { GDCLASS(DependencyErrorDialog, ConfirmationDialog); +public: + enum Mode { + MODE_SCENE, + MODE_RESOURCE, + }; + +private: String for_file; + Mode mode; Button *fdep; Label *text; Tree *files; @@ -142,7 +150,7 @@ class DependencyErrorDialog : public ConfirmationDialog { void custom_action(const String &); public: - void show(const String &p_for_file, const Vector<String> &report); + void show(Mode p_mode, const String &p_for_file, const Vector<String> &report); DependencyErrorDialog(); }; diff --git a/editor/doc/SCsub b/editor/doc/SCsub index 04c9a827ef..2b1e889fb0 100644 --- a/editor/doc/SCsub +++ b/editor/doc/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.editor_sources, "*.cpp") - -Export('env') diff --git a/editor/doc/doc_data.cpp b/editor/doc/doc_data.cpp index fe1cf3484e..599f46d6d9 100644 --- a/editor/doc/doc_data.cpp +++ b/editor/doc/doc_data.cpp @@ -30,15 +30,15 @@ #include "doc_data.h" -#include "engine.h" -#include "global_constants.h" -#include "io/compression.h" -#include "io/marshalls.h" -#include "os/dir_access.h" -#include "project_settings.h" +#include "core/engine.h" +#include "core/global_constants.h" +#include "core/io/compression.h" +#include "core/io/marshalls.h" +#include "core/os/dir_access.h" +#include "core/project_settings.h" +#include "core/script_language.h" +#include "core/version.h" #include "scene/resources/theme.h" -#include "script_language.h" -#include "version.h" void DocData::merge_from(const DocData &p_data) { @@ -382,7 +382,11 @@ void DocData::generate(bool p_basic_types) { PropertyInfo arginfo = EV->get().arguments[i]; ArgumentDoc argument; argument.name = arginfo.name; - argument.type = Variant::get_type_name(arginfo.type); + if (arginfo.type == Variant::OBJECT && arginfo.class_name != StringName()) { + argument.type = arginfo.class_name.operator String(); + } else { + argument.type = Variant::get_type_name(arginfo.type); + } signal.arguments.push_back(argument); } @@ -501,7 +505,7 @@ void DocData::generate(bool p_basic_types) { ad.name = arginfo.name; if (arginfo.type == Variant::NIL) - ad.type = "var"; + ad.type = "Variant"; else ad.type = Variant::get_type_name(arginfo.type); @@ -514,7 +518,7 @@ void DocData::generate(bool p_basic_types) { if (mi.return_val.type == Variant::NIL) { if (mi.return_val.name != "") - method.return_type = "var"; + method.return_type = "Variant"; } else { method.return_type = Variant::get_type_name(mi.return_val.type); } diff --git a/editor/doc/doc_data.h b/editor/doc/doc_data.h index c7b70b5fb9..6633c123e6 100644 --- a/editor/doc/doc_data.h +++ b/editor/doc/doc_data.h @@ -31,9 +31,9 @@ #ifndef DOC_DATA_H #define DOC_DATA_H -#include "io/xml_parser.h" -#include "map.h" -#include "variant.h" +#include "core/io/xml_parser.h" +#include "core/map.h" +#include "core/variant.h" class DocData { public: diff --git a/editor/doc/doc_dump.cpp b/editor/doc/doc_dump.cpp index adbe23dcd5..f1c337605e 100644 --- a/editor/doc/doc_dump.cpp +++ b/editor/doc/doc_dump.cpp @@ -30,9 +30,9 @@ #include "doc_dump.h" -#include "os/file_access.h" +#include "core/os/file_access.h" +#include "core/version.h" #include "scene/main/node.h" -#include "version.h" static void _write_string(FileAccess *f, int p_tablevel, const String &p_string) { @@ -142,7 +142,7 @@ void DocDump::dump(const String &p_file) { if (arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) type_name = arginfo.hint_string; else if (arginfo.type == Variant::NIL) - type_name = "var"; + type_name = "Variant"; else type_name = Variant::get_type_name(arginfo.type); @@ -223,7 +223,7 @@ void DocDump::dump(const String &p_file) { hint = "Values: "; for (int j = 0; j < arginfo.hint_string.get_slice_count(","); j++) { if (j > 0) hint += ", "; - hint += arginfo.hint_string.get_slice(",", j) + "=" + itos(1 << j); + hint += arginfo.hint_string.get_slice(",", j) + "=" + itos((uint64_t)1 << j); } break; case PROPERTY_HINT_FILE: hint = "A file:"; break; diff --git a/editor/doc/doc_dump.h b/editor/doc/doc_dump.h index 48cf1a587b..99398b5d96 100644 --- a/editor/doc/doc_dump.h +++ b/editor/doc/doc_dump.h @@ -31,7 +31,7 @@ #ifndef DOC_DUMP_H #define DOC_DUMP_H -#include "class_db.h" +#include "core/class_db.h" class DocDump { public: diff --git a/editor/editor_about.cpp b/editor/editor_about.cpp index e4602f0f94..2f2840192a 100644 --- a/editor/editor_about.cpp +++ b/editor/editor_about.cpp @@ -47,7 +47,9 @@ void EditorAbout::_notification(int p_what) { Control *base = EditorNode::get_singleton()->get_gui_base(); Ref<Font> font = base->get_font("source", "EditorFonts"); _tpl_text->add_font_override("normal_font", font); + _tpl_text->add_constant_override("line_separation", 6 * EDSCALE); _license_text->add_font_override("normal_font", font); + _license_text->add_constant_override("line_separation", 6 * EDSCALE); _logo->set_texture(base->get_icon("Logo", "EditorIcons")); } break; } @@ -113,7 +115,6 @@ 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("OK")); set_hide_on_ok(true); set_resizable(true); diff --git a/editor/editor_asset_installer.cpp b/editor/editor_asset_installer.cpp index d99908a3c3..6af45f26ae 100644 --- a/editor/editor_asset_installer.cpp +++ b/editor/editor_asset_installer.cpp @@ -30,10 +30,10 @@ #include "editor_asset_installer.h" +#include "core/io/zip_io.h" +#include "core/os/dir_access.h" +#include "core/os/file_access.h" #include "editor_node.h" -#include "io/zip_io.h" -#include "os/dir_access.h" -#include "os/file_access.h" void EditorAssetInstaller::_update_subitems(TreeItem *p_item, bool p_check, bool p_first) { diff --git a/editor/editor_audio_buses.cpp b/editor/editor_audio_buses.cpp index 9c775be87e..6cd81626c7 100644 --- a/editor/editor_audio_buses.cpp +++ b/editor/editor_audio_buses.cpp @@ -30,10 +30,10 @@ #include "editor_audio_buses.h" +#include "core/io/resource_saver.h" +#include "core/os/keyboard.h" #include "editor_node.h" #include "filesystem_dock.h" -#include "io/resource_saver.h" -#include "os/keyboard.h" #include "servers/audio_server.h" void EditorAudioBus::_notification(int p_what) { @@ -765,10 +765,7 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) { if (!ClassDB::can_instance(E->get())) continue; - Ref<Texture> icon; - if (has_icon(E->get(), "EditorIcons")) { - icon = get_icon(E->get(), "EditorIcons"); - } + Ref<Texture> icon = EditorNode::get_singleton()->get_class_icon(E->get()); String name = E->get().operator String().replace("AudioEffect", ""); effect_options->add_item(name); effect_options->set_item_metadata(effect_options->get_item_count() - 1, E->get()); diff --git a/editor/editor_autoload_settings.cpp b/editor/editor_autoload_settings.cpp index d12c85861b..64742ff74c 100644 --- a/editor/editor_autoload_settings.cpp +++ b/editor/editor_autoload_settings.cpp @@ -30,9 +30,9 @@ #include "editor_autoload_settings.h" +#include "core/global_constants.h" +#include "core/project_settings.h" #include "editor_node.h" -#include "global_constants.h" -#include "project_settings.h" #include "scene/main/viewport.h" #include "scene/resources/packed_scene.h" @@ -185,6 +185,7 @@ void EditorAutoloadSettings::_autoload_edited() { if (path.begins_with("*")) path = path.substr(1, path.length()); + // Singleton autoloads are represented with a leading "*" in their path. if (checked) path = "*" + path; @@ -651,6 +652,7 @@ void EditorAutoloadSettings::autoload_add(const String &p_name, const String &p_ UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); undo_redo->create_action(TTR("Add AutoLoad")); + // Singleton autoloads are represented with a leading "*" in their path. undo_redo->add_do_property(ProjectSettings::get_singleton(), name, "*" + path); if (ProjectSettings::get_singleton()->has_setting(name)) { diff --git a/editor/editor_builders.py b/editor/editor_builders.py index 6c2f9e298e..9e9fe752b4 100644 --- a/editor/editor_builders.py +++ b/editor/editor_builders.py @@ -9,32 +9,6 @@ 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] @@ -146,267 +120,5 @@ def make_translations_header(target, source, env): 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 69c120bb3c..9420452da1 100644 --- a/editor/editor_data.cpp +++ b/editor/editor_data.cpp @@ -30,12 +30,12 @@ #include "editor_data.h" +#include "core/io/resource_loader.h" +#include "core/os/dir_access.h" +#include "core/os/file_access.h" +#include "core/project_settings.h" #include "editor_node.h" #include "editor_settings.h" -#include "io/resource_loader.h" -#include "os/dir_access.h" -#include "os/file_access.h" -#include "project_settings.h" #include "scene/resources/packed_scene.h" void EditorHistory::cleanup_history() { @@ -869,7 +869,7 @@ bool EditorData::script_class_is_parent(const String &p_class, const String &p_i return true; } -StringName EditorData::script_class_get_base(const String &p_class) { +StringName EditorData::script_class_get_base(const String &p_class) const { if (!ScriptServer::is_global_class(p_class)) return StringName(); @@ -895,24 +895,48 @@ Object *EditorData::script_class_instance(const String &p_class) { RES script = ResourceLoader::load(ScriptServer::get_global_class_path(p_class)); if (script.is_valid()) obj->set_script(script.get_ref_ptr()); - - RES icon = ResourceLoader::load(script_class_get_icon_path(p_class)); - if (icon.is_valid()) - obj->set_meta("_editor_icon", icon); - return obj; } } return NULL; } +void EditorData::script_class_set_icon_path(const String &p_class, const String &p_icon_path) { + _script_class_icon_paths[p_class] = p_icon_path; +} + +String EditorData::script_class_get_icon_path(const String &p_class) const { + if (!ScriptServer::is_global_class(p_class)) + return String(); + + String current = p_class; + String ret = _script_class_icon_paths[current]; + while (ret.empty()) { + current = script_class_get_base(current); + if (!ScriptServer::is_global_class(current)) + return String(); + ret = _script_class_icon_paths.has(current) ? _script_class_icon_paths[current] : String(); + } + + return ret; +} + +StringName EditorData::script_class_get_name(const String &p_path) const { + return _script_class_file_to_path.has(p_path) ? _script_class_file_to_path[p_path] : StringName(); +} + +void EditorData::script_class_set_name(const String &p_path, const StringName &p_class) { + _script_class_file_to_path[p_path] = p_class; +} + void EditorData::script_class_save_icon_paths() { List<StringName> keys; _script_class_icon_paths.get_key_list(&keys); Dictionary d; for (List<StringName>::Element *E = keys.front(); E; E = E->next()) { - d[E->get()] = _script_class_icon_paths[E->get()]; + if (ScriptServer::is_global_class(E->get())) + d[E->get()] = _script_class_icon_paths[E->get()]; } ProjectSettings::get_singleton()->set("_global_script_class_icons", d); @@ -927,8 +951,11 @@ void EditorData::script_class_load_icon_paths() { d.get_key_list(&keys); for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - String key = E->get().operator String(); - _script_class_icon_paths[key] = d[key]; + String name = E->get().operator String(); + _script_class_icon_paths[name] = d[name]; + + String path = ScriptServer::get_global_class_path(name); + script_class_set_name(path, name); } } diff --git a/editor/editor_data.h b/editor/editor_data.h index 285769aa78..87a76ee5ba 100644 --- a/editor/editor_data.h +++ b/editor/editor_data.h @@ -31,12 +31,12 @@ #ifndef EDITOR_DATA_H #define EDITOR_DATA_H +#include "core/list.h" +#include "core/pair.h" +#include "core/undo_redo.h" #include "editor/editor_plugin.h" #include "editor/plugins/script_editor_plugin.h" -#include "list.h" -#include "pair.h" #include "scene/resources/texture.h" -#include "undo_redo.h" class EditorHistory { @@ -147,6 +147,7 @@ private: bool _find_updated_instances(Node *p_root, Node *p_node, Set<String> &checked_paths); HashMap<StringName, String> _script_class_icon_paths; + HashMap<String, StringName> _script_class_file_to_path; public: EditorPlugin *get_editor(Object *p_object); @@ -214,10 +215,14 @@ public: 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); + StringName script_class_get_base(const String &p_class) const; Object *script_class_instance(const String &p_class); - String script_class_get_icon_path(const String &p_class) const { return _script_class_icon_paths.has(p_class) ? _script_class_icon_paths[p_class] : String(); } - void script_class_set_icon_path(const String &p_class, const String &p_icon_path) { _script_class_icon_paths[p_class] = p_icon_path; } + + StringName script_class_get_name(const String &p_path) const; + void script_class_set_name(const String &p_path, const StringName &p_class); + + String script_class_get_icon_path(const String &p_class) const; + void script_class_set_icon_path(const String &p_class, const String &p_icon_path); void script_class_clear_icon_paths() { _script_class_icon_paths.clear(); } void script_class_save_icon_paths(); void script_class_load_icon_paths(); diff --git a/editor/editor_dir_dialog.cpp b/editor/editor_dir_dialog.cpp index c094b2b559..6f80b6bea4 100644 --- a/editor/editor_dir_dialog.cpp +++ b/editor/editor_dir_dialog.cpp @@ -30,11 +30,11 @@ #include "editor_dir_dialog.h" +#include "core/os/keyboard.h" +#include "core/os/os.h" #include "editor/editor_file_system.h" #include "editor/editor_settings.h" #include "editor_scale.h" -#include "os/keyboard.h" -#include "os/os.h" void EditorDirDialog::_update_dir(TreeItem *p_item, EditorFileSystemDirectory *p_dir, const String &p_select_path) { updating = true; diff --git a/editor/editor_dir_dialog.h b/editor/editor_dir_dialog.h index 7c19e7de38..a9dc7accfe 100644 --- a/editor/editor_dir_dialog.h +++ b/editor/editor_dir_dialog.h @@ -31,8 +31,8 @@ #ifndef EDITOR_DIR_DIALOG_H #define EDITOR_DIR_DIALOG_H +#include "core/os/dir_access.h" #include "editor/editor_file_system.h" -#include "os/dir_access.h" #include "scene/gui/dialogs.h" #include "scene/gui/tree.h" diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp index e46fe96885..209a006a06 100644 --- a/editor/editor_export.cpp +++ b/editor/editor_export.cpp @@ -30,20 +30,20 @@ #include "editor_export.h" +#include "core/io/config_file.h" +#include "core/io/resource_loader.h" +#include "core/io/resource_saver.h" +#include "core/io/zip_io.h" +#include "core/os/file_access.h" +#include "core/project_settings.h" +#include "core/script_language.h" +#include "core/version.h" #include "editor/editor_file_system.h" #include "editor/plugins/script_editor_plugin.h" #include "editor_node.h" #include "editor_settings.h" -#include "io/config_file.h" -#include "io/resource_loader.h" -#include "io/resource_saver.h" -#include "io/zip_io.h" -#include "os/file_access.h" -#include "project_settings.h" #include "scene/resources/scene_format_text.h" -#include "script_language.h" #include "thirdparty/misc/md5.h" -#include "version.h" static int _get_pad(int p_alignment, int p_n) { @@ -144,6 +144,17 @@ String EditorExportPreset::get_include_filter() const { return include_filter; } +void EditorExportPreset::set_export_path(const String &p_path) { + + export_path = p_path; + EditorExport::singleton->save_presets(); +} + +String EditorExportPreset::get_export_path() const { + + return export_path; +} + void EditorExportPreset::set_exclude_filter(const String &p_exclude) { exclude_filter = p_exclude; @@ -213,6 +224,7 @@ String EditorExportPreset::get_custom_features() const { EditorExportPreset::EditorExportPreset() { + export_path = ""; export_filter = EXPORT_ALL_RESOURCES; runnable = false; } @@ -667,7 +679,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & String remap = F->get(); String feature = remap.get_slice(".", 1); - if (feature == "fallback" || features.has(feature)) { + if (features.has(feature)) { remap_features.insert(feature); } } @@ -1034,6 +1046,7 @@ void EditorExport::_save() { } config->set_value(section, "include_filter", preset->get_include_filter()); config->set_value(section, "exclude_filter", preset->get_exclude_filter()); + config->set_value(section, "export_path", preset->get_export_path()); config->set_value(section, "patch_list", preset->get_patches()); String option_section = "preset." + itos(i) + ".options"; @@ -1097,6 +1110,7 @@ Ref<EditorExportPreset> EditorExport::get_export_preset(int p_idx) { void EditorExport::remove_export_preset(int p_idx) { export_presets.remove(p_idx); + save_presets(); } void EditorExport::add_export_plugin(const Ref<EditorExportPlugin> &p_plugin) { @@ -1188,6 +1202,7 @@ void EditorExport::load_config() { preset->set_include_filter(config->get_value(section, "include_filter")); preset->set_exclude_filter(config->get_value(section, "exclude_filter")); + preset->set_export_path(config->get_value(section, "export_path", "")); Vector<String> patch_list = config->get_value(section, "patch_list"); @@ -1336,18 +1351,21 @@ bool EditorExportPlatformPC::can_export(const Ref<EditorExportPreset> &p_preset, return valid; } -String EditorExportPlatformPC::get_binary_extension(const Ref<EditorExportPreset> &p_preset) const { +List<String> EditorExportPlatformPC::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const { + List<String> list; for (Map<String, String>::Element *E = extensions.front(); E; E = E->next()) { if (p_preset->get(E->key())) { - return extensions[E->key()]; + list.push_back(extensions[E->key()]); + return list; } } if (extensions.has("default")) { - return extensions["default"]; + list.push_back(extensions["default"]); + return list; } - return ""; + return list; } Error EditorExportPlatformPC::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { @@ -1457,7 +1475,6 @@ void EditorExportPlatformPC::resolve_platform_feature_priorities(const Ref<Edito if (p_features.has("bptc")) { if (p_preset->has("texture_format/no_bptc_fallbacks")) { p_features.erase("s3tc"); - p_features.erase("fallback"); } } } diff --git a/editor/editor_export.h b/editor/editor_export.h index b984d66a1b..380b33cd17 100644 --- a/editor/editor_export.h +++ b/editor/editor_export.h @@ -31,16 +31,16 @@ #ifndef EDITOR_EXPORT_H #define EDITOR_EXPORT_H -#include "os/dir_access.h" -#include "resource.h" +#include "core/os/dir_access.h" +#include "core/resource.h" #include "scene/main/node.h" #include "scene/main/timer.h" #include "scene/resources/texture.h" -class EditorProgress; class FileAccess; class EditorExportPlatform; class EditorFileSystemDirectory; +struct EditorProgress; class EditorExportPreset : public Reference { @@ -57,6 +57,7 @@ private: ExportFilter export_filter; String include_filter; String exclude_filter; + String export_path; String exporter; Set<String> selected_files; @@ -114,6 +115,9 @@ public: void set_custom_features(const String &p_custom_features); String get_custom_features() const; + void set_export_path(const String &p_path); + String get_export_path() const; + const List<PropertyInfo> &get_properties() const { return properties; } EditorExportPreset(); @@ -241,7 +245,7 @@ public: virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const = 0; - virtual String get_binary_extension(const Ref<EditorExportPreset> &p_preset) const = 0; + virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const = 0; virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) = 0; virtual Error export_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0); virtual Error export_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0); @@ -388,7 +392,7 @@ public: virtual Ref<Texture> get_logo() const; virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const; - virtual String get_binary_extension(const Ref<EditorExportPreset> &p_preset) const; + virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const; virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0); void set_extension(const String &p_extension, const String &p_feature_key = "default"); diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp index d240f4ed25..b39f910182 100644 --- a/editor/editor_file_dialog.cpp +++ b/editor/editor_file_dialog.cpp @@ -29,14 +29,14 @@ /*************************************************************************/ #include "editor_file_dialog.h" +#include "core/os/file_access.h" +#include "core/os/keyboard.h" +#include "core/os/os.h" +#include "core/print_string.h" #include "dependency_editor.h" #include "editor_resource_preview.h" #include "editor_scale.h" #include "editor_settings.h" -#include "os/file_access.h" -#include "os/keyboard.h" -#include "os/os.h" -#include "print_string.h" #include "scene/gui/center_container.h" #include "scene/gui/label.h" #include "scene/gui/margin_container.h" @@ -198,8 +198,22 @@ void EditorFileDialog::update_dir() { dir->set_text(dir_access->get_current_dir()); - // Disable "Open" button only when we in selecting file(s) mode or open dir mode. + // Disable "Open" button only when selecting file(s) mode. get_ok()->set_disabled(_is_open_should_be_disabled()); + switch (mode) { + + case MODE_OPEN_FILE: + case MODE_OPEN_FILES: + get_ok()->set_text(TTR("Open")); + break; + case MODE_OPEN_DIR: + get_ok()->set_text(TTR("Select Current Folder")); + break; + case MODE_OPEN_ANY: + case MODE_SAVE_FILE: + // FIXME: Implement, or refactor to avoid duplication with set_mode + break; + } } void EditorFileDialog::_dir_entered(String p_dir) { @@ -269,7 +283,7 @@ void EditorFileDialog::_post_popup() { set_process_unhandled_input(true); } -void EditorFileDialog::_thumbnail_result(const String &p_path, const Ref<Texture> &p_preview, const Variant &p_udata) { +void EditorFileDialog::_thumbnail_result(const String &p_path, const Ref<Texture> &p_preview, const Ref<Texture> &p_small_preview, const Variant &p_udata) { if (display_mode == DISPLAY_LIST || p_preview.is_null()) return; @@ -284,7 +298,7 @@ void EditorFileDialog::_thumbnail_result(const String &p_path, const Ref<Texture } } -void EditorFileDialog::_thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Variant &p_udata) { +void EditorFileDialog::_thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Ref<Texture> &p_small_preview, const Variant &p_udata) { set_process(false); preview_waiting = false; @@ -453,6 +467,8 @@ void EditorFileDialog::_item_selected(int p_item) { file->set_text(d["name"]); _request_single_thumbnail(get_current_dir().plus_file(get_current_file())); + } else if (mode == MODE_OPEN_DIR) { + get_ok()->set_text(TTR("Select This Folder")); } get_ok()->set_disabled(_is_open_should_be_disabled()); @@ -485,13 +501,18 @@ void EditorFileDialog::_items_clear_selection() { case MODE_OPEN_FILE: case MODE_OPEN_FILES: get_ok()->set_text(TTR("Open")); - get_ok()->set_disabled(item_list->is_anything_selected() == false); + get_ok()->set_disabled(!item_list->is_anything_selected()); break; case MODE_OPEN_DIR: get_ok()->set_disabled(false); get_ok()->set_text(TTR("Select Current Folder")); break; + + case MODE_OPEN_ANY: + case MODE_SAVE_FILE: + // FIXME: Implement, or refactor to avoid duplication with set_mode + break; } } @@ -559,7 +580,7 @@ void EditorFileDialog::_item_list_item_rmb_selected(int p_item, const Vector2 &p if (single_item_selected) { item_menu->add_separator(); Dictionary item_meta = item_list->get_item_metadata(p_item); - String item_text = item_meta["dir"] ? TTR("Open In File Manager") : TTR("Show In File Manager"); + String item_text = item_meta["dir"] ? TTR("Open in File Manager") : TTR("Show in File Manager"); item_menu->add_icon_item(get_icon("Filesystem", "EditorIcons"), item_text, ITEM_MENU_SHOW_IN_EXPLORER); } @@ -584,7 +605,7 @@ void EditorFileDialog::_item_list_rmb_clicked(const Vector2 &p_pos) { } item_menu->add_icon_item(get_icon("Reload", "EditorIcons"), TTR("Refresh"), ITEM_MENU_REFRESH, KEY_F5); item_menu->add_separator(); - item_menu->add_icon_item(get_icon("Filesystem", "EditorIcons"), TTR("Open In File Manager"), ITEM_MENU_SHOW_IN_EXPLORER); + item_menu->add_icon_item(get_icon("Filesystem", "EditorIcons"), TTR("Open in File Manager"), ITEM_MENU_SHOW_IN_EXPLORER); item_menu->set_position(item_list->get_global_position() + p_pos); item_menu->popup(); @@ -637,7 +658,7 @@ bool EditorFileDialog::_is_open_should_be_disabled() { Vector<int> items = item_list->get_selected_items(); if (items.size() == 0) - return true; + return mode != MODE_OPEN_DIR; // In "Open folder" mode, having nothing selected picks the current folder. for (int i = 0; i < items.size(); i++) { @@ -1114,14 +1135,10 @@ void EditorFileDialog::_update_drives() { } void EditorFileDialog::_favorite_selected(int p_idx) { - - Vector<String> favorited = EditorSettings::get_singleton()->get_favorite_dirs(); - ERR_FAIL_INDEX(p_idx, favorited.size()); - - dir_access->change_dir(favorited[p_idx]); + dir_access->change_dir(favorites->get_item_metadata(p_idx)); file->set_text(""); - invalidate(); update_dir(); + invalidate(); _push_history(); } @@ -1130,7 +1147,7 @@ void EditorFileDialog::_favorite_move_up() { int current = favorites->get_current(); if (current > 0 && current < favorites->get_item_count()) { - Vector<String> favorited = EditorSettings::get_singleton()->get_favorite_dirs(); + Vector<String> favorited = EditorSettings::get_singleton()->get_favorites(); int a_idx = favorited.find(String(favorites->get_item_metadata(current - 1))); int b_idx = favorited.find(String(favorites->get_item_metadata(current))); @@ -1139,7 +1156,7 @@ void EditorFileDialog::_favorite_move_up() { return; SWAP(favorited.write[a_idx], favorited.write[b_idx]); - EditorSettings::get_singleton()->set_favorite_dirs(favorited); + EditorSettings::get_singleton()->set_favorites(favorited); _update_favorites(); update_file_list(); @@ -1150,7 +1167,7 @@ void EditorFileDialog::_favorite_move_down() { int current = favorites->get_current(); if (current >= 0 && current < favorites->get_item_count() - 1) { - Vector<String> favorited = EditorSettings::get_singleton()->get_favorite_dirs(); + Vector<String> favorited = EditorSettings::get_singleton()->get_favorites(); int a_idx = favorited.find(String(favorites->get_item_metadata(current + 1))); int b_idx = favorited.find(String(favorites->get_item_metadata(current))); @@ -1159,7 +1176,7 @@ void EditorFileDialog::_favorite_move_down() { return; SWAP(favorited.write[a_idx], favorited.write[b_idx]); - EditorSettings::get_singleton()->set_favorite_dirs(favorited); + EditorSettings::get_singleton()->set_favorites(favorited); _update_favorites(); update_file_list(); @@ -1171,27 +1188,34 @@ void EditorFileDialog::_update_favorites() { bool res = access == ACCESS_RESOURCES; String current = get_current_dir(); - Ref<Texture> star = get_icon("Favorites", "EditorIcons"); + Ref<Texture> folder_icon = get_icon("Folder", "EditorIcons"); favorites->clear(); favorite->set_pressed(false); - Vector<String> favorited = EditorSettings::get_singleton()->get_favorite_dirs(); + Vector<String> favorited = EditorSettings::get_singleton()->get_favorites(); for (int i = 0; i < favorited.size(); i++) { bool cres = favorited[i].begins_with("res://"); if (cres != res) continue; String name = favorited[i]; - - bool setthis = name == current; + bool setthis = false; if (res && name == "res://") { + if (name == current) + setthis = true; name = "/"; + } else if (name.ends_with("/")) { + if (name == current) + setthis = true; + name = name.substr(0, name.length() - 1); + name = name.get_file(); + + favorites->add_item(name, folder_icon); } else { - name = name.get_file() + "/"; + continue; // We don't handle favorite files here } - favorites->add_item(name, star); favorites->set_item_metadata(favorites->get_item_count() - 1, favorited[i]); if (setthis) { @@ -1206,7 +1230,7 @@ void EditorFileDialog::_favorite_toggled(bool p_toggle) { String cd = get_current_dir(); - Vector<String> favorited = EditorSettings::get_singleton()->get_favorite_dirs(); + Vector<String> favorited = EditorSettings::get_singleton()->get_favorites(); bool found = false; for (int i = 0; i < favorited.size(); i++) { @@ -1228,7 +1252,7 @@ void EditorFileDialog::_favorite_toggled(bool p_toggle) { favorite->set_pressed(true); } - EditorSettings::get_singleton()->set_favorite_dirs(favorited); + EditorSettings::get_singleton()->set_favorites(favorited); _update_favorites(); } diff --git a/editor/editor_file_dialog.h b/editor/editor_file_dialog.h index b1f8f1108c..56cefb9a47 100644 --- a/editor/editor_file_dialog.h +++ b/editor/editor_file_dialog.h @@ -31,7 +31,7 @@ #ifndef EDITORFILEDIALOG_H #define EDITORFILEDIALOG_H -#include "os/dir_access.h" +#include "core/os/dir_access.h" #include "scene/gui/box_container.h" #include "scene/gui/dialogs.h" #include "scene/gui/item_list.h" @@ -190,8 +190,8 @@ private: void _save_to_recent(); //callback function is callback(String p_path,Ref<Texture> preview,Variant udata) preview null if could not load - void _thumbnail_result(const String &p_path, const Ref<Texture> &p_preview, const Variant &p_udata); - void _thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Variant &p_udata); + void _thumbnail_result(const String &p_path, const Ref<Texture> &p_preview, const Ref<Texture> &p_small_preview, const Variant &p_udata); + void _thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Ref<Texture> &p_small_preview, const Variant &p_udata); void _request_single_thumbnail(const String &p_path); void _unhandled_input(const Ref<InputEvent> &p_event); diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index 9562a8c63c..a99c9656ba 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -30,16 +30,16 @@ #include "editor_file_system.h" +#include "core/io/resource_import.h" +#include "core/io/resource_loader.h" +#include "core/io/resource_saver.h" +#include "core/os/file_access.h" +#include "core/os/os.h" +#include "core/project_settings.h" +#include "core/variant_parser.h" #include "editor_node.h" #include "editor_resource_preview.h" #include "editor_settings.h" -#include "io/resource_import.h" -#include "io/resource_loader.h" -#include "io/resource_saver.h" -#include "os/file_access.h" -#include "os/os.h" -#include "project_settings.h" -#include "variant_parser.h" EditorFileSystem *EditorFileSystem::singleton = NULL; @@ -389,7 +389,7 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo memdelete(f); - // Read the md5's from a separate file (so the import parameters aren't dependant on the file version + // Read the md5's from a separate file (so the import parameters aren't dependent on the file version String base_path = ResourceFormatImporter::get_singleton()->get_import_base_path(p_path); FileAccess *md5s = FileAccess::open(base_path + ".md5", FileAccess::READ, &err); if (!md5s) { // No md5's stored for this resource @@ -1305,11 +1305,6 @@ void EditorFileSystem::_save_late_updated_files() { } } -void EditorFileSystem::_resource_saved(const String &p_path) { - - EditorFileSystem::get_singleton()->update_file(p_path); -} - Vector<String> EditorFileSystem::_get_dependencies(const String &p_path) { List<String> deps; @@ -1358,6 +1353,7 @@ void EditorFileSystem::_scan_script_classes(EditorFileSystemDirectory *p_dir) { } ScriptServer::add_global_class(files[i]->script_class_name, files[i]->script_class_extends, lang, p_dir->get_file_path(i)); EditorNode::get_editor_data().script_class_set_icon_path(files[i]->script_class_name, files[i]->script_class_icon_path); + EditorNode::get_editor_data().script_class_set_name(files[i]->file, files[i]->script_class_name); } for (int i = 0; i < p_dir->get_subdir_count(); i++) { _scan_script_classes(p_dir->get_subdir(i)); @@ -1377,7 +1373,6 @@ void EditorFileSystem::update_script_classes() { ScriptServer::save_global_classes(); EditorNode::get_editor_data().script_class_save_icon_paths(); - emit_signal("script_classes_updated"); } void EditorFileSystem::_queue_update_script_classes() { @@ -1479,12 +1474,16 @@ void EditorFileSystem::_reimport_file(const String &p_file) { cf.instance(); Error err = cf->load(p_file + ".import"); if (err == OK) { - List<String> sk; - cf->get_section_keys("params", &sk); - for (List<String>::Element *E = sk.front(); E; E = E->next()) { - params[E->get()] = cf->get_value("params", E->get()); + if (cf->has_section("params")) { + List<String> sk; + cf->get_section_keys("params", &sk); + for (List<String>::Element *E = sk.front(); E; E = E->next()) { + params[E->get()] = cf->get_value("params", E->get()); + } + } + if (cf->has_section("remap")) { + importer_name = cf->get_value("remap", "importer"); } - importer_name = cf->get_value("remap", "importer"); } } else { @@ -1701,6 +1700,17 @@ void EditorFileSystem::reimport_files(const Vector<String> &p_files) { emit_signal("resources_reimported", p_files); } +Error EditorFileSystem::_resource_import(const String &p_path) { + + Vector<String> files; + files.push_back(p_path); + + singleton->update_file(p_path); + singleton->reimport_files(files); + + return OK; +} + void EditorFileSystem::_bind_methods() { ClassDB::bind_method(D_METHOD("get_filesystem"), &EditorFileSystem::get_filesystem); @@ -1716,7 +1726,6 @@ void EditorFileSystem::_bind_methods() { ADD_SIGNAL(MethodInfo("filesystem_changed")); ADD_SIGNAL(MethodInfo("sources_changed", PropertyInfo(Variant::BOOL, "exist"))); ADD_SIGNAL(MethodInfo("resources_reimported", PropertyInfo(Variant::POOL_STRING_ARRAY, "resources"))); - ADD_SIGNAL(MethodInfo("script_classes_updated")); } void EditorFileSystem::_update_extensions() { @@ -1741,6 +1750,7 @@ void EditorFileSystem::_update_extensions() { EditorFileSystem::EditorFileSystem() { + ResourceLoader::import = _resource_import; reimport_on_missing_imported_files = GLOBAL_DEF("editor/reimport_missing_imported_files", true); singleton = this; @@ -1757,7 +1767,6 @@ EditorFileSystem::EditorFileSystem() { abort_scan = false; scanning_changes = false; scanning_changes_done = false; - ResourceSaver::set_save_callback(_resource_saved); DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES); if (da->change_dir("res://.import") != OK) { diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h index 75ca79932f..f6eef2a152 100644 --- a/editor/editor_file_system.h +++ b/editor/editor_file_system.h @@ -31,11 +31,11 @@ #ifndef EDITOR_FILE_SYSTEM_H #define EDITOR_FILE_SYSTEM_H -#include "os/dir_access.h" -#include "os/thread.h" -#include "os/thread_safe.h" +#include "core/os/dir_access.h" +#include "core/os/thread.h" +#include "core/os/thread_safe.h" +#include "core/set.h" #include "scene/main/node.h" -#include "set.h" class FileAccess; struct EditorProgressBG; @@ -204,8 +204,6 @@ class EditorFileSystem : public Node { bool _update_scan_actions(); - static void _resource_saved(const String &p_path); - void _update_extensions(); void _reimport_file(const String &p_file); @@ -230,6 +228,8 @@ class EditorFileSystem : public Node { String _get_global_script_class(const String &p_type, const String &p_path, String *r_extends, String *r_icon_path) const; + static Error _resource_import(const String &p_path); + protected: void _notification(int p_what); static void _bind_methods(); diff --git a/editor/editor_folding.cpp b/editor/editor_folding.cpp new file mode 100644 index 0000000000..eec4438f96 --- /dev/null +++ b/editor/editor_folding.cpp @@ -0,0 +1,175 @@ +#include "editor_folding.h" + +#include "core/os/file_access.h" +#include "editor_settings.h" + +PoolVector<String> EditorFolding::_get_unfolds(const Object *p_object) { + + PoolVector<String> sections; + sections.resize(p_object->editor_get_section_folding().size()); + if (sections.size()) { + PoolVector<String>::Write w = sections.write(); + int idx = 0; + for (const Set<String>::Element *E = p_object->editor_get_section_folding().front(); E; E = E->next()) { + w[idx++] = E->get(); + } + } + + return sections; +} + +void EditorFolding::save_resource_folding(const RES &p_resource, const String &p_path) { + Ref<ConfigFile> config; + config.instance(); + PoolVector<String> unfolds = _get_unfolds(p_resource.ptr()); + config->set_value("folding", "sections_unfolded", unfolds); + + String path = EditorSettings::get_singleton()->get_project_settings_dir(); + String file = p_path.get_file() + "-folding-" + p_path.md5_text() + ".cfg"; + file = EditorSettings::get_singleton()->get_project_settings_dir().plus_file(file); + config->save(file); +} + +void EditorFolding::_set_unfolds(Object *p_object, const PoolVector<String> &p_unfolds) { + + int uc = p_unfolds.size(); + PoolVector<String>::Read r = p_unfolds.read(); + p_object->editor_clear_section_folding(); + for (int i = 0; i < uc; i++) { + p_object->editor_set_section_unfold(r[i], true); + } +} + +void EditorFolding::load_resource_folding(RES p_resource, const String &p_path) { + + Ref<ConfigFile> config; + config.instance(); + + String path = EditorSettings::get_singleton()->get_project_settings_dir(); + String file = p_path.get_file() + "-folding-" + p_path.md5_text() + ".cfg"; + file = EditorSettings::get_singleton()->get_project_settings_dir().plus_file(file); + + if (config->load(file) != OK) { + return; + } + + PoolVector<String> unfolds; + + if (config->has_section_key("folding", "sections_unfolded")) { + unfolds = config->get_value("folding", "sections_unfolded"); + } + _set_unfolds(p_resource.ptr(), unfolds); +} + +void EditorFolding::_fill_folds(const Node *p_root, const Node *p_node, Array &p_folds, Array &resource_folds, Set<RES> &resources) { + if (p_root != p_node) { + if (!p_node->get_owner()) { + return; //not owned, bye + } + if (p_node->get_owner() != p_root && !p_root->is_editable_instance(p_node)) { + return; + } + } + + PoolVector<String> unfolds = _get_unfolds(p_node); + + if (unfolds.size()) { + p_folds.push_back(p_root->get_path_to(p_node)); + p_folds.push_back(unfolds); + } + + List<PropertyInfo> plist; + p_node->get_property_list(&plist); + for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) { + if (E->get().type == Variant::OBJECT) { + RES res = p_node->get(E->get().name); + if (res.is_valid() && !resources.has(res) && res->get_path() != String() && !res->get_path().is_resource_file()) { + + PoolVector<String> res_unfolds = _get_unfolds(res.ptr()); + resource_folds.push_back(res->get_path()); + resource_folds.push_back(res_unfolds); + resources.insert(res); + } + } + } + + for (int i = 0; i < p_node->get_child_count(); i++) { + _fill_folds(p_root, p_node->get_child(i), p_folds, resource_folds, resources); + } +} +void EditorFolding::save_scene_folding(const Node *p_scene, const String &p_path) { + + Ref<ConfigFile> config; + config.instance(); + + Array unfolds, res_unfolds; + Set<RES> resources; + _fill_folds(p_scene, p_scene, unfolds, res_unfolds, resources); + + config->set_value("folding", "node_unfolds", unfolds); + config->set_value("folding", "resource_unfolds", res_unfolds); + + String path = EditorSettings::get_singleton()->get_project_settings_dir(); + String file = p_path.get_file() + "-folding-" + p_path.md5_text() + ".cfg"; + file = EditorSettings::get_singleton()->get_project_settings_dir().plus_file(file); + config->save(file); +} +void EditorFolding::load_scene_folding(Node *p_scene, const String &p_path) { + + Ref<ConfigFile> config; + config.instance(); + + String path = EditorSettings::get_singleton()->get_project_settings_dir(); + String file = p_path.get_file() + "-folding-" + p_path.md5_text() + ".cfg"; + file = EditorSettings::get_singleton()->get_project_settings_dir().plus_file(file); + + if (config->load(file) != OK) { + return; + } + + Array unfolds; + if (config->has_section_key("folding", "node_unfolds")) { + unfolds = config->get_value("folding", "node_unfolds"); + } + Array res_unfolds; + if (config->has_section_key("folding", "resource_unfolds")) { + res_unfolds = config->get_value("folding", "resource_unfolds"); + } + + ERR_FAIL_COND(unfolds.size() & 1); + ERR_FAIL_COND(res_unfolds.size() & 1); + + for (int i = 0; i < unfolds.size(); i += 2) { + NodePath path = unfolds[i]; + PoolVector<String> un = unfolds[i + 1]; + Node *node = p_scene->get_node(path); + if (!node) { + continue; + } + _set_unfolds(node, un); + } + + for (int i = 0; i < res_unfolds.size(); i += 2) { + String path = res_unfolds[i]; + RES res; + if (ResourceCache::has(path)) { + res = RES(ResourceCache::get(path)); + } + if (res.is_null()) { + continue; + } + + PoolVector<String> unfolds = res_unfolds[i + 1]; + _set_unfolds(res.ptr(), unfolds); + } +} + +bool EditorFolding::has_folding_data(const String &p_path) { + String path = EditorSettings::get_singleton()->get_project_settings_dir(); + String file = p_path.get_file() + "-folding-" + p_path.md5_text() + ".cfg"; + file = EditorSettings::get_singleton()->get_project_settings_dir().plus_file(file); + return FileAccess::exists(file); +} + +EditorFolding::EditorFolding() { +} diff --git a/editor/editor_folding.h b/editor/editor_folding.h new file mode 100644 index 0000000000..cfd4b5466d --- /dev/null +++ b/editor/editor_folding.h @@ -0,0 +1,25 @@ +#ifndef EDITOR_FOLDING_H +#define EDITOR_FOLDING_H + +#include "scene/main/node.h" + +class EditorFolding { + + PoolVector<String> _get_unfolds(const Object *p_object); + void _set_unfolds(Object *p_object, const PoolVector<String> &p_unfolds); + + void _fill_folds(const Node *p_root, const Node *p_node, Array &p_folds, Array &resource_folds, Set<RES> &resources); + +public: + void save_resource_folding(const RES &p_resource, const String &p_path); + void load_resource_folding(RES p_resource, const String &p_path); + + void save_scene_folding(const Node *p_scene, const String &p_path); + void load_scene_folding(Node *p_scene, const String &p_path); + + bool has_folding_data(const String &p_path); + + EditorFolding(); +}; + +#endif // EDITOR_FOLDING_H diff --git a/editor/editor_fonts.cpp b/editor/editor_fonts.cpp index 8e0d92267c..8b1818b595 100644 --- a/editor/editor_fonts.cpp +++ b/editor/editor_fonts.cpp @@ -37,37 +37,11 @@ #include "scene/resources/default_theme/default_theme.h" #include "scene/resources/dynamic_font.h" -static Ref<BitmapFont> make_font(int p_height, int p_ascent, int p_valign, int p_charcount, const int *p_chars, const Ref<Texture> &p_texture) { - - Ref<BitmapFont> font(memnew(BitmapFont)); - font->add_texture(p_texture); - - for (int i = 0; i < p_charcount; i++) { - - const int *c = &p_chars[i * 8]; - - int chr = c[0]; - Rect2 frect; - frect.position.x = c[1]; - frect.position.y = c[2]; - frect.size.x = c[3]; - frect.size.y = c[4]; - Point2 align(c[5], c[6] + p_valign); - int advance = c[7]; - - font->add_char(chr, 0, frect, align, advance); - } - - font->set_height(p_height); - font->set_ascent(p_ascent); - - return font; -} - #define MAKE_FALLBACKS(m_name) \ m_name->add_fallback(FontArabic); \ m_name->add_fallback(FontHebrew); \ m_name->add_fallback(FontThai); \ + m_name->add_fallback(FontHindi); \ m_name->add_fallback(FontJapanese); \ m_name->add_fallback(FontFallback); @@ -204,6 +178,12 @@ void editor_register_fonts(Ref<Theme> p_theme) { FontThai->set_font_ptr(_font_NotoSansThaiUI_Regular, _font_NotoSansThaiUI_Regular_size); FontThai->set_force_autohinter(true); //just looks better..i think? + Ref<DynamicFontData> FontHindi; + FontHindi.instance(); + FontHindi->set_hinting(font_hinting); + FontHindi->set_font_ptr(_font_NotoSansDevanagariUI_Regular, _font_NotoSansDevanagariUI_Regular_size); + FontHindi->set_force_autohinter(true); //just looks better..i think? + /* Hack */ Ref<DynamicFontData> dfmono; diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index 5a0a49d577..3ee8d9c6c5 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -30,17 +30,17 @@ #include "editor_help.h" +#include "core/os/keyboard.h" #include "doc_data_compressed.gen.h" #include "editor/plugins/script_editor_plugin.h" #include "editor_node.h" #include "editor_settings.h" -#include "os/keyboard.h" -#define CONTRIBUTE_URL "http://docs.godotengine.org/en/latest/community/contributing/updating_the_class_reference.html" +#define CONTRIBUTE_URL "https://docs.godotengine.org/en/latest/community/contributing/updating_the_class_reference.html" #define CONTRIBUTE2_URL "https://github.com/godotengine/godot-docs" #define REQUEST_URL "https://github.com/godotengine/godot-docs/issues/new" -void EditorHelpSearch::popup() { +void EditorHelpSearch::popup_dialog() { popup_centered(Size2(700, 600) * EDSCALE); if (search_box->get_text() != "") { @@ -50,15 +50,16 @@ void EditorHelpSearch::popup() { search_box->grab_focus(); } -void EditorHelpSearch::popup(const String &p_term) { +void EditorHelpSearch::popup_dialog(const String &p_term) { popup_centered(Size2(700, 600) * EDSCALE); if (p_term != "") { search_box->set_text(p_term); search_box->select_all(); _update_search(); - } else + } else { search_box->clear(); + } search_box->grab_focus(); } @@ -88,10 +89,8 @@ void EditorHelpSearch::IncrementalSearch::phase1(Map<String, DocData::ClassDoc>: TreeItem *item = search_options->create_item(root); item->set_metadata(0, "class_name:" + E->key()); item->set_text(0, E->key() + " (Class)"); - if (search->has_icon(E->key(), "EditorIcons")) - item->set_icon(0, search->get_icon(E->key(), "EditorIcons")); - else - item->set_icon(0, def_icon); + Ref<Texture> icon = EditorNode::get_singleton()->get_class_icon(E->key(), "Node"); + item->set_icon(0, icon); } } @@ -99,11 +98,7 @@ void EditorHelpSearch::IncrementalSearch::phase2(Map<String, DocData::ClassDoc>: DocData::ClassDoc &c = E->get(); - Ref<Texture> cicon; - if (search->has_icon(E->key(), "EditorIcons")) - cicon = search->get_icon(E->key(), "EditorIcons"); - else - cicon = def_icon; + Ref<Texture> cicon = EditorNode::get_singleton()->get_class_icon(E->key(), "Node"); for (int i = 0; i < c.methods.size(); i++) { if ((term.begins_with(".") && c.methods[i].name.begins_with(term.right(1))) || (term.ends_with("(") && c.methods[i].name.ends_with(term.left(term.length() - 1).strip_edges())) || (term.begins_with(".") && term.ends_with("(") && c.methods[i].name == term.substr(1, term.length() - 2).strip_edges()) || c.methods[i].name.findn(term) != -1) { @@ -225,7 +220,6 @@ bool EditorHelpSearch::IncrementalSearch::work(uint64_t slot) { void EditorHelpSearch::_update_search() { search_options->clear(); - search_options->set_hide_root(true); String term = search_box->get_text(); if (term.length() < 2) @@ -258,6 +252,8 @@ void EditorHelpSearch::_notification(int p_what) { connect("confirmed", this, "_confirmed"); _update_search(); + } else if (p_what == NOTIFICATION_EXIT_TREE) { + disconnect("confirmed", this, "_confirmed"); } else if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { if (is_visible_in_tree()) { @@ -307,6 +303,7 @@ EditorHelpSearch::EditorHelpSearch() { search_box->connect("text_changed", this, "_text_changed"); search_box->connect("gui_input", this, "_sbox_input"); search_options = memnew(Tree); + search_options->set_hide_root(true); vbc->add_margin_child(TTR("Matches:"), search_options, true); get_ok()->set_text(TTR("Open")); get_ok()->set_disabled(true); @@ -343,10 +340,8 @@ void EditorHelpIndex::add_type(const String &p_type, HashMap<String, TreeItem *> item->set_tooltip(0, EditorHelp::get_doc_data()->class_list[p_type].brief_description); item->set_text(0, p_type); - if (has_icon(p_type, "EditorIcons")) { - - item->set_icon(0, get_icon(p_type, "EditorIcons")); - } + Ref<Texture> icon = EditorNode::get_singleton()->get_class_icon(p_type); + item->set_icon(0, icon); p_types[p_type] = item; } @@ -370,7 +365,7 @@ void EditorHelpIndex::select_class(const String &p_class) { class_list->ensure_cursor_is_visible(); } -void EditorHelpIndex::popup() { +void EditorHelpIndex::popup_dialog() { popup_centered(Size2(500, 600) * EDSCALE); @@ -397,6 +392,16 @@ void EditorHelpIndex::_notification(int p_what) { //_update_icons search_box->set_right_icon(get_icon("Search", "EditorIcons")); search_box->set_clear_button_enabled(true); + + bool enable_rl = EditorSettings::get_singleton()->get("docks/scene_tree/draw_relationship_lines"); + Color rl_color = EditorSettings::get_singleton()->get("docks/scene_tree/relationship_line_color"); + + if (enable_rl) { + class_list->add_constant_override("draw_relationship_lines", 1); + class_list->add_color_override("relationship_line_color", rl_color); + } else { + class_list->add_constant_override("draw_relationship_lines", 0); + } } } @@ -410,7 +415,6 @@ void EditorHelpIndex::_update_class_list() { class_list->clear(); tree_item_map.clear(); TreeItem *root = class_list->create_item(); - class_list->set_hide_root(true); String filter = search_box->get_text().strip_edges(); String to_select = ""; @@ -489,10 +493,21 @@ EditorHelpIndex::EditorHelpIndex() { class_list = memnew(Tree); vbc->add_margin_child(TTR("Class List:") + " ", class_list, true); + class_list->set_hide_root(true); class_list->set_v_size_flags(SIZE_EXPAND_FILL); class_list->connect("item_activated", this, "_tree_item_selected"); + bool enable_rl = EditorSettings::get_singleton()->get("docks/scene_tree/draw_relationship_lines"); + Color rl_color = EditorSettings::get_singleton()->get("docks/scene_tree/relationship_line_color"); + + if (enable_rl) { + class_list->add_constant_override("draw_relationship_lines", 1); + class_list->add_color_override("relationship_line_color", rl_color); + } else { + class_list->add_constant_override("draw_relationship_lines", 0); + } + get_ok()->set_text(TTR("Open")); set_title(TTR("Search Classes")); } @@ -740,6 +755,8 @@ Error EditorHelp::_goto_desc(const String &p_class, int p_vscr) { } void EditorHelp::_update_doc() { + if (!doc->class_list.has(edited_class)) + return; scroll_locked = true; @@ -756,6 +773,7 @@ void EditorHelp::_update_doc() { Ref<Font> doc_code_font = get_font("doc_source", "EditorFonts"); String link_color_text = title_color.to_html(false); + // Class name section_line.push_back(Pair<String, int>(TTR("Top"), 0)); class_desc->push_font(doc_title_font); class_desc->push_color(title_color); @@ -767,18 +785,18 @@ void EditorHelp::_update_doc() { class_desc->pop(); class_desc->add_newline(); + // Inheritance tree + + // Ascendents if (cd.inherits != "") { class_desc->push_color(title_color); - class_desc->push_font(doc_title_font); + class_desc->push_font(doc_font); class_desc->add_text(TTR("Inherits:") + " "); class_desc->pop(); - class_desc->pop(); String inherits = cd.inherits; - class_desc->push_font(doc_font); - while (inherits != "") { _add_type(inherits); @@ -793,6 +811,7 @@ void EditorHelp::_update_doc() { class_desc->add_newline(); } + // Descendents if (ClassDB::class_exists(cd.name)) { bool found = false; @@ -804,13 +823,10 @@ void EditorHelp::_update_doc() { if (!found) { class_desc->push_color(title_color); - class_desc->push_font(doc_title_font); + class_desc->push_font(doc_font); class_desc->add_text(TTR("Inherited by:") + " "); class_desc->pop(); - class_desc->pop(); - found = true; - class_desc->push_font(doc_font); } if (prev) { @@ -833,6 +849,7 @@ void EditorHelp::_update_doc() { class_desc->add_newline(); class_desc->add_newline(); + // Brief description if (cd.brief_description != "") { class_desc->push_color(title_color); @@ -854,15 +871,16 @@ void EditorHelp::_update_doc() { class_desc->add_newline(); } + // Properties overview Set<String> skip_methods; bool property_descr = false; if (cd.properties.size()) { - section_line.push_back(Pair<String, int>(TTR("Members"), class_desc->get_line_count() - 2)); + section_line.push_back(Pair<String, int>(TTR("Properties"), class_desc->get_line_count() - 2)); class_desc->push_color(title_color); class_desc->push_font(doc_title_font); - class_desc->add_text(TTR("Members:")); + class_desc->add_text(TTR("Properties:")); class_desc->pop(); class_desc->pop(); @@ -920,6 +938,7 @@ void EditorHelp::_update_doc() { class_desc->add_newline(); } + // Methods overview bool method_descr = false; bool sort_methods = EditorSettings::get_singleton()->get("text_editor/help/sort_functions_alphabetically"); @@ -936,10 +955,10 @@ void EditorHelp::_update_doc() { if (sort_methods) methods.sort(); - section_line.push_back(Pair<String, int>(TTR("Public Methods"), class_desc->get_line_count() - 2)); + section_line.push_back(Pair<String, int>(TTR("Methods"), class_desc->get_line_count() - 2)); class_desc->push_color(title_color); class_desc->push_font(doc_title_font); - class_desc->add_text(TTR("Public Methods:")); + class_desc->add_text(TTR("Methods:")); class_desc->pop(); class_desc->pop(); @@ -1004,22 +1023,20 @@ void EditorHelp::_update_doc() { class_desc->add_newline(); } + // Theme properties if (cd.theme_properties.size()) { - section_line.push_back(Pair<String, int>(TTR("GUI Theme Items"), class_desc->get_line_count() - 2)); + section_line.push_back(Pair<String, int>(TTR("Theme Properties"), class_desc->get_line_count() - 2)); class_desc->push_color(title_color); class_desc->push_font(doc_title_font); - class_desc->add_text(TTR("GUI Theme Items:")); + class_desc->add_text(TTR("Theme Properties:")); class_desc->pop(); class_desc->pop(); - // class_desc->add_newline(); class_desc->push_indent(1); class_desc->push_table(2); class_desc->set_table_column_expand(1, 1); - //class_desc->add_newline(); - for (int i = 0; i < cd.theme_properties.size(); i++) { theme_property_line[cd.theme_properties[i].name] = class_desc->get_line_count() - 2; //gets overridden if description @@ -1056,6 +1073,7 @@ void EditorHelp::_update_doc() { class_desc->add_newline(); } + // Signals if (cd.signals.size()) { if (sort_methods) { @@ -1124,6 +1142,7 @@ void EditorHelp::_update_doc() { class_desc->add_newline(); } + // Constants and enums if (cd.constants.size()) { Map<String, Vector<DocData::ConstantDoc> > enums; @@ -1143,6 +1162,7 @@ void EditorHelp::_update_doc() { } } + // Enums if (enums.size()) { section_line.push_back(Pair<String, int>(TTR("Enumerations"), class_desc->get_line_count() - 2)); @@ -1225,6 +1245,7 @@ void EditorHelp::_update_doc() { class_desc->add_newline(); } + // Constants if (constants.size()) { section_line.push_back(Pair<String, int>(TTR("Constants"), class_desc->get_line_count() - 2)); @@ -1283,13 +1304,14 @@ void EditorHelp::_update_doc() { } } + // Class description if (cd.description != "") { - section_line.push_back(Pair<String, int>(TTR("Description"), class_desc->get_line_count() - 2)); + section_line.push_back(Pair<String, int>(TTR("Class Description"), class_desc->get_line_count() - 2)); description_line = class_desc->get_line_count() - 2; class_desc->push_color(title_color); class_desc->push_font(doc_title_font); - class_desc->add_text(TTR("Description:")); + class_desc->add_text(TTR("Class Description:")); class_desc->pop(); class_desc->pop(); @@ -1306,8 +1328,8 @@ void EditorHelp::_update_doc() { class_desc->add_newline(); } + // Online tutorials { - class_desc->push_color(title_color); class_desc->push_font(doc_title_font); class_desc->add_text(TTR("Online Tutorials:")); @@ -1345,12 +1367,14 @@ void EditorHelp::_update_doc() { class_desc->add_newline(); class_desc->add_newline(); } + + // Property descriptions if (property_descr) { - section_line.push_back(Pair<String, int>(TTR("Properties"), class_desc->get_line_count() - 2)); + section_line.push_back(Pair<String, int>(TTR("Property Descriptions"), class_desc->get_line_count() - 2)); class_desc->push_color(title_color); class_desc->push_font(doc_title_font); - class_desc->add_text(TTR("Property Description:")); + class_desc->add_text(TTR("Property Descriptions:")); class_desc->pop(); class_desc->pop(); @@ -1438,12 +1462,13 @@ void EditorHelp::_update_doc() { } } + // Method descriptions if (method_descr) { - section_line.push_back(Pair<String, int>(TTR("Methods"), class_desc->get_line_count() - 2)); + section_line.push_back(Pair<String, int>(TTR("Method Descriptions"), class_desc->get_line_count() - 2)); class_desc->push_color(title_color); class_desc->push_font(doc_title_font); - class_desc->add_text(TTR("Method Description:")); + class_desc->add_text(TTR("Method Descriptions:")); class_desc->pop(); class_desc->pop(); diff --git a/editor/editor_help.h b/editor/editor_help.h index ad81a39945..25db68b42d 100644 --- a/editor/editor_help.h +++ b/editor/editor_help.h @@ -92,8 +92,8 @@ protected: static void _bind_methods(); public: - void popup(); - void popup(const String &p_term); + void popup_dialog(); + void popup_dialog(const String &p_term); EditorHelpSearch(); }; @@ -120,7 +120,7 @@ protected: public: void select_class(const String &p_class); - void popup(); + void popup_dialog(); EditorHelpIndex(); }; diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 852e1930d2..75b364089d 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -36,9 +36,49 @@ #include "multi_node_edit.h" #include "scene/resources/packed_scene.h" -// TODO: -// arrays and dictionary -// replace property editor in sectionedpropertyeditor +EditorDefaultClassValueCache *EditorDefaultClassValueCache::singleton = NULL; + +EditorDefaultClassValueCache *EditorDefaultClassValueCache::get_singleton() { + return singleton; +} + +Variant EditorDefaultClassValueCache::get_default_value(const StringName &p_class, const StringName &p_property) { + + if (!default_values.has(p_class)) { + + default_values[p_class] = Map<StringName, Variant>(); + + if (ClassDB::can_instance(p_class)) { + + Object *c = ClassDB::instance(p_class); + List<PropertyInfo> plist; + c->get_property_list(&plist); + for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) { + if (E->get().usage & PROPERTY_USAGE_EDITOR) { + + Variant v = c->get(E->get().name); + default_values[p_class][E->get().name] = v; + } + } + memdelete(c); + } + } + + if (!default_values.has(p_class)) { + return Variant(); + } + + if (!default_values[p_class].has(p_property)) { + return Variant(); + } + + return default_values[p_class][p_property]; +} + +EditorDefaultClassValueCache::EditorDefaultClassValueCache() { + ERR_FAIL_COND(singleton != NULL); + singleton = this; +} Size2 EditorProperty::get_minimum_size() const { @@ -268,11 +308,6 @@ void EditorProperty::_notification(int p_what) { } else { keying_rect = Rect2(); } - - //int vs = get_constant("vseparation", "Tree"); - Color guide_color = get_color("guide_color", "Tree"); - int vs_height = get_size().height; // vs / 2; - // draw_line(Point2(0, vs_height), Point2(get_size().width, vs_height), guide_color); } } @@ -467,6 +502,12 @@ void EditorProperty::update_reload_status() { bool has_reload = false; + if (EditorDefaultClassValueCache::get_singleton()) { + Variant default_value = EditorDefaultClassValueCache::get_singleton()->get_default_value(object->get_class_name(), property); + if (default_value != Variant() && default_value != object->get(property)) { + has_reload = true; + } + } if (_is_instanced_node_with_original_property_different()) { has_reload = true; } @@ -493,6 +534,17 @@ void EditorProperty::update_reload_status() { } bool EditorProperty::use_keying_next() const { + List<PropertyInfo> plist; + object->get_property_list(&plist, true); + + for (List<PropertyInfo>::Element *I = plist.front(); I; I = I->next()) { + PropertyInfo &p = I->get(); + + if (p.name == property) { + return p.hint == PROPERTY_HINT_SPRITE_FRAME; + } + } + return false; } void EditorProperty::set_checkable(bool p_checkable) { @@ -627,6 +679,11 @@ void EditorProperty::_gui_input(const Ref<InputEvent> &p_event) { if (keying_rect.has_point(mb->get_position())) { emit_signal("property_keyed", property); + + if (use_keying_next()) { + call_deferred("emit_signal", "property_changed", property, object->get(property).operator int64_t() + 1); + call_deferred("update_property"); + } } if (revert_rect.has_point(mb->get_position())) { @@ -644,6 +701,7 @@ void EditorProperty::_gui_input(const Ref<InputEvent> &p_event) { Variant rev = object->call("property_get_revert", property); emit_signal("property_changed", property, rev); update_property(); + return; } if (!object->get_script().is_null()) { @@ -652,6 +710,16 @@ void EditorProperty::_gui_input(const Ref<InputEvent> &p_event) { if (scr->get_property_default_value(property, orig_value)) { emit_signal("property_changed", property, orig_value); update_property(); + return; + } + } + + if (EditorDefaultClassValueCache::get_singleton()) { + Variant default_value = EditorDefaultClassValueCache::get_singleton()->get_default_value(object->get_class_name(), property); + if (default_value != Variant()) { + emit_signal("property_changed", property, default_value); + update_property(); + return; } } } @@ -731,9 +799,9 @@ Control *EditorProperty::make_custom_tooltip(const String &p_text) const { tooltip_text = p_text; EditorHelpBit *help_bit = memnew(EditorHelpBit); help_bit->add_style_override("panel", get_stylebox("panel", "TooltipPanel")); - help_bit->get_rich_text()->set_fixed_size_to_width(300); + help_bit->get_rich_text()->set_fixed_size_to_width(360 * EDSCALE); - String text = TTR("Property: ") + "[u][b]" + p_text.get_slice("::", 0) + "[/b][/u]\n"; + String text = TTR("Property:") + " [u][b]" + p_text.get_slice("::", 0) + "[/b][/u]\n"; text += p_text.get_slice("::", 1).strip_edges(); help_bit->set_text(text); help_bit->call_deferred("set_text", text); //hack so it uses proper theme once inside scene @@ -953,7 +1021,7 @@ Control *EditorInspectorCategory::make_custom_tooltip(const String &p_text) cons tooltip_text = p_text; EditorHelpBit *help_bit = memnew(EditorHelpBit); help_bit->add_style_override("panel", get_stylebox("panel", "TooltipPanel")); - help_bit->get_rich_text()->set_fixed_size_to_width(300); + help_bit->get_rich_text()->set_fixed_size_to_width(360 * EDSCALE); String text = "[u][b]" + p_text.get_slice("::", 0) + "[/b][/u]\n"; text += p_text.get_slice("::", 1).strip_edges(); @@ -1075,10 +1143,8 @@ void EditorInspectorSection::_notification(int p_what) { Color color = get_color("font_color", "Tree"); draw_string(font, Point2(hs, font->get_ascent() + (h - font->get_height()) / 2).floor(), label, color, get_size().width); - int ofs = 0; if (arrow.is_valid()) { draw_texture(arrow, Point2(get_size().width - arrow->get_width(), (h - arrow->get_height()) / 2).floor()); - ofs += hs + arrow->get_width(); } } } @@ -1138,7 +1204,6 @@ void EditorInspectorSection::_gui_input(const Ref<InputEvent> &p_event) { return; #ifdef TOOLS_ENABLED - Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { @@ -1167,7 +1232,6 @@ void EditorInspectorSection::unfold() { _test_unfold(); #ifdef TOOLS_ENABLED - object->editor_set_section_unfold(section, true); vbox->show(); update(); @@ -1180,8 +1244,8 @@ void EditorInspectorSection::fold() { if (!vbox_added) return; //kinda pointless -#ifdef TOOLS_ENABLED +#ifdef TOOLS_ENABLED object->editor_set_section_unfold(section, false); vbox->hide(); update(); @@ -1202,7 +1266,6 @@ EditorInspectorSection::EditorInspectorSection() { foldable = false; vbox = memnew(VBoxContainer); vbox_added = false; - //add_child(vbox); } EditorInspectorSection::~EditorInspectorSection() { @@ -1366,6 +1429,28 @@ void EditorInspector::update_tree() { // TreeItem *current_category = NULL; + bool unfold_if_edited = false; + + if (use_folding && auto_unfold_edited && get_tree()->get_edited_scene_root()) { + String path; + Node *node = Object::cast_to<Node>(object); + if (node) { + path = get_tree()->get_edited_scene_root()->get_filename(); + } + Resource *res = Object::cast_to<Resource>(object); + if (res) { + if (res->get_path().is_resource_file()) { + path = res->get_path(); + } else if (res->get_path().begins_with("res://")) { //internal resource + path = get_tree()->get_edited_scene_root()->get_filename(); + } + } + + if (!EditorNode::get_singleton()->get_editor_folding().has_folding_data(path)) { + unfold_if_edited = true; + } + } + String filter = search_box ? search_box->get_text() : ""; String group; String group_base; @@ -1376,6 +1461,8 @@ void EditorInspector::update_tree() { object->get_property_list(&plist, true); HashMap<String, VBoxContainer *> item_path; + Map<VBoxContainer *, EditorInspectorSection *> section_map; + item_path[""] = main_vbox; Color sscolor = get_color("prop_subsection", "Editor"); @@ -1427,10 +1514,7 @@ void EditorInspector::update_tree() { category_vbox = NULL; //reset String type = p.name; - if (has_icon(type, "EditorIcons")) - category->icon = get_icon(type, "EditorIcons"); - else - category->icon = get_icon("Object", "EditorIcons"); + category->icon = EditorNode::get_singleton()->get_class_icon(type, "Object"); category->label = type; category->bg_color = get_color("prop_category", "Editor"); @@ -1461,6 +1545,9 @@ void EditorInspector::update_tree() { } else if (!(p.usage & PROPERTY_USAGE_EDITOR)) continue; + if (p.usage & PROPERTY_USAGE_HIGH_END_GFX && VS::get_singleton()->is_low_end()) + continue; //do not show this property in low end gfx + if (p.name == "script" && (hide_script || bool(object->call("_hide_script_from_inspector")))) { continue; } @@ -1538,7 +1625,9 @@ void EditorInspector::update_tree() { c.a /= level; section->setup(acc_path, path_name, object, c, use_folding); - item_path[acc_path] = section->get_vbox(); + VBoxContainer *vb = section->get_vbox(); + item_path[acc_path] = vb; + section_map[vb] = section; } current_vbox = item_path[acc_path]; level = (MIN(level + 1, 4)); @@ -1607,12 +1696,6 @@ void EditorInspector::update_tree() { doc_hint = descr; } -#if 0 - if (p.name == selected_property) { - - item->select(1); - } -#endif for (List<Ref<EditorInspectorPlugin> >::Element *E = valid_plugins.front(); E; E = E->next()) { Ref<EditorInspectorPlugin> ped = E->get(); bool exclusive = ped->parse_property(object, p.type, p.name, p.hint, p.hint_string, p.usage); @@ -1687,6 +1770,13 @@ void EditorInspector::update_tree() { if (current_selected && ep->property == current_selected) { ep->select(current_focusable); } + + if (unfold_if_edited && ep->can_revert_to_default()) { + //if edited and there is a parent section, unfold it. + if (current_vbox && section_map.has(current_vbox)) { + section_map[current_vbox]->unfold(); + } + } } } @@ -1752,7 +1842,7 @@ void EditorInspector::edit(Object *p_object) { if (object) { update_scroll_request = 0; //reset if (scroll_cache.has(object->get_instance_id())) { //if exists, set something else - update_scroll_request = scroll_cache[object->get_instance_id()]; //done this way because wait until full size is accomodated + update_scroll_request = scroll_cache[object->get_instance_id()]; //done this way because wait until full size is accommodated } object->add_change_receptor(this); update_tree(); @@ -1812,12 +1902,6 @@ void EditorInspector::_filter_changed(const String &p_text) { update_tree(); } -void EditorInspector::set_subsection_selectable(bool p_selectable) { -} - -void EditorInspector::set_property_selectable(bool p_selectable) { -} - void EditorInspector::set_use_folding(bool p_enable) { use_folding = p_enable; update_tree(); @@ -2005,7 +2089,7 @@ void EditorInspector::_property_keyed(const String &p_path) { if (!object) return; - emit_signal("property_keyed", p_path, object->get(p_path), false); //second param is deprecated + emit_signal("property_keyed", p_path, object->get(p_path), true); //second param is deprecated } void EditorInspector::_property_keyed_with_value(const String &p_path, const Variant &p_value) { @@ -2147,6 +2231,13 @@ void EditorInspector::_notification(int p_what) { } if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) { + + if (use_sub_inspector_bg) { + add_style_override("bg", get_stylebox("sub_inspector_bg", "Editor")); + } else if (is_inside_tree()) { + add_style_override("bg", get_stylebox("bg", "Tree")); + } + update_tree(); } } @@ -2182,6 +2273,10 @@ String EditorInspector::get_object_class() const { return object_class; } +void EditorInspector::set_auto_unfold_edited(bool p_enable) { + auto_unfold_edited = p_enable; +} + void EditorInspector::_bind_methods() { ClassDB::bind_method("_property_changed", &EditorInspector::_property_changed, DEFVAL(false)); @@ -2206,6 +2301,7 @@ void EditorInspector::_bind_methods() { ADD_SIGNAL(MethodInfo("resource_selected", PropertyInfo(Variant::OBJECT, "res"), PropertyInfo(Variant::STRING, "prop"))); ADD_SIGNAL(MethodInfo("object_id_selected", PropertyInfo(Variant::INT, "id"))); ADD_SIGNAL(MethodInfo("property_edited", PropertyInfo(Variant::STRING, "property"))); + ADD_SIGNAL(MethodInfo("property_toggled", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::BOOL, "checked"))); ADD_SIGNAL(MethodInfo("restart_requested")); } @@ -2237,6 +2333,7 @@ EditorInspector::EditorInspector() { set_process(true); property_focusable = -1; use_sub_inspector_bg = false; + auto_unfold_edited = false; get_v_scrollbar()->connect("value_changed", this, "_vscroll_changed"); update_scroll_request = -1; diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index ebe2124a40..350debcb7b 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -37,6 +37,20 @@ class UndoRedo; +class EditorDefaultClassValueCache : public Object { + GDCLASS(EditorDefaultClassValueCache, Object) + + Map<StringName, Map<StringName, Variant> > default_values; + + static EditorDefaultClassValueCache *singleton; + +public: + static EditorDefaultClassValueCache *get_singleton(); + + Variant get_default_value(const StringName &p_class, const StringName &p_property); + EditorDefaultClassValueCache(); +}; + class EditorProperty : public Container { GDCLASS(EditorProperty, Container) @@ -152,6 +166,8 @@ public: void set_draw_top_bg(bool p_draw) { draw_top_bg = p_draw; } + bool can_revert_to_default() const { return can_revert; } + EditorProperty(); }; @@ -271,6 +287,7 @@ class EditorInspector : public ScrollContainer { bool read_only; bool keying; bool use_sub_inspector_bg; + bool auto_unfold_edited; float refresh_countdown; bool update_tree_pending; @@ -349,9 +366,6 @@ public: void set_use_filter(bool p_use); void register_text_enter(Node *p_line_edit); - void set_subsection_selectable(bool p_selectable); - void set_property_selectable(bool p_selectable); - void set_use_folding(bool p_enable); bool is_using_folding(); @@ -368,6 +382,7 @@ public: String get_object_class() const; void set_use_sub_inspector_bg(bool p_enable); + void set_auto_unfold_edited(bool p_enable); EditorInspector(); }; diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp index b3ec717d85..5f5c46f4a7 100644 --- a/editor/editor_log.cpp +++ b/editor/editor_log.cpp @@ -31,10 +31,10 @@ #include "editor_log.h" #include "core/os/keyboard.h" +#include "core/version.h" #include "editor_node.h" #include "scene/gui/center_container.h" #include "scene/resources/dynamic_font.h" -#include "version.h" void EditorLog::_error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, ErrorHandlerType p_type) { @@ -49,11 +49,6 @@ void EditorLog::_error_handler(void *p_self, const char *p_func, const char *p_f err_str = String(p_file) + ":" + itos(p_line) + " - " + String(p_error); } - /* - if (!self->is_visible_in_tree()) - self->emit_signal("show_request"); - */ - if (p_type == ERR_HANDLER_WARNING) { self->add_message(err_str, MSG_TYPE_WARNING); } else { @@ -76,17 +71,6 @@ void EditorLog::_notification(int p_what) { } } } - - /*if (p_what==NOTIFICATION_DRAW) { - - RID ci = get_canvas_item(); - get_stylebox("panel","PopupMenu")->draw(ci,Rect2(Point2(),get_size())); - int top_ofs = 20; - int border_ofs=4; - Ref<StyleBox> style = get_stylebox("normal","TextEdit"); - - style->draw(ci,Rect2( Point2(border_ofs,top_ofs),get_size()-Size2(border_ofs*2,top_ofs+border_ofs))); - }*/ } void EditorLog::_clear_request() { @@ -105,6 +89,8 @@ void EditorLog::add_message(const String &p_msg, MessageType p_type) { bool restore = p_type != MSG_TYPE_STD; switch (p_type) { + case MSG_TYPE_STD: { + } break; case MSG_TYPE_ERROR: { log->push_color(get_color("error_color", "Editor")); Ref<Texture> icon = get_icon("Error", "EditorIcons"); @@ -122,7 +108,6 @@ void EditorLog::add_message(const String &p_msg, MessageType p_type) { } log->add_text(p_msg); - //button->set_text(p_msg); if (restore) log->pop(); @@ -132,21 +117,6 @@ void EditorLog::set_tool_button(ToolButton *p_tool_button) { tool_button = p_tool_button; } -/* -void EditorLog::_dragged(const Point2& p_ofs) { - - int ofs = ec->get_minsize().height; - ofs = ofs-p_ofs.y; - if (ofs<50) - ofs=50; - if (ofs>300) - ofs=300; - ec->set_minsize(Size2(ec->get_minsize().width,ofs)); - minimum_size_changed(); - -} -*/ - void EditorLog::_undo_redo_cbk(void *p_self, const String &p_name) { EditorLog *self = (EditorLog *)p_self; @@ -156,7 +126,6 @@ void EditorLog::_undo_redo_cbk(void *p_self, const String &p_name) { void EditorLog::_bind_methods() { ClassDB::bind_method(D_METHOD("_clear_request"), &EditorLog::_clear_request); - //ClassDB::bind_method(D_METHOD("_dragged"),&EditorLog::_dragged ); ADD_SIGNAL(MethodInfo("clear_request")); } @@ -187,7 +156,6 @@ EditorLog::EditorLog() { log->set_h_size_flags(SIZE_EXPAND_FILL); vb->add_child(log); add_message(VERSION_FULL_NAME " (c) 2007-2018 Juan Linietsky, Ariel Manzur & Godot Contributors."); - //log->add_text("Initialization Complete.\n"); //because it looks cool. eh.errfunc = _error_handler; eh.userdata = this; diff --git a/editor/editor_log.h b/editor/editor_log.h index 8d0310d914..78a5671d29 100644 --- a/editor/editor_log.h +++ b/editor/editor_log.h @@ -36,7 +36,7 @@ #include "scene/gui/rich_text_label.h" #include "scene/gui/texture_button.h" //#include "scene/gui/empty_control.h" -#include "os/thread.h" +#include "core/os/thread.h" #include "pane_drag.h" #include "scene/gui/box_container.h" #include "scene/gui/panel_container.h" diff --git a/editor/editor_name_dialog.cpp b/editor/editor_name_dialog.cpp index bacb288273..1ef61802c4 100644 --- a/editor/editor_name_dialog.cpp +++ b/editor/editor_name_dialog.cpp @@ -30,8 +30,8 @@ #include "editor_name_dialog.h" -#include "class_db.h" -#include "os/keyboard.h" +#include "core/class_db.h" +#include "core/os/keyboard.h" void EditorNameDialog::_line_gui_input(const Ref<InputEvent> &p_event) { diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 353dce5b20..81bcbd63a1 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -36,7 +36,6 @@ #include "core/io/resource_loader.h" #include "core/io/resource_saver.h" #include "core/io/stream_peer_ssl.h" -#include "core/io/zip_io.h" #include "core/message_queue.h" #include "core/os/file_access.h" #include "core/os/input.h" @@ -54,7 +53,6 @@ #include "editor/editor_audio_buses.h" #include "editor/editor_file_system.h" #include "editor/editor_help.h" -#include "editor/editor_initialize_ssl.h" #include "editor/editor_properties.h" #include "editor/editor_settings.h" #include "editor/editor_themes.h" @@ -141,12 +139,7 @@ void EditorNode::_update_scene_tabs() { String type = editor_data.get_scene_type(i); Ref<Texture> icon; if (type != String()) { - - if (!gui_base->has_icon(type, "EditorIcons")) { - type = "Node"; - } - - icon = gui_base->get_icon(type, "EditorIcons"); + icon = get_class_icon(type, "Node"); } int current = editor_data.get_edited_scene(); @@ -234,12 +227,6 @@ void EditorNode::_unhandled_input(const Ref<InputEvent> &p_event) { _editor_select_prev(); } - if (k->get_scancode() == KEY_ESCAPE) { - for (int i = 0; i < bottom_panel_items.size(); i++) { - _bottom_panel_switch(false, i); - } - } - if (old_editor != editor_plugin_screen) { get_tree()->set_input_as_handled(); } @@ -587,13 +574,29 @@ void EditorNode::_editor_select_prev() { _editor_select(editor); } -Error EditorNode::load_resource(const String &p_scene) { +Error EditorNode::load_resource(const String &p_resource, bool p_ignore_broken_deps) { + + dependency_errors.clear(); - RES res = ResourceLoader::load(p_scene); + Error err; + RES res = ResourceLoader::load(p_resource, "", false, &err); ERR_FAIL_COND_V(!res.is_valid(), ERR_CANT_OPEN); - inspector_dock->edit_resource(res); + if (!p_ignore_broken_deps && dependency_errors.has(p_resource)) { + + //current_option = -1; + Vector<String> errors; + for (Set<String>::Element *E = dependency_errors[p_resource].front(); E; E = E->next()) { + + errors.push_back(E->get()); + } + dependency_error->show(DependencyErrorDialog::MODE_RESOURCE, p_resource, errors); + dependency_errors.erase(p_resource); + + return ERR_FILE_MISSING_DEPENDENCIES; + } + inspector_dock->edit_resource(res); return OK; } @@ -1074,6 +1077,9 @@ void EditorNode::_save_scene(String p_file, int idx) { set_current_version(editor_data.get_undo_redo().get_version()); else editor_data.set_edited_scene_version(0, idx); + + editor_folding.save_scene_folding(scene, p_file); + _update_title(); _update_scene_tabs(); } else { @@ -1174,6 +1180,16 @@ void EditorNode::_dialog_action(String p_file) { int scene_idx = (current_option == FILE_SAVE_SCENE || current_option == FILE_SAVE_AS_SCENE) ? -1 : tab_closing; if (file->get_mode() == EditorFileDialog::MODE_SAVE_FILE) { + bool same_open_scene = false; + for (int i = 0; i < editor_data.get_edited_scene_count(); i++) { + if (editor_data.get_scene_path(i) == p_file && i != scene_idx) + same_open_scene = true; + } + + if (same_open_scene) { + show_warning(TTR("Can't overwrite scene that is still open!")); + return; + } _save_default_environment(); _save_scene_with_preview(p_file, scene_idx); @@ -1735,13 +1751,13 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { } break; case FILE_QUICK_OPEN_SCENE: { - quick_open->popup("PackedScene", true); + quick_open->popup_dialog("PackedScene", true); quick_open->set_title(TTR("Quick Open Scene...")); } break; case FILE_QUICK_OPEN_SCRIPT: { - quick_open->popup("Script", true); + quick_open->popup_dialog("Script", true); quick_open->set_title(TTR("Quick Open Script...")); } break; @@ -1999,7 +2015,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { case RUN_PLAY_CUSTOM_SCENE: { if (run_custom_filename.empty() || editor_run.get_status() == EditorRun::STATUS_STOP) { _menu_option_confirm(RUN_STOP, true); - quick_run->popup("PackedScene", true); + quick_run->popup_dialog("PackedScene", true); quick_run->set_title(TTR("Quick Run Scene...")); play_custom_scene_button->set_pressed(false); } else { @@ -2035,6 +2051,14 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { emit_signal("stop_pressed"); } break; + + case FILE_SHOW_IN_FILESYSTEM: { + String path = editor_data.get_scene_path(editor_data.get_edited_scene()); + if (path != String()) { + filesystem_dock->navigate_to_path(path); + } + } break; + case RUN_PLAY_SCENE: { _save_default_environment(); @@ -2068,7 +2092,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { } break; case RUN_PROJECT_DATA_FOLDER: { - OS::get_singleton()->shell_open(OS::get_singleton()->get_user_data_dir()); + OS::get_singleton()->shell_open(String("file://") + OS::get_singleton()->get_user_data_dir()); } break; case FILE_QUIT: case RUN_PROJECT_MANAGER: { @@ -2204,11 +2228,11 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { } break; case SETTINGS_EDITOR_DATA_FOLDER: { - OS::get_singleton()->shell_open(EditorSettings::get_singleton()->get_data_dir()); + OS::get_singleton()->shell_open(String("file://") + EditorSettings::get_singleton()->get_data_dir()); } break; case SETTINGS_EDITOR_CONFIG_FOLDER: { - OS::get_singleton()->shell_open(EditorSettings::get_singleton()->get_settings_dir()); + OS::get_singleton()->shell_open(String("file://") + EditorSettings::get_singleton()->get_settings_dir()); } break; case SETTINGS_MANAGE_EXPORT_TEMPLATES: { @@ -2246,7 +2270,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { emit_signal("request_help_search", ""); } break; case HELP_DOCS: { - OS::get_singleton()->shell_open("http://docs.godotengine.org/"); + OS::get_singleton()->shell_open("https://docs.godotengine.org/"); } break; case HELP_QA: { OS::get_singleton()->shell_open("https://godotengine.org/qa/"); @@ -2747,6 +2771,8 @@ void EditorNode::set_current_scene(int p_idx) { Dictionary state = editor_data.restore_edited_scene_state(editor_selection, &editor_history); _edit_current(); + _update_title(); + call_deferred("_set_main_scene_state", state, get_edited_scene()); //do after everything else is done setting up } @@ -2835,7 +2861,7 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b errors.push_back(E->get()); } - dependency_error->show(lpath, errors); + dependency_error->show(DependencyErrorDialog::MODE_SCENE, lpath, errors); opening_prev = false; if (prev != -1) { @@ -2900,6 +2926,8 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b _update_scene_tabs(); _add_to_recent_scenes(lpath); + editor_folding.load_scene_folding(new_scene, lpath); + prev_scene->set_disabled(previous_scenes.size() == 0); opening_prev = false; @@ -3114,6 +3142,86 @@ void EditorNode::stop_child_process() { _menu_option_confirm(RUN_STOP, false); } +Ref<Texture> EditorNode::get_object_icon(const Object *p_object, const String &p_fallback) const { + ERR_FAIL_COND_V(!p_object || !gui_base, NULL); + + Ref<Script> script = p_object->get_script(); + if (script.is_null() && p_object->is_class("Script")) { + script = p_object; + } + + StringName name; + String icon_path; + if (script.is_valid()) { + name = EditorNode::get_editor_data().script_class_get_name(script->get_path()); + icon_path = EditorNode::get_editor_data().script_class_get_icon_path(name); + name = script->get_instance_base_type(); + } + + if (gui_base->has_icon(p_object->get_class(), "EditorIcons")) + return gui_base->get_icon(p_object->get_class(), "EditorIcons"); + + if (icon_path.length()) + return ResourceLoader::load(icon_path); + + if (p_object->has_meta("_editor_icon")) + return p_object->get_meta("_editor_icon"); + + if (name != StringName()) { + const Map<String, Vector<EditorData::CustomType> > &p_map = EditorNode::get_editor_data().get_custom_types(); + for (const Map<String, Vector<EditorData::CustomType> >::Element *E = p_map.front(); E; E = E->next()) { + const Vector<EditorData::CustomType> &ct = E->value(); + for (int i = 0; i < ct.size(); ++i) { + if (ct[i].name == name && ct[i].icon.is_valid()) { + return ct[i].icon; + } + } + } + } + + if (p_fallback.length()) + return gui_base->get_icon(p_fallback, "EditorIcons"); + + return NULL; +} + +Ref<Texture> EditorNode::get_class_icon(const String &p_class, const String &p_fallback) const { + ERR_FAIL_COND_V(p_class.empty(), NULL); + + if (gui_base->has_icon(p_class, "EditorIcons")) { + return gui_base->get_icon(p_class, "EditorIcons"); + } + + if (ScriptServer::is_global_class(p_class)) { + String icon_path = EditorNode::get_editor_data().script_class_get_icon_path(p_class); + RES icon; + if (FileAccess::exists(icon_path)) { + icon = ResourceLoader::load(icon_path); + } + if (!icon.is_valid()) { + icon = gui_base->get_icon(ScriptServer::get_global_class_base(p_class), "EditorIcons"); + } + return icon; + } + + const Map<String, Vector<EditorData::CustomType> > &p_map = EditorNode::get_editor_data().get_custom_types(); + for (const Map<String, Vector<EditorData::CustomType> >::Element *E = p_map.front(); E; E = E->next()) { + const Vector<EditorData::CustomType> &ct = E->value(); + for (int i = 0; i < ct.size(); ++i) { + if (ct[i].name == p_class) { + if (ct[i].icon.is_valid()) { + return ct[i].icon; + } + } + } + } + + if (p_fallback.length() && gui_base->has_icon(p_fallback, "EditorIcons")) + return gui_base->get_icon(p_fallback, "EditorIcons"); + + return NULL; +} + void EditorNode::progress_add_task(const String &p_task, const String &p_label, int p_steps, bool p_can_cancel) { singleton->progress_dialog->add_task(p_task, p_label, p_steps, p_can_cancel); @@ -3271,19 +3379,12 @@ void EditorNode::_dock_select_input(const Ref<InputEvent> &p_input) { dock_slot[nrect]->show(); dock_select->update(); - VSplitContainer *splits[DOCK_SLOT_MAX / 2] = { - left_l_vsplit, - left_r_vsplit, - right_l_vsplit, - right_r_vsplit, - }; - - for (int i = 0; i < 4; i++) { + for (int i = 0; i < vsplits.size(); i++) { bool in_use = dock_slot[i * 2 + 0]->get_tab_count() || dock_slot[i * 2 + 1]->get_tab_count(); if (in_use) - splits[i]->show(); + vsplits[i]->show(); else - splits[i]->hide(); + vsplits[i]->hide(); } if (right_l_vsplit->is_visible() || right_r_vsplit->is_visible()) @@ -3455,30 +3556,16 @@ void EditorNode::_save_docks_to_config(Ref<ConfigFile> p_layout, const String &p p_layout->set_value(p_section, "dock_filesystem_split", filesystem_dock->get_split_offset()); - VSplitContainer *splits[DOCK_SLOT_MAX / 2] = { - left_l_vsplit, - left_r_vsplit, - right_l_vsplit, - right_r_vsplit, - }; + for (int i = 0; i < vsplits.size(); i++) { - for (int i = 0; i < DOCK_SLOT_MAX / 2; i++) { - - if (splits[i]->is_visible_in_tree()) { - p_layout->set_value(p_section, "dock_split_" + itos(i + 1), splits[i]->get_split_offset()); + if (vsplits[i]->is_visible_in_tree()) { + p_layout->set_value(p_section, "dock_split_" + itos(i + 1), vsplits[i]->get_split_offset()); } } - HSplitContainer *h_splits[4] = { - left_l_hsplit, - left_r_hsplit, - main_hsplit, - right_hsplit, - }; - - for (int i = 0; i < 4; i++) { + for (int i = 0; i < hsplits.size(); i++) { - p_layout->set_value(p_section, "dock_hsplit_" + itos(i + 1), h_splits[i]->get_split_offset()); + p_layout->set_value(p_section, "dock_hsplit_" + itos(i + 1), hsplits[i]->get_split_offset()); } } @@ -3524,21 +3611,14 @@ void EditorNode::_load_docks() { void EditorNode::_update_dock_slots_visibility() { - VSplitContainer *splits[DOCK_SLOT_MAX / 2] = { - left_l_vsplit, - left_r_vsplit, - right_l_vsplit, - right_r_vsplit, - }; - if (!docks_visible) { for (int i = 0; i < DOCK_SLOT_MAX; i++) { dock_slot[i]->hide(); } - for (int i = 0; i < DOCK_SLOT_MAX / 2; i++) { - splits[i]->hide(); + for (int i = 0; i < vsplits.size(); i++) { + vsplits[i]->hide(); } right_hsplit->hide(); @@ -3552,12 +3632,12 @@ void EditorNode::_update_dock_slots_visibility() { dock_slot[i]->hide(); } - for (int i = 0; i < DOCK_SLOT_MAX / 2; i++) { + for (int i = 0; i < vsplits.size(); i++) { bool in_use = dock_slot[i * 2 + 0]->get_tab_count() || dock_slot[i * 2 + 1]->get_tab_count(); if (in_use) - splits[i]->show(); + vsplits[i]->show(); else - splits[i]->hide(); + vsplits[i]->hide(); } for (int i = 0; i < DOCK_SLOT_MAX; i++) { @@ -3577,13 +3657,7 @@ void EditorNode::_update_dock_slots_visibility() { void EditorNode::_dock_tab_changed(int p_tab) { - // update visibility but dont set current tab - VSplitContainer *splits[DOCK_SLOT_MAX / 2] = { - left_l_vsplit, - left_r_vsplit, - right_l_vsplit, - right_r_vsplit, - }; + // update visibility but don't set current tab if (!docks_visible) { @@ -3591,8 +3665,8 @@ void EditorNode::_dock_tab_changed(int p_tab) { dock_slot[i]->hide(); } - for (int i = 0; i < DOCK_SLOT_MAX / 2; i++) { - splits[i]->hide(); + for (int i = 0; i < vsplits.size(); i++) { + vsplits[i]->hide(); } right_hsplit->hide(); @@ -3606,12 +3680,12 @@ void EditorNode::_dock_tab_changed(int p_tab) { dock_slot[i]->hide(); } - for (int i = 0; i < DOCK_SLOT_MAX / 2; i++) { + for (int i = 0; i < vsplits.size(); i++) { bool in_use = dock_slot[i * 2 + 0]->get_tab_count() || dock_slot[i * 2 + 1]->get_tab_count(); if (in_use) - splits[i]->show(); + vsplits[i]->show(); else - splits[i]->hide(); + vsplits[i]->hide(); } bottom_panel->show(); @@ -3670,42 +3744,28 @@ void EditorNode::_load_docks_from_config(Ref<ConfigFile> p_layout, const String } filesystem_dock->set_split_offset(fs_split_ofs); - VSplitContainer *splits[DOCK_SLOT_MAX / 2] = { - left_l_vsplit, - left_r_vsplit, - right_l_vsplit, - right_r_vsplit, - }; - - for (int i = 0; i < DOCK_SLOT_MAX / 2; i++) { + for (int i = 0; i < vsplits.size(); i++) { if (!p_layout->has_section_key(p_section, "dock_split_" + itos(i + 1))) continue; int ofs = p_layout->get_value(p_section, "dock_split_" + itos(i + 1)); - splits[i]->set_split_offset(ofs); + vsplits[i]->set_split_offset(ofs); } - HSplitContainer *h_splits[4] = { - left_l_hsplit, - left_r_hsplit, - main_hsplit, - right_hsplit, - }; - - for (int i = 0; i < 4; i++) { + for (int i = 0; i < hsplits.size(); i++) { if (!p_layout->has_section_key(p_section, "dock_hsplit_" + itos(i + 1))) continue; int ofs = p_layout->get_value(p_section, "dock_hsplit_" + itos(i + 1)); - h_splits[i]->set_split_offset(ofs); + hsplits[i]->set_split_offset(ofs); } - for (int i = 0; i < DOCK_SLOT_MAX / 2; i++) { + for (int i = 0; i < vsplits.size(); i++) { bool in_use = dock_slot[i * 2 + 0]->get_tab_count() || dock_slot[i * 2 + 1]->get_tab_count(); if (in_use) - splits[i]->show(); + vsplits[i]->show(); else - splits[i]->hide(); + vsplits[i]->hide(); } if (right_l_vsplit->is_visible() || right_r_vsplit->is_visible()) @@ -3844,7 +3904,7 @@ void EditorNode::_scene_tab_closed(int p_tab) { } void EditorNode::_scene_tab_hover(int p_tab) { - if (bool(EDITOR_GET("interface/scene_tabs/show_thumbnail_on_hover")) == false) { + if (!bool(EDITOR_GET("interface/scene_tabs/show_thumbnail_on_hover"))) { return; } int current_tab = scene_tabs->get_current_tab(); @@ -3853,7 +3913,9 @@ void EditorNode::_scene_tab_hover(int p_tab) { tab_preview_panel->hide(); } else { String path = editor_data.get_scene_path(p_tab); - EditorResourcePreview::get_singleton()->queue_resource_preview(path, this, "_thumbnail_done", p_tab); + if (path != String()) { + EditorResourcePreview::get_singleton()->queue_resource_preview(path, this, "_thumbnail_done", p_tab); + } } } @@ -3889,6 +3951,7 @@ void EditorNode::_scene_tab_input(const Ref<InputEvent> &p_input) { 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("Show in FileSystem"), FILE_SHOW_IN_FILESYSTEM); scene_tabs_context_menu->add_item(TTR("Play This Scene"), RUN_PLAY_SCENE); scene_tabs_context_menu->add_item(TTR("Close Tab"), FILE_CLOSE); } @@ -3903,7 +3966,7 @@ void EditorNode::_reposition_active_tab(int idx_to) { _update_scene_tabs(); } -void EditorNode::_thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Variant &p_udata) { +void EditorNode::_thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Ref<Texture> &p_small_preview, const Variant &p_udata) { int p_tab = p_udata.operator signed int(); if (p_preview.is_valid()) { Rect2 rect = scene_tabs->get_tab_rect(p_tab); @@ -4476,6 +4539,16 @@ void EditorNode::_bottom_panel_raise_toggled(bool p_pressed) { } } +void EditorNode::_update_video_driver_color() { + + //todo probably should de-harcode this and add to editor settings + if (video_driver->get_text() == "GLES2") { + video_driver->add_color_override("font_color", Color::hex(0x5586a4ff)); + } else if (video_driver->get_text() == "GLES3") { + video_driver->add_color_override("font_color", Color::hex(0xa5557dff)); + } +} + void EditorNode::_video_driver_selected(int p_which) { String driver = video_driver->get_item_metadata(p_which); @@ -4489,6 +4562,20 @@ void EditorNode::_video_driver_selected(int p_which) { video_driver_request = driver; video_restart_dialog->popup_centered_minsize(); video_driver->select(video_driver_current); + _update_video_driver_color(); +} + +void EditorNode::_resource_saved(RES p_resource, const String &p_path) { + if (EditorFileSystem::get_singleton()) { + EditorFileSystem::get_singleton()->update_file(p_path); + } + + singleton->editor_folding.save_resource_folding(p_resource, p_path); +} + +void EditorNode::_resource_loaded(RES p_resource, const String &p_path) { + + singleton->editor_folding.load_resource_folding(p_resource, p_path); } void EditorNode::_bind_methods() { @@ -4595,11 +4682,10 @@ EditorNode::EditorNode() { Physics2DServer::get_singleton()->set_active(false); // no physics by default if editor ScriptServer::set_scripting_enabled(false); // no scripting by default if editor - EditorHelp::generate_doc(); //before any editor classes are crated + EditorHelp::generate_doc(); //before any editor classes are created SceneState::set_disable_placeholders(true); ResourceLoader::clear_translation_remaps(); //no remaps using during editor ResourceLoader::clear_path_remaps(); - editor_initialize_certificates(); //for asset sharing InputDefault *id = Object::cast_to<InputDefault>(Input::get_singleton()); @@ -4681,6 +4767,8 @@ EditorNode::EditorNode() { ResourceLoader::set_timestamp_on_load(true); ResourceSaver::set_timestamp_on_save(true); + default_value_cache = memnew(EditorDefaultClassValueCache); + { //register importers at the beginning, so dialogs are created with the right extensions Ref<ResourceImporterTexture> import_texture; import_texture.instance(); @@ -4792,8 +4880,9 @@ 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_RST("interface/inspector/auto_unfold_edited", true); EDITOR_DEF("interface/inspector/horizontal_vector2_editing", false); - EDITOR_DEF("interface/inspector/horizontal_vector3_editing", true); + EDITOR_DEF("interface/inspector/horizontal_vector_types_editing", true); EDITOR_DEF("interface/inspector/open_resources_in_current_inspector", true); EDITOR_DEF("interface/inspector/resources_types_to_open_in_new_inspector", "SpatialMaterial,Script"); EDITOR_DEF("run/auto_save/save_before_running", true); @@ -4840,9 +4929,6 @@ EditorNode::EditorNode() { left_l_vsplit->add_child(dock_slot[DOCK_SLOT_LEFT_UL]); dock_slot[DOCK_SLOT_LEFT_BL] = memnew(TabContainer); left_l_vsplit->add_child(dock_slot[DOCK_SLOT_LEFT_BL]); - left_l_vsplit->hide(); - dock_slot[DOCK_SLOT_LEFT_UL]->hide(); - dock_slot[DOCK_SLOT_LEFT_BL]->hide(); left_r_hsplit = memnew(HSplitContainer); left_l_hsplit->add_child(left_r_hsplit); @@ -4880,19 +4966,22 @@ EditorNode::EditorNode() { right_r_vsplit->add_child(dock_slot[DOCK_SLOT_RIGHT_UR]); dock_slot[DOCK_SLOT_RIGHT_BR] = memnew(TabContainer); right_r_vsplit->add_child(dock_slot[DOCK_SLOT_RIGHT_BR]); - right_r_vsplit->hide(); - dock_slot[DOCK_SLOT_RIGHT_UR]->hide(); - dock_slot[DOCK_SLOT_RIGHT_BR]->hide(); - left_l_vsplit->connect("dragged", this, "_dock_split_dragged"); - left_r_vsplit->connect("dragged", this, "_dock_split_dragged"); - right_l_vsplit->connect("dragged", this, "_dock_split_dragged"); - right_r_vsplit->connect("dragged", this, "_dock_split_dragged"); + // Store them for easier access + vsplits.push_back(left_l_vsplit); + vsplits.push_back(left_r_vsplit); + vsplits.push_back(right_l_vsplit); + vsplits.push_back(right_r_vsplit); - left_l_hsplit->connect("dragged", this, "_dock_split_dragged"); - left_r_hsplit->connect("dragged", this, "_dock_split_dragged"); - main_hsplit->connect("dragged", this, "_dock_split_dragged"); - right_hsplit->connect("dragged", this, "_dock_split_dragged"); + hsplits.push_back(left_l_hsplit); + hsplits.push_back(left_r_hsplit); + hsplits.push_back(main_hsplit); + hsplits.push_back(right_hsplit); + + for (int i = 0; i < vsplits.size(); i++) { + vsplits[i]->connect("dragged", this, "_dock_split_dragged"); + hsplits[i]->connect("dragged", this, "_dock_split_dragged"); + } dock_select_popup = memnew(PopupPanel); gui_base->add_child(dock_select_popup); @@ -4931,7 +5020,7 @@ EditorNode::EditorNode() { dock_select_rect_over = -1; dock_popup_selected = -1; for (int i = 0; i < DOCK_SLOT_MAX; i++) { - dock_slot[i]->set_custom_minimum_size(Size2(230, 220) * EDSCALE); + dock_slot[i]->set_custom_minimum_size(Size2(170, 0) * EDSCALE); dock_slot[i]->set_v_size_flags(Control::SIZE_EXPAND_FILL); dock_slot[i]->set_popup(dock_select_popup); dock_slot[i]->connect("pre_popup_pressed", this, "_dock_pre_popup", varray(i)); @@ -5108,7 +5197,7 @@ EditorNode::EditorNode() { p->add_separator(); p->add_shortcut(ED_SHORTCUT("editor/save_scene", TTR("Save Scene"), KEY_MASK_CMD + KEY_S), FILE_SAVE_SCENE); p->add_shortcut(ED_SHORTCUT("editor/save_scene_as", TTR("Save Scene As..."), KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_S), FILE_SAVE_AS_SCENE); - p->add_shortcut(ED_SHORTCUT("editor/save_all_scenes", TTR("Save all Scenes"), KEY_MASK_ALT + KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_S), FILE_SAVE_ALL_SCENES); + p->add_shortcut(ED_SHORTCUT("editor/save_all_scenes", TTR("Save All Scenes"), KEY_MASK_ALT + KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_S), FILE_SAVE_ALL_SCENES); p->add_separator(); p->add_shortcut(ED_SHORTCUT("editor/close_scene", TTR("Close Scene"), KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_W), FILE_CLOSE); p->add_separator(); @@ -5346,22 +5435,21 @@ EditorNode::EditorNode() { play_custom_scene_button->set_shortcut(ED_SHORTCUT("editor/play_custom_scene", TTR("Play Custom Scene"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F5)); #endif + // Toggle for video driver video_driver = memnew(OptionButton); video_driver->set_flat(true); video_driver->set_focus_mode(Control::FOCUS_NONE); video_driver->set_v_size_flags(Control::SIZE_SHRINK_CENTER); + video_driver->connect("item_selected", this, "_video_driver_selected"); + video_driver->add_font_override("font", gui_base->get_font("bold", "EditorFonts")); + menu_hb->add_child(video_driver); + String video_drivers = ProjectSettings::get_singleton()->get_custom_property_info()["rendering/quality/driver/driver_name"].hint_string; String current_video_driver = OS::get_singleton()->get_video_driver_name(OS::get_singleton()->get_current_video_driver()); - menu_hb->add_child(video_driver); video_driver_current = 0; for (int i = 0; i < video_drivers.get_slice_count(","); i++) { String driver = video_drivers.get_slice(",", i); - if (gui_base->has_icon(driver, "EditorIcons")) { - video_driver->add_icon_item(gui_base->get_icon(driver, "EditorIcons"), ""); - } else { - video_driver->add_item(driver); - } - + video_driver->add_item(driver); video_driver->set_item_metadata(i, driver); if (current_video_driver == driver) { @@ -5370,7 +5458,8 @@ EditorNode::EditorNode() { } } - video_driver->connect("item_selected", this, "_video_driver_selected"); + _update_video_driver_color(); + video_restart_dialog = memnew(ConfirmationDialog); video_restart_dialog->set_text(TTR("Changing the video driver requires restarting the editor.")); video_restart_dialog->get_ok()->set_text(TTR("Save & Restart")); @@ -5405,63 +5494,72 @@ EditorNode::EditorNode() { _menu_option(SETTINGS_UPDATE_SPINNER_HIDE); } - scene_tree_dock = memnew(SceneTreeDock(this, scene_root, editor_selection, editor_data)); - dock_slot[DOCK_SLOT_RIGHT_UL]->add_child(scene_tree_dock); - dock_slot[DOCK_SLOT_RIGHT_UL]->set_tab_title(scene_tree_dock->get_index(), TTR("Scene")); - dock_slot[DOCK_SLOT_LEFT_BR]->hide(); + // Instantiate and place editor docks + scene_tree_dock = memnew(SceneTreeDock(this, scene_root, editor_selection, editor_data)); inspector_dock = memnew(InspectorDock(this, editor_data)); - dock_slot[DOCK_SLOT_RIGHT_BL]->add_child(inspector_dock); - dock_slot[DOCK_SLOT_RIGHT_BL]->set_tab_title(inspector_dock->get_index(), TTR("Inspector")); - - Button *property_editable_warning; - import_dock = memnew(ImportDock); - dock_slot[DOCK_SLOT_RIGHT_UL]->add_child(import_dock); - dock_slot[DOCK_SLOT_RIGHT_UL]->set_tab_title(import_dock->get_index(), TTR("Import")); - - bool use_single_dock_column = (OS::get_singleton()->get_screen_size(OS::get_singleton()->get_current_screen()).x < 1200); - node_dock = memnew(NodeDock); - if (use_single_dock_column) { - dock_slot[DOCK_SLOT_RIGHT_UL]->add_child(node_dock); - dock_slot[DOCK_SLOT_RIGHT_UL]->set_tab_title(node_dock->get_index(), TTR("Node")); - } else { - dock_slot[DOCK_SLOT_RIGHT_BL]->add_child(node_dock); - dock_slot[DOCK_SLOT_RIGHT_BL]->set_tab_title(node_dock->get_index(), TTR("Node")); - } filesystem_dock = memnew(FileSystemDock(this)); - filesystem_dock->set_file_list_display_mode(int(EditorSettings::get_singleton()->get("docks/filesystem/display_mode"))); - - if (use_single_dock_column) { - dock_slot[DOCK_SLOT_RIGHT_BL]->add_child(filesystem_dock); - dock_slot[DOCK_SLOT_RIGHT_BL]->set_tab_title(filesystem_dock->get_index(), TTR("FileSystem")); - left_r_vsplit->hide(); - dock_slot[DOCK_SLOT_LEFT_UR]->hide(); - dock_slot[DOCK_SLOT_LEFT_BR]->hide(); - } else { - dock_slot[DOCK_SLOT_LEFT_UR]->add_child(filesystem_dock); - dock_slot[DOCK_SLOT_LEFT_UR]->set_tab_title(filesystem_dock->get_index(), TTR("FileSystem")); - } + filesystem_dock->set_file_list_display_mode(int(EditorSettings::get_singleton()->get("docks/filesystem/files_display_mode"))); filesystem_dock->connect("open", this, "open_request"); filesystem_dock->connect("instance", this, "_instance_request"); - const String docks_section = "docks"; + // Scene: Top left + dock_slot[DOCK_SLOT_LEFT_UR]->add_child(scene_tree_dock); + dock_slot[DOCK_SLOT_LEFT_UR]->set_tab_title(scene_tree_dock->get_index(), TTR("Scene")); + + // Import: Top left, behind Scene + dock_slot[DOCK_SLOT_LEFT_UR]->add_child(import_dock); + dock_slot[DOCK_SLOT_LEFT_UR]->set_tab_title(import_dock->get_index(), TTR("Import")); + + // FileSystem: Bottom left + dock_slot[DOCK_SLOT_LEFT_BR]->add_child(filesystem_dock); + dock_slot[DOCK_SLOT_LEFT_BR]->set_tab_title(filesystem_dock->get_index(), TTR("FileSystem")); + + // Inspector: Full height right + dock_slot[DOCK_SLOT_RIGHT_UL]->add_child(inspector_dock); + dock_slot[DOCK_SLOT_RIGHT_UL]->set_tab_title(inspector_dock->get_index(), TTR("Inspector")); + + // Node: Full height right, behind Inspector + dock_slot[DOCK_SLOT_RIGHT_UL]->add_child(node_dock); + dock_slot[DOCK_SLOT_RIGHT_UL]->set_tab_title(node_dock->get_index(), TTR("Node")); + + // Hide unused dock slots and vsplits + dock_slot[DOCK_SLOT_LEFT_UL]->hide(); + dock_slot[DOCK_SLOT_LEFT_BL]->hide(); + dock_slot[DOCK_SLOT_RIGHT_BL]->hide(); + dock_slot[DOCK_SLOT_RIGHT_UR]->hide(); + dock_slot[DOCK_SLOT_RIGHT_BR]->hide(); + left_l_vsplit->hide(); + right_r_vsplit->hide(); + + // Add some offsets to left_r and main hsplits to make LEFT_R and RIGHT_L docks wider than minsize + left_r_hsplit->set_split_offset(70 * EDSCALE); + main_hsplit->set_split_offset(-70 * EDSCALE); + + // Define corresponding default layout + const String docks_section = "docks"; overridden_default_layout = -1; default_layout.instance(); - default_layout->set_value(docks_section, "dock_3", "FileSystem"); - default_layout->set_value(docks_section, "dock_5", "Scene,Import"); - default_layout->set_value(docks_section, "dock_6", "Inspector,Node"); + // Dock numbers are based on DockSlot enum value + 1 + default_layout->set_value(docks_section, "dock_3", "Scene,Import"); + default_layout->set_value(docks_section, "dock_4", "FileSystem"); + default_layout->set_value(docks_section, "dock_5", "Inspector,Node"); - for (int i = 0; i < DOCK_SLOT_MAX / 2; i++) - default_layout->set_value(docks_section, "dock_hsplit_" + itos(i + 1), 0); - for (int i = 0; i < DOCK_SLOT_MAX / 2; i++) + for (int i = 0; i < vsplits.size(); i++) default_layout->set_value(docks_section, "dock_split_" + itos(i + 1), 0); + default_layout->set_value(docks_section, "dock_hsplit_1", 0); + default_layout->set_value(docks_section, "dock_hsplit_2", 70 * EDSCALE); + default_layout->set_value(docks_section, "dock_hsplit_3", -70 * EDSCALE); + default_layout->set_value(docks_section, "dock_hsplit_4", 0); _update_layouts_menu(); + // Bottom panels + bottom_panel = memnew(PanelContainer); bottom_panel->add_style_override("panel", gui_base->get_stylebox("panel", "TabContainer")); center_split->add_child(bottom_panel); @@ -5634,10 +5732,6 @@ EditorNode::EditorNode() { 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 - // Waiting for PropertyEditor rewrite (planned for 3.1) to be refactored. - //add_editor_plugin(memnew(MaterialEditorPlugin(this))); - for (int i = 0; i < EditorPlugins::get_plugin_count(); i++) add_editor_plugin(EditorPlugins::create(i, this)); @@ -5772,6 +5866,9 @@ EditorNode::EditorNode() { print_handler.userdata = this; add_print_handler(&print_handler); + ResourceSaver::set_save_callback(_resource_saved); + ResourceLoader::set_load_callback(_resource_loaded); + #ifdef OSX_ENABLED ED_SHORTCUT("editor/editor_2d", TTR("Open 2D Editor"), KEY_MASK_ALT | KEY_1); ED_SHORTCUT("editor/editor_3d", TTR("Open 3D Editor"), KEY_MASK_ALT | KEY_2); @@ -5780,7 +5877,7 @@ EditorNode::EditorNode() { #else ED_SHORTCUT("editor/editor_2d", TTR("Open 2D Editor"), KEY_F1); ED_SHORTCUT("editor/editor_3d", TTR("Open 3D Editor"), KEY_F2); - ED_SHORTCUT("editor/editor_script", TTR("Open Script Editor"), KEY_F3); //hack neded for script editor F3 search to work :) Assign like this or don't use F3 + ED_SHORTCUT("editor/editor_script", TTR("Open Script Editor"), KEY_F3); //hack needed for script editor F3 search to work :) Assign like this or don't use F3 ED_SHORTCUT("editor/editor_help", TTR("Search Help"), KEY_F4); #endif ED_SHORTCUT("editor/editor_assetlib", TTR("Open Asset Library")); @@ -5800,6 +5897,7 @@ EditorNode::~EditorNode() { memdelete(editor_plugins_force_input_forwarding); memdelete(file_server); memdelete(progress_hb); + memdelete(default_value_cache); EditorSettings::destroy(); } @@ -5850,17 +5948,31 @@ bool EditorPluginList::forward_spatial_gui_input(Camera *p_camera, const Ref<Inp return discard; } -void EditorPluginList::forward_draw_over_viewport(Control *p_overlay) { +void EditorPluginList::forward_canvas_draw_over_viewport(Control *p_overlay) { + + for (int i = 0; i < plugins_list.size(); i++) { + plugins_list[i]->forward_canvas_draw_over_viewport(p_overlay); + } +} + +void EditorPluginList::forward_canvas_force_draw_over_viewport(Control *p_overlay) { + + for (int i = 0; i < plugins_list.size(); i++) { + plugins_list[i]->forward_canvas_force_draw_over_viewport(p_overlay); + } +} + +void EditorPluginList::forward_spatial_draw_over_viewport(Control *p_overlay) { for (int i = 0; i < plugins_list.size(); i++) { - plugins_list[i]->forward_draw_over_viewport(p_overlay); + plugins_list[i]->forward_spatial_draw_over_viewport(p_overlay); } } -void EditorPluginList::forward_force_draw_over_viewport(Control *p_overlay) { +void EditorPluginList::forward_spatial_force_draw_over_viewport(Control *p_overlay) { for (int i = 0; i < plugins_list.size(); i++) { - plugins_list[i]->forward_force_draw_over_viewport(p_overlay); + plugins_list[i]->forward_spatial_force_draw_over_viewport(p_overlay); } } diff --git a/editor/editor_node.h b/editor/editor_node.h index 5a17ab6ca0..0b82555acf 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -38,6 +38,7 @@ #include "editor/editor_about.h" #include "editor/editor_data.h" #include "editor/editor_export.h" +#include "editor/editor_folding.h" #include "editor/editor_inspector.h" #include "editor/editor_log.h" #include "editor/editor_name_dialog.h" @@ -125,6 +126,7 @@ private: FILE_SAVE_ALL_SCENES, FILE_SAVE_BEFORE_RUN, FILE_SAVE_AND_RUN, + FILE_SHOW_IN_FILESYSTEM, FILE_IMPORT_SUBSCENE, FILE_EXPORT_PROJECT, FILE_EXPORT_MESH_LIBRARY, @@ -207,8 +209,9 @@ private: int video_driver_current; String video_driver_request; void _video_driver_selected(int); + void _update_video_driver_color(); - //split + // Split containers HSplitContainer *left_l_hsplit; VSplitContainer *left_l_vsplit; @@ -221,7 +224,11 @@ private: VSplitContainer *center_split; - //main tabs + // To access those easily by index + Vector<VSplitContainer *> vsplits; + Vector<HSplitContainer *> hsplits; + + // Main tabs Tabs *scene_tabs; PopupMenu *scene_tabs_context_menu; @@ -265,6 +272,7 @@ private: Ref<Theme> theme; + EditorDefaultClassValueCache *default_value_cache; PopupMenu *recent_scenes; SceneTreeDock *scene_tree_dock; InspectorDock *inspector_dock; @@ -379,6 +387,7 @@ private: EditorSelection *editor_selection; ProjectExportDialog *project_export; EditorResourcePreview *resource_preview; + EditorFolding editor_folding; EditorFileServer *file_server; @@ -535,7 +544,7 @@ private: void _scene_tab_exit(); void _scene_tab_input(const Ref<InputEvent> &p_input); void _reposition_active_tab(int idx_to); - void _thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Variant &p_udata); + void _thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Ref<Texture> &p_small_preview, const Variant &p_udata); void _scene_tab_script_edited(int p_tab); Dictionary _get_main_scene_state(); @@ -594,6 +603,9 @@ private: PrintHandlerList print_handler; static void _print_handler(void *p_this, const String &p_string, bool p_error); + static void _resource_saved(RES p_resource, const String &p_path); + static void _resource_loaded(RES p_resource, const String &p_path); + protected: void _notification(int p_what); static void _bind_methods(); @@ -676,7 +688,7 @@ public: void fix_dependencies(const String &p_for_file); void clear_scene() { _cleanup_scene(); } Error load_scene(const String &p_scene, bool p_ignore_broken_deps = false, bool p_set_inherited = false, bool p_clear_errors = true, bool p_force_open_imported = false); - Error load_resource(const String &p_scene); + Error load_resource(const String &p_resource, bool p_ignore_broken_deps = false); bool is_scene_open(const String &p_path); @@ -684,6 +696,7 @@ public: void set_current_scene(int p_idx); static EditorData &get_editor_data() { return singleton->editor_data; } + static EditorFolding &get_editor_folding() { return singleton->editor_folding; } EditorHistory *get_editor_history() { return &editor_history; } static VSplitContainer *get_top_split() { return singleton->top_split; } @@ -706,6 +719,8 @@ public: void stop_child_process(); Ref<Theme> get_editor_theme() const { return theme; } + Ref<Texture> get_object_icon(const Object *p_object, const String &p_fallback = "Object") const; + Ref<Texture> get_class_icon(const String &p_class, const String &p_fallback = "Object") const; void show_accept(const String &p_text, const String &p_title); void show_warning(const String &p_text, const String &p_title = "Warning!"); @@ -810,9 +825,11 @@ public: void make_visible(bool p_visible); void edit(Object *p_object); bool forward_gui_input(const Ref<InputEvent> &p_event); + void forward_canvas_draw_over_viewport(Control *p_overlay); + void forward_canvas_force_draw_over_viewport(Control *p_overlay); bool forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event, bool serve_when_force_input_enabled); - void forward_draw_over_viewport(Control *p_overlay); - void forward_force_draw_over_viewport(Control *p_overlay); + void forward_spatial_draw_over_viewport(Control *p_overlay); + void forward_spatial_force_draw_over_viewport(Control *p_overlay); void add_plugin(EditorPlugin *p_plugin); void clear(); bool empty(); diff --git a/editor/editor_path.cpp b/editor/editor_path.cpp index 9506a0e951..0eff1fd7dd 100644 --- a/editor/editor_path.cpp +++ b/editor/editor_path.cpp @@ -54,12 +54,7 @@ void EditorPath::_add_children_to_popup(Object *p_obj, int p_depth) { if (!obj) continue; - Ref<Texture> icon; - - if (has_icon(obj->get_class(), "EditorIcons")) - icon = get_icon(obj->get_class(), "EditorIcons"); - else - icon = get_icon("Object", "EditorIcons"); + Ref<Texture> icon = EditorNode::get_singleton()->get_object_icon(obj); int index = popup->get_item_count(); popup->add_icon_item(icon, E->get().name.capitalize(), objects.size()); @@ -122,12 +117,7 @@ void EditorPath::_notification(int p_what) { String type = obj->get_class(); - Ref<Texture> icon; - - if (has_icon(obj->get_class(), "EditorIcons")) - icon = get_icon(obj->get_class(), "EditorIcons"); - else - icon = get_icon("Object", "EditorIcons"); + Ref<Texture> icon = EditorNode::get_singleton()->get_object_icon(obj); icon->draw(ci, Point2i(ofs, (size.height - icon->get_height()) / 2)); diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp index 1f2e73654c..86b2db877e 100644 --- a/editor/editor_plugin.cpp +++ b/editor/editor_plugin.cpp @@ -39,6 +39,7 @@ #include "scene/3d/camera.h" #include "scene/gui/popup_menu.h" #include "servers/visual_server.h" + Array EditorInterface::_make_mesh_previews(const Array &p_meshes, int p_preview_size) { Vector<Ref<Mesh> > meshes; @@ -474,7 +475,6 @@ void EditorPlugin::set_force_draw_over_forwarding_enabled() { } void EditorPlugin::notify_scene_changed(const Node *scn_root) { - if (scn_root == NULL) return; emit_signal("scene_changed", scn_root); } @@ -503,17 +503,17 @@ bool EditorPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return false; } -void EditorPlugin::forward_draw_over_viewport(Control *p_overlay) { +void EditorPlugin::forward_canvas_draw_over_viewport(Control *p_overlay) { - if (get_script_instance() && get_script_instance()->has_method("forward_draw_over_viewport")) { - get_script_instance()->call("forward_draw_over_viewport", p_overlay); + if (get_script_instance() && get_script_instance()->has_method("forward_canvas_draw_over_viewport")) { + get_script_instance()->call("forward_canvas_draw_over_viewport", p_overlay); } } -void EditorPlugin::forward_force_draw_over_viewport(Control *p_overlay) { +void EditorPlugin::forward_canvas_force_draw_over_viewport(Control *p_overlay) { - if (get_script_instance() && get_script_instance()->has_method("forward_force_draw_over_viewport")) { - get_script_instance()->call("forward_force_draw_over_viewport", p_overlay); + if (get_script_instance() && get_script_instance()->has_method("forward_canvas_force_draw_over_viewport")) { + get_script_instance()->call("forward_canvas_force_draw_over_viewport", p_overlay); } } @@ -522,7 +522,7 @@ int EditorPlugin::update_overlays() const { if (SpatialEditor::get_singleton()->is_visible()) { int count = 0; - for (int i = 0; i < SpatialEditor::VIEWPORTS_COUNT; i++) { + for (uint32_t i = 0; i < SpatialEditor::VIEWPORTS_COUNT; i++) { SpatialEditorViewport *vp = SpatialEditor::get_singleton()->get_editor_viewport(i); if (vp->is_visible()) { vp->update_surface(); @@ -545,6 +545,20 @@ bool EditorPlugin::forward_spatial_gui_input(Camera *p_camera, const Ref<InputEv return false; } + +void EditorPlugin::forward_spatial_draw_over_viewport(Control *p_overlay) { + + if (get_script_instance() && get_script_instance()->has_method("forward_spatial_draw_over_viewport")) { + get_script_instance()->call("forward_spatial_draw_over_viewport", p_overlay); + } +} + +void EditorPlugin::forward_spatial_force_draw_over_viewport(Control *p_overlay) { + + if (get_script_instance() && get_script_instance()->has_method("forward_spatial_force_draw_over_viewport")) { + get_script_instance()->call("forward_spatial_force_draw_over_viewport", p_overlay); + } +} String EditorPlugin::get_name() const { if (get_script_instance() && get_script_instance()->has_method("get_plugin_name")) { @@ -769,8 +783,8 @@ void EditorPlugin::_bind_methods() { ClassDB::bind_method(D_METHOD("get_script_create_dialog"), &EditorPlugin::get_script_create_dialog); ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "forward_canvas_gui_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"))); - 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("forward_canvas_draw_over_viewport", PropertyInfo(Variant::OBJECT, "overlay", PROPERTY_HINT_RESOURCE_TYPE, "Control"))); + ClassDB::add_virtual_method(get_class_static(), MethodInfo("forward_canvas_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"))); 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")); diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h index 1ebddbe2b0..e03aeb5d30 100644 --- a/editor/editor_plugin.h +++ b/editor/editor_plugin.h @@ -31,15 +31,15 @@ #ifndef EDITOR_PLUGIN_H #define EDITOR_PLUGIN_H +#include "core/io/config_file.h" +#include "core/undo_redo.h" #include "editor/editor_inspector.h" #include "editor/import/editor_import_plugin.h" #include "editor/import/resource_importer_scene.h" #include "editor/script_create_dialog.h" -#include "io/config_file.h" #include "scene/gui/tool_button.h" #include "scene/main/node.h" #include "scene/resources/texture.h" -#include "undo_redo.h" /** @author Juan Linietsky <reduzio@gmail.com> @@ -174,9 +174,13 @@ public: void notify_resource_saved(const Ref<Resource> &p_resource); 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); + virtual void forward_canvas_draw_over_viewport(Control *p_overlay); + virtual void forward_canvas_force_draw_over_viewport(Control *p_overlay); + virtual bool forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event); + virtual void forward_spatial_draw_over_viewport(Control *p_overlay); + virtual void forward_spatial_force_draw_over_viewport(Control *p_overlay); + virtual String get_name() const; virtual const Ref<Texture> get_icon() const; virtual bool has_main_screen() const; diff --git a/editor/editor_plugin_settings.cpp b/editor/editor_plugin_settings.cpp index 68f8ed6d94..30027c0c34 100644 --- a/editor/editor_plugin_settings.cpp +++ b/editor/editor_plugin_settings.cpp @@ -30,11 +30,11 @@ #include "editor_plugin_settings.h" +#include "core/io/config_file.h" +#include "core/os/file_access.h" +#include "core/os/main_loop.h" +#include "core/project_settings.h" #include "editor_node.h" -#include "io/config_file.h" -#include "os/file_access.h" -#include "os/main_loop.h" -#include "project_settings.h" #include "scene/gui/margin_container.h" void EditorPluginSettings::_notification(int p_what) { @@ -117,7 +117,7 @@ void EditorPluginSettings::update_plugins() { TreeItem *item = plugin_list->create_item(root); item->set_text(0, name); - item->set_tooltip(0, "Name: " + name + "\nPath: " + path + "\nMain Script: " + script); + item->set_tooltip(0, "Name: " + name + "\nPath: " + path + "\nMain Script: " + script + "\nDescription: " + description); item->set_metadata(0, d); item->set_text(1, version); item->set_metadata(1, script); diff --git a/editor/editor_plugin_settings.h b/editor/editor_plugin_settings.h index 194fac6b92..fe14f87cfc 100644 --- a/editor/editor_plugin_settings.h +++ b/editor/editor_plugin_settings.h @@ -31,11 +31,11 @@ #ifndef EDITORPLUGINSETTINGS_H #define EDITORPLUGINSETTINGS_H +#include "core/undo_redo.h" #include "editor/plugin_config_dialog.h" #include "editor_data.h" #include "property_editor.h" #include "scene/gui/dialogs.h" -#include "undo_redo.h" class EditorPluginSettings : public VBoxContainer { diff --git a/editor/editor_profiler.cpp b/editor/editor_profiler.cpp index f57c863bcf..d3978749c0 100644 --- a/editor/editor_profiler.cpp +++ b/editor/editor_profiler.cpp @@ -30,9 +30,9 @@ #include "editor_profiler.h" +#include "core/os/os.h" #include "editor_scale.h" #include "editor_settings.h" -#include "os/os.h" void EditorProfiler::_make_metric_ptrs(Metric &m) { @@ -100,8 +100,6 @@ void EditorProfiler::clear() { updating_frame = false; hover_metric = -1; seeking = false; - - _update_plot(); } static String _get_percent_txt(float p_value, float p_total) { @@ -169,7 +167,7 @@ void EditorProfiler::_update_plot() { int w = graph->get_size().width; int h = graph->get_size().height; - bool reset_texture = graph_texture.is_null(); + bool reset_texture = false; int desired_len = w * h * 4; @@ -259,7 +257,7 @@ void EditorProfiler::_update_plot() { //get const Metric &m = frame_metrics[idx]; - if (m.valid == false) + if (!m.valid) continue; //skip because invalid float value = 0; @@ -437,6 +435,7 @@ void EditorProfiler::_activate_pressed() { void EditorProfiler::_clear_pressed() { clear(); + _update_plot(); } void EditorProfiler::_notification(int p_what) { diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 3c3df6b8ef..b68ec97406 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -108,6 +108,7 @@ void EditorPropertyMultilineText::_open_big_text() { if (!big_text_dialog) { big_text = memnew(TextEdit); big_text->connect("text_changed", this, "_big_text_changed"); + big_text->set_wrap_enabled(true); big_text_dialog = memnew(AcceptDialog); big_text_dialog->add_child(big_text); big_text_dialog->set_title("Edit Text:"); @@ -152,6 +153,7 @@ EditorPropertyMultilineText::EditorPropertyMultilineText() { set_bottom_editor(hb); text = memnew(TextEdit); text->connect("text_changed", this, "_text_changed"); + text->set_wrap_enabled(true); add_focusable(text); hb->add_child(text); text->set_h_size_flags(SIZE_EXPAND_FILL); @@ -475,33 +477,16 @@ EditorPropertyCheck::EditorPropertyCheck() { void EditorPropertyEnum::_option_selected(int p_which) { - String text = options->get_item_text(p_which); - Vector<String> text_split = text.split(":"); - if (text_split.size() == 1) { - emit_signal("property_changed", get_edited_property(), p_which); - return; - } - String name = text_split[1]; - emit_signal("property_changed", get_edited_property(), name.to_int()); + int val = options->get_item_metadata(p_which); + emit_signal("property_changed", get_edited_property(), val); } void EditorPropertyEnum::update_property() { int which = get_edited_object()->get(get_edited_property()); - if (which == 0) { - options->select(which); - return; - } for (int i = 0; i < options->get_item_count(); i++) { - String text = options->get_item_text(i); - Vector<String> text_split = text.split(":"); - if (text_split.size() == 1) { - options->select(which); - return; - } - String name = text_split[1]; - if (itos(which) == name) { + if (which == (int)options->get_item_metadata(i)) { options->select(i); return; } @@ -509,8 +494,15 @@ void EditorPropertyEnum::update_property() { } void EditorPropertyEnum::setup(const Vector<String> &p_options) { + + int current_val = 0; for (int i = 0; i < p_options.size(); i++) { - options->add_item(p_options[i], i); + Vector<String> text_split = p_options[i].split(":"); + if (text_split.size() != 1) + current_val = text_split[1].to_int(); + options->add_item(text_split[0]); + options->set_item_metadata(i, current_val); + current_val += 1; } } @@ -651,6 +643,7 @@ public: int h = bsize * 2 + 1; int vofs = (rect.size.height - h) / 2; + Color color = get_color("highlight_color", "Editor"); for (int i = 0; i < 2; i++) { Point2 ofs(4, vofs); @@ -667,7 +660,8 @@ public: uint32_t idx = i * 10 + j; bool on = value & (1 << idx); Rect2 rect = Rect2(o, Size2(bsize, bsize)); - draw_rect(rect, Color(0, 0, 0, on ? 0.8 : 0.3)); + color.a = on ? 0.6 : 0.2; + draw_rect(rect, color); flag_rects.push_back(rect); } } @@ -815,10 +809,10 @@ void EditorPropertyInteger::_bind_methods() { ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyInteger::_value_changed); } -void EditorPropertyInteger::setup(int p_min, int p_max, bool p_allow_greater, bool p_allow_lesser) { +void EditorPropertyInteger::setup(int p_min, int p_max, int p_step, bool p_allow_greater, bool p_allow_lesser) { spin->set_min(p_min); spin->set_max(p_max); - spin->set_step(1); + spin->set_step(p_step); spin->set_allow_greater(p_allow_greater); spin->set_allow_lesser(p_allow_lesser); } @@ -844,18 +838,11 @@ void EditorPropertyObjectID::update_property() { if (type == "") type = "Object"; - String icon_type = type; - if (has_icon(icon_type, "EditorIcons")) { - type = icon_type; - } else { - type = "Object"; - } - ObjectID id = get_edited_object()->get(get_edited_property()); if (id != 0) { edit->set_text(type + " ID: " + itos(id)); edit->set_disabled(false); - edit->set_icon(get_icon(icon_type, "EditorIcons")); + edit->set_icon(EditorNode::get_singleton()->get_class_icon(type)); } else { edit->set_text(TTR("[Empty]")); edit->set_disabled(true); @@ -1188,21 +1175,39 @@ void EditorPropertyRect2::setup(double p_min, double p_max, double p_step, bool } EditorPropertyRect2::EditorPropertyRect2() { - VBoxContainer *vb = memnew(VBoxContainer); - add_child(vb); + + bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector_types_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", "w", "h" }; 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]); + 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; } + ///////////////////// VECTOR3 ///////////////////////// void EditorPropertyVector3::_value_changed(double val) { @@ -1250,7 +1255,7 @@ void EditorPropertyVector3::setup(double p_min, double p_max, double p_step, boo } EditorPropertyVector3::EditorPropertyVector3() { - bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector3_editing"); + bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector_types_editing"); BoxContainer *bc; @@ -1331,7 +1336,7 @@ void EditorPropertyPlane::setup(double p_min, double p_max, double p_step, bool EditorPropertyPlane::EditorPropertyPlane() { - bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector3_editing"); + bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector_types_editing"); BoxContainer *bc; @@ -1412,7 +1417,7 @@ void EditorPropertyQuat::setup(double p_min, double p_max, double p_step, bool p } EditorPropertyQuat::EditorPropertyQuat() { - bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector3_editing"); + bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector_types_editing"); BoxContainer *bc; @@ -1797,8 +1802,13 @@ void EditorPropertyNodePath::_node_selected(const NodePath &p_path) { NodePath path = p_path; Node *base_node = Object::cast_to<Node>(get_edited_object()); - if (base_node == NULL && get_edited_object()->has_method("get_root_path")) { - base_node = get_edited_object()->call("get_root_path"); + if (base_node == NULL) { + if (Object::cast_to<Resource>(get_edited_object())) { + Node *to_node = get_node(p_path); + path = get_tree()->get_edited_scene_root()->get_path_to(to_node); + } else if (get_edited_object()->has_method("get_root_path")) { + base_node = get_edited_object()->call("get_root_path"); + } } if (base_node) { // for AnimationTrackKeyEdit path = base_node->get_path().rel_path_to(p_path); @@ -1855,15 +1865,14 @@ void EditorPropertyNodePath::update_property() { Node *target_node = base_node->get_node(p); ERR_FAIL_COND(!target_node); - assign->set_text(target_node->get_name()); - - Ref<Texture> icon; - if (has_icon(target_node->get_class(), "EditorIcons")) - icon = get_icon(target_node->get_class(), "EditorIcons"); - else - icon = get_icon("Node", "EditorIcons"); + if (String(target_node->get_name()).find("@") != -1) { + assign->set_icon(Ref<Texture>()); + assign->set_text(p); + return; + } - assign->set_icon(icon); + assign->set_text(target_node->get_name()); + assign->set_icon(EditorNode::get_singleton()->get_object_icon(target_node, "Node")); } void EditorPropertyNodePath::setup(const NodePath &p_base_hint, Vector<StringName> p_valid_types) { @@ -1903,7 +1912,7 @@ EditorPropertyNodePath::EditorPropertyNodePath() { clear->connect("pressed", this, "_node_clear"); hbc->add_child(clear); - scene_tree = NULL; //do not allocate unnecesarily + scene_tree = NULL; //do not allocate unnecessarily } ////////////// RESOURCE ////////////////////// @@ -2094,12 +2103,18 @@ void EditorPropertyResource::_menu_option(int p_which) { } } -void EditorPropertyResource::_resource_preview(const String &p_path, const Ref<Texture> &p_preview, ObjectID p_obj) { +void EditorPropertyResource::_resource_preview(const String &p_path, const Ref<Texture> &p_preview, const Ref<Texture> &p_small_preview, ObjectID p_obj) { RES p = get_edited_object()->get(get_edited_property()); if (p.is_valid() && p->get_instance_id() == p_obj) { + String type = p->get_class_name(); + + if (ClassDB::is_parent_class(type, "Script")) { + assign->set_text(p->get_path().get_file()); + return; + } + if (p_preview.is_valid()) { - String type = p->get_class_name(); preview->set_margin(MARGIN_LEFT, assign->get_icon()->get_width() + assign->get_stylebox("normal")->get_default_margin(MARGIN_LEFT) + get_constant("hseparation", "Button")); if (type == "GradientTexture") { preview->set_stretch_mode(TextureRect::STRETCH_SCALE); @@ -2206,7 +2221,7 @@ void EditorPropertyResource::_update_menu() { RES r = res; if (r.is_valid() && r->get_path().is_resource_file()) { menu->add_separator(); - menu->add_item(TTR("Show in File System"), OBJ_MENU_SHOW_IN_FILE_SYSTEM); + menu->add_item(TTR("Show in FileSystem"), OBJ_MENU_SHOW_IN_FILE_SYSTEM); } } else { } @@ -2361,18 +2376,12 @@ void EditorPropertyResource::update_property() { assign->set_text(TTR("[empty]")); } else { - Ref<Texture> icon; - if (has_icon(res->get_class(), "EditorIcons")) - icon = get_icon(res->get_class(), "EditorIcons"); - else - icon = get_icon("Node", "EditorIcons"); - - assign->set_icon(icon); + assign->set_icon(EditorNode::get_singleton()->get_object_icon(res.operator->(), "Node")); if (res->get_name() != String()) { assign->set_text(res->get_name()); } else if (res->get_path().is_resource_file()) { - assign->set_text(res->get_name()); + assign->set_text(res->get_path().get_file()); assign->set_tooltip(res->get_path()); } else { assign->set_text(res->get_class()); @@ -2654,7 +2663,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } else if (p_hint == PROPERTY_HINT_LAYERS_2D_PHYSICS || p_hint == PROPERTY_HINT_LAYERS_2D_RENDER || p_hint == PROPERTY_HINT_LAYERS_3D_PHYSICS || p_hint == PROPERTY_HINT_LAYERS_3D_RENDER) { - EditorPropertyLayers::LayerType lt; + EditorPropertyLayers::LayerType lt = EditorPropertyLayers::LAYER_RENDER_2D; switch (p_hint) { case PROPERTY_HINT_LAYERS_2D_RENDER: lt = EditorPropertyLayers::LAYER_RENDER_2D; @@ -2668,7 +2677,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ case PROPERTY_HINT_LAYERS_3D_PHYSICS: lt = EditorPropertyLayers::LAYER_PHYSICS_3D; break; - default: {} //compiler could be smarter here and realize this cant happen + default: {} //compiler could be smarter here and realize this can't happen } EditorPropertyLayers *editor = memnew(EditorPropertyLayers); editor->setup(lt); @@ -2681,14 +2690,19 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } else { EditorPropertyInteger *editor = memnew(EditorPropertyInteger); - int min = 0, max = 65535; + int min = 0, max = 65535, step = 1; bool greater = true, lesser = true; if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) { - greater = false; //if using ranged, asume false by default + greater = false; //if using ranged, assume false by default lesser = false; min = p_hint_text.get_slice(",", 0).to_int(); max = p_hint_text.get_slice(",", 1).to_int(); + + if (p_hint_text.get_slice_count(",") >= 3) { + step = p_hint_text.get_slice(",", 2).to_int(); + } + for (int i = 2; i < p_hint_text.get_slice_count(","); i++) { String slice = p_hint_text.get_slice(",", i).strip_edges(); if (slice == "or_greater") { @@ -2700,7 +2714,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } } - editor->setup(min, max, greater, lesser); + editor->setup(min, max, step, greater, lesser); add_property_editor(p_path, editor); } @@ -2733,7 +2747,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ bool greater = true, lesser = true; if ((p_hint == PROPERTY_HINT_RANGE || p_hint == PROPERTY_HINT_EXP_RANGE) && p_hint_text.get_slice_count(",") >= 2) { - greater = false; //if using ranged, asume false by default + greater = false; //if using ranged, assume false by default lesser = false; min = p_hint_text.get_slice(",", 0).to_double(); max = p_hint_text.get_slice(",", 1).to_double(); @@ -3066,7 +3080,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ default: {} } - return false; //can be overriden, although it will most likely be last anyway + return false; //can be overridden, although it will most likely be last anyway } void EditorInspectorDefaultPlugin::parse_end() { diff --git a/editor/editor_properties.h b/editor/editor_properties.h index cfc433b880..35d8f4d306 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -265,7 +265,7 @@ protected: public: virtual void update_property(); - void setup(int p_min, int p_max, bool p_allow_greater, bool p_allow_lesser); + void setup(int p_min, int p_max, int p_step, bool p_allow_greater, bool p_allow_lesser); EditorPropertyInteger(); }; @@ -548,7 +548,7 @@ class EditorPropertyResource : public EditorProperty { void _file_selected(const String &p_path); void _menu_option(int p_which); - void _resource_preview(const String &p_path, const Ref<Texture> &p_preview, ObjectID p_obj); + void _resource_preview(const String &p_path, const Ref<Texture> &p_preview, const Ref<Texture> &p_small_preview, ObjectID p_obj); void _resource_selected(); void _viewport_selected(const NodePath &p_path); diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp index 23dbb026dd..24360813a2 100644 --- a/editor/editor_properties_array_dict.cpp +++ b/editor/editor_properties_array_dict.cpp @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* editor_properties_array_dict.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 "editor_properties_array_dict.h" #include "editor/editor_scale.h" #include "editor_properties.h" @@ -306,16 +336,16 @@ void EditorPropertyArray::update_property() { } break; case Variant::INT: { - EditorPropertyInteger *ed = memnew(EditorPropertyInteger); - ed->setup(-100000, 100000, true, true); - prop = ed; + EditorPropertyInteger *editor = memnew(EditorPropertyInteger); + editor->setup(-100000, 100000, 1, true, true); + prop = editor; } break; case Variant::REAL: { - EditorPropertyFloat *ed = memnew(EditorPropertyFloat); - ed->setup(-100000, 100000, 0.001, true, false, true, true); - prop = ed; + EditorPropertyFloat *editor = memnew(EditorPropertyFloat); + editor->setup(-100000, 100000, 0.001, true, false, true, true); + prop = editor; } break; case Variant::STRING: { @@ -327,63 +357,63 @@ void EditorPropertyArray::update_property() { case Variant::VECTOR2: { - EditorPropertyVector2 *ed = memnew(EditorPropertyVector2); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyVector2 *editor = memnew(EditorPropertyVector2); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; case Variant::RECT2: { - EditorPropertyRect2 *ed = memnew(EditorPropertyRect2); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyRect2 *editor = memnew(EditorPropertyRect2); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; case Variant::VECTOR3: { - EditorPropertyVector3 *ed = memnew(EditorPropertyVector3); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyVector3 *editor = memnew(EditorPropertyVector3); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; case Variant::TRANSFORM2D: { - EditorPropertyTransform2D *ed = memnew(EditorPropertyTransform2D); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyTransform2D *editor = memnew(EditorPropertyTransform2D); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; case Variant::PLANE: { - EditorPropertyPlane *ed = memnew(EditorPropertyPlane); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyPlane *editor = memnew(EditorPropertyPlane); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; case Variant::QUAT: { - EditorPropertyQuat *ed = memnew(EditorPropertyQuat); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyQuat *editor = memnew(EditorPropertyQuat); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; case Variant::AABB: { - EditorPropertyAABB *ed = memnew(EditorPropertyAABB); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyAABB *editor = memnew(EditorPropertyAABB); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; case Variant::BASIS: { - EditorPropertyBasis *ed = memnew(EditorPropertyBasis); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyBasis *editor = memnew(EditorPropertyBasis); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; case Variant::TRANSFORM: { - EditorPropertyTransform *ed = memnew(EditorPropertyTransform); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyTransform *editor = memnew(EditorPropertyTransform); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; @@ -401,8 +431,9 @@ void EditorPropertyArray::update_property() { } break; case Variant::OBJECT: { - - prop = memnew(EditorPropertyResource); + EditorPropertyResource *editor = memnew(EditorPropertyResource); + editor->setup("Resource"); + prop = editor; } break; case Variant::DICTIONARY: { @@ -713,7 +744,7 @@ void EditorPropertyDictionary::update_property() { page->connect("value_changed", this, "_page_changed"); } else { // Queue childs for deletion, delete immediately might cause errors. - for (size_t i = 1; i < vbox->get_child_count(); i++) { + for (int i = 1; i < vbox->get_child_count(); i++) { vbox->get_child(i)->queue_delete(); } } @@ -768,16 +799,16 @@ void EditorPropertyDictionary::update_property() { } break; case Variant::INT: { - EditorPropertyInteger *ed = memnew(EditorPropertyInteger); - ed->setup(-100000, 100000, true, true); - prop = ed; + EditorPropertyInteger *editor = memnew(EditorPropertyInteger); + editor->setup(-100000, 100000, 1, true, true); + prop = editor; } break; case Variant::REAL: { - EditorPropertyFloat *ed = memnew(EditorPropertyFloat); - ed->setup(-100000, 100000, 0.001, true, false, true, true); - prop = ed; + EditorPropertyFloat *editor = memnew(EditorPropertyFloat); + editor->setup(-100000, 100000, 0.001, true, false, true, true); + prop = editor; } break; case Variant::STRING: { @@ -785,67 +816,66 @@ void EditorPropertyDictionary::update_property() { } break; - // math types - + // math types case Variant::VECTOR2: { - EditorPropertyVector2 *ed = memnew(EditorPropertyVector2); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyVector2 *editor = memnew(EditorPropertyVector2); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; case Variant::RECT2: { - EditorPropertyRect2 *ed = memnew(EditorPropertyRect2); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyRect2 *editor = memnew(EditorPropertyRect2); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; case Variant::VECTOR3: { - EditorPropertyVector3 *ed = memnew(EditorPropertyVector3); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyVector3 *editor = memnew(EditorPropertyVector3); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; case Variant::TRANSFORM2D: { - EditorPropertyTransform2D *ed = memnew(EditorPropertyTransform2D); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyTransform2D *editor = memnew(EditorPropertyTransform2D); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; case Variant::PLANE: { - EditorPropertyPlane *ed = memnew(EditorPropertyPlane); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyPlane *editor = memnew(EditorPropertyPlane); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; case Variant::QUAT: { - EditorPropertyQuat *ed = memnew(EditorPropertyQuat); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyQuat *editor = memnew(EditorPropertyQuat); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; case Variant::AABB: { - EditorPropertyAABB *ed = memnew(EditorPropertyAABB); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyAABB *editor = memnew(EditorPropertyAABB); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; case Variant::BASIS: { - EditorPropertyBasis *ed = memnew(EditorPropertyBasis); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyBasis *editor = memnew(EditorPropertyBasis); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; case Variant::TRANSFORM: { - EditorPropertyTransform *ed = memnew(EditorPropertyTransform); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyTransform *editor = memnew(EditorPropertyTransform); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; @@ -863,8 +893,9 @@ void EditorPropertyDictionary::update_property() { } break; case Variant::OBJECT: { - - prop = memnew(EditorPropertyResource); + EditorPropertyResource *editor = memnew(EditorPropertyResource); + editor->setup("Resource"); + prop = editor; } break; case Variant::DICTIONARY: { @@ -872,39 +903,53 @@ void EditorPropertyDictionary::update_property() { } break; case Variant::ARRAY: { - - prop = memnew(EditorPropertyArray); - + EditorPropertyArray *editor = memnew(EditorPropertyArray); + editor->setup(Variant::ARRAY); + prop = editor; } break; // arrays case Variant::POOL_BYTE_ARRAY: { - prop = memnew(EditorPropertyArray); + EditorPropertyArray *editor = memnew(EditorPropertyArray); + editor->setup(Variant::POOL_BYTE_ARRAY); + prop = editor; } break; case Variant::POOL_INT_ARRAY: { - prop = memnew(EditorPropertyArray); + EditorPropertyArray *editor = memnew(EditorPropertyArray); + editor->setup(Variant::POOL_INT_ARRAY); + prop = editor; } break; case Variant::POOL_REAL_ARRAY: { - prop = memnew(EditorPropertyArray); + EditorPropertyArray *editor = memnew(EditorPropertyArray); + editor->setup(Variant::POOL_REAL_ARRAY); + prop = editor; } break; case Variant::POOL_STRING_ARRAY: { - prop = memnew(EditorPropertyArray); + EditorPropertyArray *editor = memnew(EditorPropertyArray); + editor->setup(Variant::POOL_STRING_ARRAY); + prop = editor; } break; case Variant::POOL_VECTOR2_ARRAY: { - prop = memnew(EditorPropertyArray); + EditorPropertyArray *editor = memnew(EditorPropertyArray); + editor->setup(Variant::POOL_VECTOR2_ARRAY); + prop = editor; } break; case Variant::POOL_VECTOR3_ARRAY: { - prop = memnew(EditorPropertyArray); + EditorPropertyArray *editor = memnew(EditorPropertyArray); + editor->setup(Variant::POOL_VECTOR3_ARRAY); + prop = editor; } break; case Variant::POOL_COLOR_ARRAY: { - prop = memnew(EditorPropertyArray); + EditorPropertyArray *editor = memnew(EditorPropertyArray); + editor->setup(Variant::POOL_COLOR_ARRAY); + prop = editor; } break; default: {} } @@ -924,7 +969,7 @@ void EditorPropertyDictionary::update_property() { pc->add_child(add_vbox); } prop->set_object_and_property(object.ptr(), prop_name); - int change_index; + int change_index = 0; if (i < amount) { String cs = key.get_construct_string(); diff --git a/editor/editor_properties_array_dict.h b/editor/editor_properties_array_dict.h index a8ddb02e9d..d2bd849f30 100644 --- a/editor/editor_properties_array_dict.h +++ b/editor/editor_properties_array_dict.h @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* editor_properties_array_dict.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 EDITOR_PROPERTIES_ARRAY_DICT_H #define EDITOR_PROPERTIES_ARRAY_DICT_H diff --git a/editor/editor_resource_preview.cpp b/editor/editor_resource_preview.cpp index bc56a95b47..310c3b3a52 100644 --- a/editor/editor_resource_preview.cpp +++ b/editor/editor_resource_preview.cpp @@ -30,13 +30,16 @@ #include "editor_resource_preview.h" +#include "core/method_bind_ext.gen.inc" + +#include "core/io/resource_loader.h" +#include "core/io/resource_saver.h" +#include "core/message_queue.h" +#include "core/os/file_access.h" +#include "core/project_settings.h" +#include "editor_node.h" #include "editor_scale.h" #include "editor_settings.h" -#include "io/resource_loader.h" -#include "io/resource_saver.h" -#include "message_queue.h" -#include "os/file_access.h" -#include "project_settings.h" bool EditorResourcePreviewGenerator::handles(const String &p_type) const { @@ -46,32 +49,37 @@ 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) const { + +Ref<Texture> EditorResourcePreviewGenerator::generate(const RES &p_from, const Size2 p_size) const { if (get_script_instance() && get_script_instance()->has_method("generate")) { - return get_script_instance()->call("generate", p_from); + return get_script_instance()->call("generate", p_from, p_size); } ERR_EXPLAIN("EditorResourcePreviewGenerator::generate needs to be overridden"); ERR_FAIL_V(Ref<Texture>()); } -Ref<Texture> EditorResourcePreviewGenerator::generate_from_path(const String &p_path) const { +Ref<Texture> EditorResourcePreviewGenerator::generate_from_path(const String &p_path, const Size2 p_size) const { if (get_script_instance() && get_script_instance()->has_method("generate_from_path")) { - return get_script_instance()->call("generate_from_path", p_path); + return get_script_instance()->call("generate_from_path", p_path, p_size); } RES res = ResourceLoader::load(p_path); if (!res.is_valid()) return res; - return generate(res); + return generate(res, p_size); +} + +bool EditorResourcePreviewGenerator::should_generate_small_preview() const { + return false; } void EditorResourcePreviewGenerator::_bind_methods() { ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "handles", PropertyInfo(Variant::STRING, "type"))); - ClassDB::add_virtual_method(get_class_static(), MethodInfo(CLASS_INFO(Texture), "generate", PropertyInfo(Variant::OBJECT, "from", PROPERTY_HINT_RESOURCE_TYPE, "Resource"))); - ClassDB::add_virtual_method(get_class_static(), MethodInfo(CLASS_INFO(Texture), "generate_from_path", PropertyInfo(Variant::STRING, "path", PROPERTY_HINT_FILE))); + ClassDB::add_virtual_method(get_class_static(), MethodInfo(CLASS_INFO(Texture), "generate", PropertyInfo(Variant::OBJECT, "from", PROPERTY_HINT_RESOURCE_TYPE, "Resource"), PropertyInfo(Variant::VECTOR2, "size"))); + ClassDB::add_virtual_method(get_class_static(), MethodInfo(CLASS_INFO(Texture), "generate_from_path", PropertyInfo(Variant::STRING, "path", PROPERTY_HINT_FILE), PropertyInfo(Variant::VECTOR2, "size"))); } EditorResourcePreviewGenerator::EditorResourcePreviewGenerator() { @@ -85,7 +93,7 @@ void EditorResourcePreview::_thread_func(void *ud) { erp->_thread(); } -void EditorResourcePreview::_preview_ready(const String &p_str, const Ref<Texture> &p_texture, ObjectID id, const StringName &p_func, const Variant &p_ud) { +void EditorResourcePreview::_preview_ready(const String &p_str, const Ref<Texture> &p_texture, const Ref<Texture> &p_small_texture, ObjectID id, const StringName &p_func, const Variant &p_ud) { preview_mutex->lock(); @@ -103,6 +111,7 @@ void EditorResourcePreview::_preview_ready(const String &p_str, const Ref<Textur Item item; item.order = order++; item.preview = p_texture; + item.small_preview = p_small_texture; item.last_hash = hash; item.modified_time = modified_time; @@ -110,11 +119,10 @@ void EditorResourcePreview::_preview_ready(const String &p_str, const Ref<Textur preview_mutex->unlock(); - MessageQueue::get_singleton()->push_call(id, p_func, path, p_texture, p_ud); + MessageQueue::get_singleton()->push_call(id, p_func, path, p_texture, p_small_texture, p_ud); } -Ref<Texture> EditorResourcePreview::_generate_preview(const QueueItem &p_item, const String &cache_base) { - +void EditorResourcePreview::_generate_preview(Ref<ImageTexture> &r_texture, Ref<ImageTexture> &r_small_texture, const QueueItem &p_item, const String &cache_base) { String type; if (p_item.resource.is_valid()) @@ -122,40 +130,59 @@ Ref<Texture> EditorResourcePreview::_generate_preview(const QueueItem &p_item, c else type = ResourceLoader::get_resource_type(p_item.path); - if (type == "") - return Ref<Texture>(); //could not guess type + if (type == "") { + r_texture = Ref<ImageTexture>(); + r_small_texture = Ref<ImageTexture>(); + return; //could not guess type + } - Ref<Texture> generated; + int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size"); + thumbnail_size *= EDSCALE; - for (int i = 0; i < preview_generators.size(); i++) { + r_texture = Ref<ImageTexture>(); + r_small_texture = Ref<ImageTexture>(); + for (int i = 0; i < preview_generators.size(); i++) { if (!preview_generators[i]->handles(type)) continue; + + Ref<Texture> generated; if (p_item.resource.is_valid()) { - generated = preview_generators[i]->generate(p_item.resource); + generated = preview_generators[i]->generate(p_item.resource, Vector2(thumbnail_size, thumbnail_size)); } else { - generated = preview_generators[i]->generate_from_path(p_item.path); + generated = preview_generators[i]->generate_from_path(p_item.path, Vector2(thumbnail_size, thumbnail_size)); } + r_texture = generated; + if (r_texture.is_valid() && preview_generators[i]->should_generate_small_preview()) { + int small_thumbnail_size = EditorNode::get_singleton()->get_theme_base()->get_icon("Object", "EditorIcons")->get_width(); // Kind of a workaround to retreive the default icon size + small_thumbnail_size *= EDSCALE; + + Ref<Image> small_image = r_texture->get_data(); + small_image->resize(small_thumbnail_size, small_thumbnail_size, Image::INTERPOLATE_CUBIC); + r_small_texture.instance(); + r_small_texture->create_from_image(small_image); + } break; } if (!p_item.resource.is_valid()) { // cache the preview in case it's a resource on disk - if (generated.is_valid()) { - int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size"); - thumbnail_size *= EDSCALE; + if (r_texture.is_valid()) { //wow it generated a preview... save cache - ResourceSaver::save(cache_base + ".png", generated); + bool has_small_texture = r_small_texture.is_valid(); + ResourceSaver::save(cache_base + ".png", r_texture); + if (has_small_texture) { + ResourceSaver::save(cache_base + "_small.png", r_small_texture); + } FileAccess *f = FileAccess::open(cache_base + ".txt", FileAccess::WRITE); f->store_line(itos(thumbnail_size)); + f->store_line(itos(has_small_texture)); f->store_line(itos(FileAccess::get_modified_time(p_item.path))); f->store_line(FileAccess::get_md5(p_item.path)); memdelete(f); } } - - return generated; } void EditorResourcePreview::_thread() { @@ -177,7 +204,7 @@ void EditorResourcePreview::_thread() { path += ":" + itos(cache[item.path].last_hash); //keep last hash (see description of what this is in condition below) } - _preview_ready(path, cache[item.path].preview, item.id, item.function, item.userdata); + _preview_ready(path, cache[item.path].preview, cache[item.path].small_preview, item.id, item.function, item.userdata); preview_mutex->unlock(); } else { @@ -185,15 +212,17 @@ void EditorResourcePreview::_thread() { preview_mutex->unlock(); Ref<ImageTexture> texture; + Ref<ImageTexture> small_texture; int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size"); thumbnail_size *= EDSCALE; if (item.resource.is_valid()) { - texture = _generate_preview(item, String()); + _generate_preview(texture, small_texture, item, String()); + //adding hash to the end of path (should be ID:<objid>:<hash>) because of 5 argument limit to call_deferred - _preview_ready(item.path + ":" + itos(item.resource->hash_edited_version()), texture, item.id, item.function, item.userdata); + _preview_ready(item.path + ":" + itos(item.resource->hash_edited_version()), texture, small_texture, item.id, item.function, item.userdata); } else { @@ -207,12 +236,13 @@ void EditorResourcePreview::_thread() { FileAccess *f = FileAccess::open(file, FileAccess::READ); if (!f) { - //generate - texture = _generate_preview(item, cache_base); + // No cache found, generate + _generate_preview(texture, small_texture, item, cache_base); } else { uint64_t modtime = FileAccess::get_modified_time(item.path); int tsize = f->get_line().to_int64(); + bool has_small_texture = f->get_line().to_int(); uint64_t last_modtime = f->get_line().to_int64(); bool cache_valid = true; @@ -236,6 +266,7 @@ void EditorResourcePreview::_thread() { f = FileAccess::open(file, FileAccess::WRITE); f->store_line(itos(modtime)); + f->store_line(itos(has_small_texture)); f->store_line(md5); memdelete(f); } @@ -243,12 +274,12 @@ void EditorResourcePreview::_thread() { memdelete(f); } - //cache_valid = false; - if (cache_valid) { Ref<Image> img; img.instance(); + Ref<Image> small_img; + small_img.instance(); if (img->load(cache_base + ".png") != OK) { cache_valid = false; @@ -256,16 +287,24 @@ void EditorResourcePreview::_thread() { texture.instance(); texture->create_from_image(img, Texture::FLAG_FILTER); + + if (has_small_texture) { + if (small_img->load(cache_base + "_small.png") != OK) { + cache_valid = false; + } else { + small_texture.instance(); + small_texture->create_from_image(small_img, Texture::FLAG_FILTER); + } + } } } if (!cache_valid) { - texture = _generate_preview(item, cache_base); + _generate_preview(texture, small_texture, item, cache_base); } } - - _preview_ready(item.path, texture, item.id, item.function, item.userdata); + _preview_ready(item.path, texture, small_texture, item.id, item.function, item.userdata); } } @@ -287,7 +326,7 @@ void EditorResourcePreview::queue_edited_resource_preview(const Ref<Resource> &p if (cache.has(path_id) && cache[path_id].last_hash == p_res->hash_edited_version()) { cache[path_id].order = order++; - p_receiver->call_deferred(p_receiver_func, path_id, cache[path_id].preview, p_userdata); + p_receiver->call_deferred(p_receiver_func, path_id, cache[path_id].preview, cache[path_id].small_preview, p_userdata); preview_mutex->unlock(); return; } @@ -312,7 +351,7 @@ void EditorResourcePreview::queue_resource_preview(const String &p_path, Object preview_mutex->lock(); if (cache.has(p_path)) { cache[p_path].order = order++; - p_receiver->call_deferred(p_receiver_func, p_path, cache[p_path].preview, p_userdata); + p_receiver->call_deferred(p_receiver_func, p_path, cache[p_path].preview, cache[p_path].small_preview, p_userdata); preview_mutex->unlock(); return; } diff --git a/editor/editor_resource_preview.h b/editor/editor_resource_preview.h index 74841b1a1e..a3417cdf60 100644 --- a/editor/editor_resource_preview.h +++ b/editor/editor_resource_preview.h @@ -31,8 +31,8 @@ #ifndef EDITORRESOURCEPREVIEW_H #define EDITORRESOURCEPREVIEW_H -#include "os/semaphore.h" -#include "os/thread.h" +#include "core/os/semaphore.h" +#include "core/os/thread.h" #include "scene/main/node.h" #include "scene/resources/texture.h" @@ -63,8 +63,10 @@ protected: public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from) const; - virtual Ref<Texture> generate_from_path(const String &p_path) const; + virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const; + virtual Ref<Texture> generate_from_path(const String &p_path, const Size2 p_size) const; + + virtual bool should_generate_small_preview() const; EditorResourcePreviewGenerator(); }; @@ -92,6 +94,7 @@ class EditorResourcePreview : public Node { struct Item { Ref<Texture> preview; + Ref<Texture> small_preview; int order; uint32_t last_hash; uint64_t modified_time; @@ -101,8 +104,8 @@ class EditorResourcePreview : public Node { Map<String, Item> cache; - void _preview_ready(const String &p_str, const Ref<Texture> &p_texture, ObjectID id, const StringName &p_func, const Variant &p_ud); - Ref<Texture> _generate_preview(const QueueItem &p_item, const String &cache_base); + void _preview_ready(const String &p_str, const Ref<Texture> &p_texture, const Ref<Texture> &p_small_texture, ObjectID id, const StringName &p_func, const Variant &p_ud); + void _generate_preview(Ref<ImageTexture> &r_texture, Ref<ImageTexture> &r_small_texture, const QueueItem &p_item, const String &cache_base); static void _thread_func(void *ud); void _thread(); diff --git a/editor/editor_run.cpp b/editor/editor_run.cpp index bbd18306a2..ad5cf49f3f 100644 --- a/editor/editor_run.cpp +++ b/editor/editor_run.cpp @@ -30,8 +30,8 @@ #include "editor_run.h" +#include "core/project_settings.h" #include "editor_settings.h" -#include "project_settings.h" EditorRun::Status EditorRun::get_status() const { @@ -196,8 +196,8 @@ Error EditorRun::run(const String &p_scene, const String p_custom_args, const Li void EditorRun::stop() { if (status != STATUS_STOP && pid != 0) { - const int max_wait_msec = GLOBAL_DEF("editor/stop_max_wait_msec", 10000); - OS::get_singleton()->kill(pid, max_wait_msec); + + OS::get_singleton()->kill(pid); } status = STATUS_STOP; diff --git a/editor/editor_run.h b/editor/editor_run.h index 8da607e6dc..df2324efd7 100644 --- a/editor/editor_run.h +++ b/editor/editor_run.h @@ -31,7 +31,7 @@ #ifndef EDITOR_RUN_H #define EDITOR_RUN_H -#include "os/os.h" +#include "core/os/os.h" #include "scene/main/node.h" class EditorRun { public: diff --git a/editor/editor_run_script.h b/editor/editor_run_script.h index 027fdd428d..892d5151a0 100644 --- a/editor/editor_run_script.h +++ b/editor/editor_run_script.h @@ -31,8 +31,8 @@ #ifndef EDITOR_RUN_SCRIPT_H #define EDITOR_RUN_SCRIPT_H +#include "core/reference.h" #include "editor_plugin.h" -#include "reference.h" class EditorNode; class EditorScript : public Reference { diff --git a/editor/editor_scale.cpp b/editor/editor_scale.cpp index 365ea95e3e..ba1607b408 100644 --- a/editor/editor_scale.cpp +++ b/editor/editor_scale.cpp @@ -30,7 +30,7 @@ #include "editor_scale.h" -#include "os/os.h" +#include "core/os/os.h" static float scale = 1.0; diff --git a/editor/editor_sectioned_inspector.cpp b/editor/editor_sectioned_inspector.cpp index 72050cd79b..9d3ab59116 100644 --- a/editor/editor_sectioned_inspector.cpp +++ b/editor/editor_sectioned_inspector.cpp @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* editor_sectioned_inspector.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 "editor_sectioned_inspector.h" #include "editor_scale.h" class SectionedInspectorFilter : public Object { diff --git a/editor/editor_sectioned_inspector.h b/editor/editor_sectioned_inspector.h index 75b51a1581..30d5cd3038 100644 --- a/editor/editor_sectioned_inspector.h +++ b/editor/editor_sectioned_inspector.h @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* editor_sectioned_inspector.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 EDITOR_SECTIONED_INSPECTOR_H #define EDITOR_SECTIONED_INSPECTOR_H diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 5d3c6dd087..34c273fbae 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -30,6 +30,7 @@ #include "editor_settings.h" +#include "core/io/certs_compressed.gen.h" #include "core/io/compression.h" #include "core/io/config_file.h" #include "core/io/file_access_memory.h" @@ -356,7 +357,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("text_editor/theme/color_theme", "Adaptive"); hints["text_editor/theme/color_theme"] = PropertyInfo(Variant::STRING, "text_editor/theme/color_theme", PROPERTY_HINT_ENUM, "Adaptive,Default,Custom"); - _initial_set("text_editor/theme/line_spacing", 4); + _initial_set("text_editor/theme/line_spacing", 6); _initial_set("text_editor/theme/selection_color", Color::html("40808080")); _load_default_text_editor_theme(); @@ -409,7 +410,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("text_editor/completion/add_type_hints", false); _initial_set("docks/scene_tree/start_create_dialog_fully_expanded", false); - _initial_set("docks/scene_tree/draw_relationship_lines", false); + _initial_set("docks/scene_tree/draw_relationship_lines", true); _initial_set("docks/scene_tree/relationship_line_color", Color::html("464646")); _initial_set("editors/grid_map/pick_distance", 5000.0); @@ -484,6 +485,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("editors/2d/bone_outline_color", Color(0.35, 0.35, 0.35)); _initial_set("editors/2d/bone_outline_size", 2); _initial_set("editors/2d/keep_margins_when_changing_anchors", false); + _initial_set("editors/2d/viewport_border_color", Color(0.4, 0.4, 1.0, 0.4)); _initial_set("editors/2d/warped_mouse_panning", true); _initial_set("editors/2d/simple_spacebar_panning", false); _initial_set("editors/2d/scroll_to_pan", false); @@ -510,17 +512,16 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("filesystem/file_dialog/show_hidden_files", false); _initial_set("filesystem/file_dialog/display_mode", 0); hints["filesystem/file_dialog/display_mode"] = PropertyInfo(Variant::INT, "filesystem/file_dialog/display_mode", PROPERTY_HINT_ENUM, "Thumbnails,List"); + _initial_set("filesystem/file_dialog/thumbnail_size", 64); hints["filesystem/file_dialog/thumbnail_size"] = PropertyInfo(Variant::INT, "filesystem/file_dialog/thumbnail_size", PROPERTY_HINT_RANGE, "32,128,16"); - _initial_set("docks/filesystem/disable_split", false); - _initial_set("docks/filesystem/split_mode_minimum_height", 600); _initial_set("docks/filesystem/display_mode", 0); - hints["docks/filesystem/display_mode"] = PropertyInfo(Variant::INT, "docks/filesystem/display_mode", PROPERTY_HINT_ENUM, "Thumbnails,List"); + hints["docks/filesystem/display_mode"] = PropertyInfo(Variant::INT, "docks/filesystem/display_mode", PROPERTY_HINT_ENUM, "Tree only, Split"); _initial_set("docks/filesystem/thumbnail_size", 64); hints["docks/filesystem/thumbnail_size"] = PropertyInfo(Variant::INT, "docks/filesystem/thumbnail_size", PROPERTY_HINT_RANGE, "32,128,16"); - _initial_set("docks/filesystem/display_mode", 0); - hints["docks/filesystem/display_mode"] = PropertyInfo(Variant::INT, "docks/filesystem/display_mode", PROPERTY_HINT_ENUM, "Thumbnails,List"); + _initial_set("docks/filesystem/files_display_mode", 0); + hints["docks/filesystem/files_display_mode"] = PropertyInfo(Variant::INT, "docks/filesystem/files_display_mode", PROPERTY_HINT_ENUM, "Thumbnails,List"); _initial_set("docks/filesystem/always_show_folders", true); _initial_set("editors/animation/autorename_animation_tracks", true); @@ -530,7 +531,6 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("docks/property_editor/texture_preview_width", 48); _initial_set("docks/property_editor/auto_refresh_interval", 0.3); - _initial_set("text_editor/help/doc_path", ""); _initial_set("text_editor/help/show_help_index", true); _initial_set("filesystem/import/ask_save_before_reimport", false); @@ -947,6 +947,10 @@ void EditorSettings::setup_network() { _initial_set("network/debug/remote_port", port); add_property_hint(PropertyInfo(Variant::INT, "network/debug/remote_port", PROPERTY_HINT_RANGE, "1,65535,1")); + + // Editor SSL certificates override + _initial_set("network/ssl/editor_ssl_certificates", _SYSTEM_CERTS_PATH); + add_property_hint(PropertyInfo(Variant::STRING, "network/ssl/editor_ssl_certificates", PROPERTY_HINT_GLOBAL_FILE, "*.crt,*.pem")); } void EditorSettings::save() { @@ -1147,20 +1151,20 @@ Variant EditorSettings::get_project_metadata(const String &p_section, const Stri return cf->get_value(p_section, p_key, p_default); } -void EditorSettings::set_favorite_dirs(const Vector<String> &p_favorites_dirs) { +void EditorSettings::set_favorites(const Vector<String> &p_favorites) { - favorite_dirs = p_favorites_dirs; - FileAccess *f = FileAccess::open(get_project_settings_dir().plus_file("favorite_dirs"), FileAccess::WRITE); + favorites = p_favorites; + FileAccess *f = FileAccess::open(get_project_settings_dir().plus_file("favorites"), FileAccess::WRITE); if (f) { - for (int i = 0; i < favorite_dirs.size(); i++) - f->store_line(favorite_dirs[i]); + for (int i = 0; i < favorites.size(); i++) + f->store_line(favorites[i]); memdelete(f); } } -Vector<String> EditorSettings::get_favorite_dirs() const { +Vector<String> EditorSettings::get_favorites() const { - return favorite_dirs; + return favorites; } void EditorSettings::set_recent_dirs(const Vector<String> &p_recent_dirs) { @@ -1181,11 +1185,11 @@ Vector<String> EditorSettings::get_recent_dirs() const { void EditorSettings::load_favorites() { - FileAccess *f = FileAccess::open(get_project_settings_dir().plus_file("favorite_dirs"), FileAccess::READ); + FileAccess *f = FileAccess::open(get_project_settings_dir().plus_file("favorites"), FileAccess::READ); if (f) { String line = f->get_line().strip_edges(); while (line != "") { - favorite_dirs.push_back(line); + favorites.push_back(line); line = f->get_line().strip_edges(); } memdelete(f); @@ -1212,18 +1216,25 @@ bool EditorSettings::is_dark_theme() { void EditorSettings::list_text_editor_themes() { String themes = "Adaptive,Default,Custom"; + DirAccess *d = DirAccess::open(get_text_editor_themes_dir()); if (d) { + List<String> custom_themes; d->list_dir_begin(); String file = d->get_next(); while (file != String()) { if (file.get_extension() == "tet" && file.get_basename().to_lower() != "default" && file.get_basename().to_lower() != "adaptive" && file.get_basename().to_lower() != "custom") { - themes += "," + file.get_basename(); + custom_themes.push_back(file.get_basename()); } file = d->get_next(); } d->list_dir_end(); memdelete(d); + + custom_themes.sort(); + for (List<String>::Element *E = custom_themes.front(); E; E = E->next()) { + themes += "," + E->get(); + } } add_property_hint(PropertyInfo(Variant::STRING, "text_editor/theme/color_theme", PROPERTY_HINT_ENUM, themes)); } @@ -1466,8 +1477,8 @@ void EditorSettings::_bind_methods() { ClassDB::bind_method(D_METHOD("set_project_metadata", "section", "key", "data"), &EditorSettings::set_project_metadata); ClassDB::bind_method(D_METHOD("get_project_metadata", "section", "key", "default"), &EditorSettings::get_project_metadata, DEFVAL(Variant())); - ClassDB::bind_method(D_METHOD("set_favorite_dirs", "dirs"), &EditorSettings::set_favorite_dirs); - ClassDB::bind_method(D_METHOD("get_favorite_dirs"), &EditorSettings::get_favorite_dirs); + ClassDB::bind_method(D_METHOD("set_favorites", "dirs"), &EditorSettings::set_favorites); + ClassDB::bind_method(D_METHOD("get_favorites"), &EditorSettings::get_favorites); ClassDB::bind_method(D_METHOD("set_recent_dirs", "dirs"), &EditorSettings::set_recent_dirs); ClassDB::bind_method(D_METHOD("get_recent_dirs"), &EditorSettings::get_recent_dirs); diff --git a/editor/editor_settings.h b/editor/editor_settings.h index e5b61abf54..7b0de9617c 100644 --- a/editor/editor_settings.h +++ b/editor/editor_settings.h @@ -31,13 +31,13 @@ #ifndef EDITOR_SETTINGS_H #define EDITOR_SETTINGS_H -#include "object.h" +#include "core/object.h" #include "core/io/config_file.h" -#include "os/thread_safe.h" -#include "resource.h" +#include "core/os/thread_safe.h" +#include "core/resource.h" +#include "core/translation.h" #include "scene/gui/shortcut.h" -#include "translation.h" class EditorPlugin; @@ -107,7 +107,7 @@ private: String config_file_path; String project_config_dir; - Vector<String> favorite_dirs; + Vector<String> favorites; Vector<String> recent_dirs; bool save_changed_setting; @@ -173,8 +173,8 @@ public: void set_project_metadata(const String &p_section, const String &p_key, Variant p_data); Variant get_project_metadata(const String &p_section, const String &p_key, Variant p_default) const; - void set_favorite_dirs(const Vector<String> &p_favorites_dirs); - Vector<String> get_favorite_dirs() const; + void set_favorites(const Vector<String> &p_favorites); + Vector<String> get_favorites() const; void set_recent_dirs(const Vector<String> &p_recent_dirs); Vector<String> get_recent_dirs() const; void load_favorites(); diff --git a/editor/editor_spin_slider.cpp b/editor/editor_spin_slider.cpp index b2c9f9865a..b6e4375ce9 100644 --- a/editor/editor_spin_slider.cpp +++ b/editor/editor_spin_slider.cpp @@ -29,9 +29,9 @@ /*************************************************************************/ #include "editor_spin_slider.h" +#include "core/math/expression.h" +#include "core/os/input.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()); @@ -274,7 +274,7 @@ void EditorSpinSlider::_notification(int p_what) { update(); } if (p_what == NOTIFICATION_FOCUS_ENTER) { - /* Sorry, I dont like this, it makes navigating the different fields with arrows more difficult. + /* Sorry, I don't 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) { _focus_entered(); diff --git a/editor/editor_sub_scene.cpp b/editor/editor_sub_scene.cpp index 056ee59860..7036a0ddaa 100644 --- a/editor/editor_sub_scene.cpp +++ b/editor/editor_sub_scene.cpp @@ -30,6 +30,7 @@ #include "editor_sub_scene.h" +#include "editor/editor_node.h" #include "scene/gui/margin_container.h" #include "scene/resources/packed_scene.h" @@ -84,9 +85,7 @@ void EditorSubScene::_fill_tree(Node *p_node, TreeItem *p_parent) { it->set_text(0, p_node->get_name()); it->set_editable(0, false); it->set_selectable(0, true); - if (has_icon(p_node->get_class(), "EditorIcons")) { - it->set_icon(0, get_icon(p_node->get_class(), "EditorIcons")); - } + it->set_icon(0, EditorNode::get_singleton()->get_object_icon(p_node, "Node")); for (int i = 0; i < p_node->get_child_count(); i++) { diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 7ed7b920d9..36053d7534 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -36,7 +36,6 @@ #include "editor_scale.h" #include "editor_settings.h" #include "modules/svg/image_loader_svg.h" -#include "time.h" static Ref<StyleBoxTexture> make_stylebox(Ref<Texture> p_texture, float p_left, float p_top, float p_right, float p_botton, float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_botton = -1, bool p_draw_center = true) { Ref<StyleBoxTexture> style(memnew(StyleBoxTexture)); @@ -82,12 +81,6 @@ static Ref<StyleBoxLine> make_line_stylebox(Color p_color, int p_thickness = 1, return style; } -static Ref<StyleBoxFlat> change_border_color(Ref<StyleBoxFlat> p_style, Color p_color) { - Ref<StyleBoxFlat> style = p_style->duplicate(); - style->set_border_color_all(p_color); - return style; -} - Ref<ImageTexture> editor_generate_icon(int p_index, bool p_convert_color, float p_scale = EDSCALE, bool p_force_filter = false) { Ref<ImageTexture> icon = memnew(ImageTexture); @@ -199,8 +192,6 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme = exceptions.push_back("StatusWarning"); exceptions.push_back("NodeWarning"); - clock_t begin_time = clock(); - ImageLoaderSVG::set_convert_colors(&dark_icon_color_dictionary); // generate icons @@ -235,8 +226,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(); #else print_line("SVG support disabled, editor icons won't be rendered."); #endif @@ -260,11 +249,9 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { bool use_gn_headers = EDITOR_DEF("interface/theme/use_graph_node_headers", false); - Color script_bg_color = EDITOR_DEF("text_editor/highlighting/background_color", Color(0, 0, 0, 0)); - Color preset_accent_color; Color preset_base_color; - float preset_contrast; + float preset_contrast = 0; // Please, use alphabet order if you've added new theme here(After "Default" and "Custom") @@ -389,7 +376,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { //Register icons + font // the resolution and the icon color (dark_theme bool) has not changed, so we do not regenerate the icons - if (p_theme != NULL && fabs(p_theme->get_constant("scale", "Editor") - EDSCALE) < 0.00001 && p_theme->get_constant("dark_theme", "Editor") == dark_theme) { + if (p_theme != NULL && fabs(p_theme->get_constant("scale", "Editor") - EDSCALE) < 0.00001 && (bool)p_theme->get_constant("dark_theme", "Editor") == dark_theme) { // register already generated icons for (int i = 0; i < editor_icons_count; i++) { theme->set_icon(editor_icons_names[i], "EditorIcons", p_theme->get_icon(editor_icons_names[i], "EditorIcons")); @@ -491,8 +478,8 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { style_tab_selected->set_bg_color(tab_color); Ref<StyleBoxFlat> style_tab_unselected = style_tab_selected->duplicate(); - style_tab_unselected->set_draw_center(false); - style_tab_unselected->set_border_width_all(0); + style_tab_unselected->set_bg_color(dark_color_1); + style_tab_unselected->set_border_color_all(dark_color_2); // Editor background theme->set_stylebox("Background", "EditorStyles", make_flat_stylebox(background_color, default_margin_size, default_margin_size, default_margin_size, default_margin_size)); @@ -648,7 +635,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_icon("visibility_hidden", "PopupMenu", theme->get_icon("GuiVisibilityHidden", "EditorIcons")); theme->set_icon("visibility_visible", "PopupMenu", theme->get_icon("GuiVisibilityVisible", "EditorIcons")); theme->set_icon("visibility_xray", "PopupMenu", theme->get_icon("GuiVisibilityXray", "EditorIcons")); - theme->set_constant("vseparation", "PopupMenu", (extra_spacing + default_margin_size) * EDSCALE); + theme->set_constant("vseparation", "PopupMenu", (extra_spacing + default_margin_size + 1) * EDSCALE); Ref<StyleBoxFlat> sub_inspector_bg = make_flat_stylebox(dark_color_1, 2, 0, 0, 0); sub_inspector_bg->set_border_width(MARGIN_LEFT, 2); @@ -747,10 +734,10 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_color("font_color", "ItemList", font_color); theme->set_color("font_color_selected", "ItemList", mono_color); theme->set_color("guide_color", "ItemList", guide_color); - theme->set_constant("vseparation", "ItemList", 2 * EDSCALE); - theme->set_constant("hseparation", "ItemList", 2 * EDSCALE); + theme->set_constant("vseparation", "ItemList", 3 * EDSCALE); + theme->set_constant("hseparation", "ItemList", 3 * EDSCALE); theme->set_constant("icon_margin", "ItemList", default_margin_size * EDSCALE); - theme->set_constant("line_separation", "ItemList", 2 * EDSCALE); + theme->set_constant("line_separation", "ItemList", 3 * EDSCALE); // Tabs & TabContainer theme->set_stylebox("tab_fg", "TabContainer", style_tab_selected); @@ -945,6 +932,11 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { // TooltipPanel Ref<StyleBoxFlat> style_tooltip = style_popup->duplicate(); + float v = MAX(border_size * EDSCALE, 1.0); + style_tooltip->set_default_margin(MARGIN_LEFT, v); + style_tooltip->set_default_margin(MARGIN_TOP, v); + style_tooltip->set_default_margin(MARGIN_RIGHT, v); + style_tooltip->set_default_margin(MARGIN_BOTTOM, v); style_tooltip->set_bg_color(Color(mono_color.r, mono_color.g, mono_color.b, 0.9)); style_tooltip->set_border_width_all(border_width); style_tooltip->set_border_color_all(mono_color); @@ -1024,6 +1016,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_constant("port_offset", "GraphNode", 14 * EDSCALE); theme->set_constant("title_h_offset", "GraphNode", -16 * EDSCALE); + theme->set_constant("title_offset", "GraphNode", 20 * EDSCALE); theme->set_constant("close_h_offset", "GraphNode", 20 * EDSCALE); theme->set_constant("close_offset", "GraphNode", 20 * EDSCALE); theme->set_constant("separation", "GraphNode", 1 * EDSCALE); @@ -1064,8 +1057,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { const float mono_value = mono_color.r; const Color alpha1 = Color(mono_value, mono_value, mono_value, 0.07); const Color alpha2 = Color(mono_value, mono_value, mono_value, 0.14); - const Color alpha3 = Color(mono_value, mono_value, mono_value, 0.5); - const Color alpha4 = Color(mono_value, mono_value, mono_value, 0.7); + const Color alpha3 = Color(mono_value, mono_value, mono_value, 0.7); // editor main color const Color main_color = Color::html(dark_theme ? "#57b3ff" : "#0480ff"); @@ -1099,9 +1091,9 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { const Color member_variable_color = main_color.linear_interpolate(mono_color, 0.6); const Color mark_color = Color(error_color.r, error_color.g, error_color.b, 0.3); const Color breakpoint_color = error_color; - const Color code_folding_color = alpha4; + const Color code_folding_color = alpha3; const Color search_result_color = alpha1; - const Color search_result_border_color = alpha4; + const Color search_result_border_color = alpha3; EditorSettings *setting = EditorSettings::get_singleton(); String text_editor_color_theme = setting->get("text_editor/theme/color_theme"); diff --git a/editor/export_template_manager.cpp b/editor/export_template_manager.cpp index 6c9d1568fa..52611367f6 100644 --- a/editor/export_template_manager.cpp +++ b/editor/export_template_manager.cpp @@ -30,14 +30,14 @@ #include "export_template_manager.h" +#include "core/io/json.h" +#include "core/io/zip_io.h" +#include "core/os/dir_access.h" #include "core/os/input.h" #include "core/os/keyboard.h" +#include "core/version.h" #include "editor_node.h" #include "editor_scale.h" -#include "io/json.h" -#include "io/zip_io.h" -#include "os/dir_access.h" -#include "version.h" void ExportTemplateManager::_update_template_list() { diff --git a/editor/file_type_cache.cpp b/editor/file_type_cache.cpp index 02e647b733..0e328247ac 100644 --- a/editor/file_type_cache.cpp +++ b/editor/file_type_cache.cpp @@ -30,8 +30,8 @@ #include "file_type_cache.h" -#include "os/file_access.h" -#include "project_settings.h" +#include "core/os/file_access.h" +#include "core/project_settings.h" FileTypeCache *FileTypeCache::singleton = NULL; diff --git a/editor/file_type_cache.h b/editor/file_type_cache.h index 33b50cb1c4..aa2d78f763 100644 --- a/editor/file_type_cache.h +++ b/editor/file_type_cache.h @@ -31,7 +31,7 @@ #ifndef FILE_TYPE_CACHE_H #define FILE_TYPE_CACHE_H -#include "object.h" +#include "core/object.h" class FileTypeCache : Object { diff --git a/editor/fileserver/SCsub b/editor/fileserver/SCsub index f1fa50148f..2b1e889fb0 100644 --- a/editor/fileserver/SCsub +++ b/editor/fileserver/SCsub @@ -1,5 +1,5 @@ #!/usr/bin/env python Import('env') -Export('env') + env.add_source_files(env.editor_sources, "*.cpp") diff --git a/editor/fileserver/editor_file_server.cpp b/editor/fileserver/editor_file_server.cpp index 28b1095256..27fe716855 100644 --- a/editor/fileserver/editor_file_server.cpp +++ b/editor/fileserver/editor_file_server.cpp @@ -31,7 +31,7 @@ #include "editor_file_server.h" #include "../editor_settings.h" -#include "io/marshalls.h" +#include "core/io/marshalls.h" //#define DEBUG_PRINT(m_p) print_line(m_p) //#define DEBUG_TIME(m_what) printf("MS: %s - %lu\n", m_what, OS::get_singleton()->get_ticks_usec()); @@ -330,7 +330,7 @@ void EditorFileServer::stop() { EditorFileServer::EditorFileServer() { - server = TCP_Server::create_ref(); + server.instance(); wait_mutex = Mutex::create(); quit = false; active = false; diff --git a/editor/fileserver/editor_file_server.h b/editor/fileserver/editor_file_server.h index d73c78ee70..8e32c3c82f 100644 --- a/editor/fileserver/editor_file_server.h +++ b/editor/fileserver/editor_file_server.h @@ -31,11 +31,11 @@ #ifndef EDITOR_FILE_SERVER_H #define EDITOR_FILE_SERVER_H -#include "io/file_access_network.h" -#include "io/packet_peer.h" -#include "io/tcp_server.h" -#include "object.h" -#include "os/thread.h" +#include "core/io/file_access_network.h" +#include "core/io/packet_peer.h" +#include "core/io/tcp_server.h" +#include "core/object.h" +#include "core/os/thread.h" class EditorFileServer : public Object { diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index cb38c2f85e..828e608fa4 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -30,62 +30,113 @@ #include "filesystem_dock.h" +#include "core/io/resource_loader.h" +#include "core/os/dir_access.h" +#include "core/os/file_access.h" #include "core/os/keyboard.h" +#include "core/os/os.h" +#include "core/project_settings.h" #include "editor_node.h" #include "editor_settings.h" -#include "io/resource_loader.h" -#include "os/dir_access.h" -#include "os/file_access.h" -#include "os/os.h" -#include "project_settings.h" #include "scene/main/viewport.h" +Ref<Texture> FileSystemDock::_get_tree_item_icon(EditorFileSystemDirectory *p_dir, int p_idx) { + Ref<Texture> file_icon; + if (!p_dir->get_file_import_is_valid(p_idx)) { + file_icon = get_icon("ImportFail", "EditorIcons"); + } else { + String file_type = p_dir->get_file_type(p_idx); + file_icon = (has_icon(file_type, "EditorIcons")) ? get_icon(file_type, "EditorIcons") : get_icon("File", "EditorIcons"); + } + return file_icon; +} + bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory *p_dir, Vector<String> &uncollapsed_paths) { - TreeItem *item = tree->create_item(p_parent); + bool parent_should_expand = false; + + // Create a tree item for the subdirectory + TreeItem *subdirectory_item = tree->create_item(p_parent); String dname = p_dir->get_name(); if (dname == "") dname = "res://"; - item->set_text(0, dname); - item->set_icon(0, get_icon("Folder", "EditorIcons")); - item->set_selectable(0, true); + subdirectory_item->set_text(0, dname); + subdirectory_item->set_icon(0, get_icon("Folder", "EditorIcons")); + subdirectory_item->set_selectable(0, true); String lpath = p_dir->get_path(); - if (lpath != "res://" && lpath.ends_with("/")) { - lpath = lpath.substr(0, lpath.length() - 1); - } - item->set_metadata(0, lpath); - if (lpath == path) { - item->select(0); + subdirectory_item->set_metadata(0, lpath); + if (path == lpath || ((display_mode_setting == DISPLAY_MODE_SETTING_SPLIT) && path.get_base_dir() == lpath)) { + subdirectory_item->select(0); } if ((path.begins_with(lpath) && path != lpath)) { - item->set_collapsed(false); + subdirectory_item->set_collapsed(false); } else { - bool is_collapsed = true; - for (int i = 0; i < uncollapsed_paths.size(); i++) { - if (lpath == uncollapsed_paths[i]) { - is_collapsed = false; - break; + subdirectory_item->set_collapsed(uncollapsed_paths.find(lpath) < 0); + } + if (searched_string.length() > 0 && dname.to_lower().find(searched_string) >= 0) { + parent_should_expand = true; + } + + // Create items for all subdirectories + for (int i = 0; i < p_dir->get_subdir_count(); i++) + parent_should_expand = (_create_tree(subdirectory_item, p_dir->get_subdir(i), uncollapsed_paths) || parent_should_expand); + + // Create all items for the files in the subdirectory + if (display_mode_setting == DISPLAY_MODE_SETTING_TREE_ONLY) { + for (int i = 0; i < p_dir->get_file_count(); i++) { + String file_name = p_dir->get_file(i); + + if (searched_string.length() > 0) { + if (file_name.to_lower().find(searched_string) < 0) { + // The seached string is not in the file name, we skip it + continue; + } else { + // We expand all parents + parent_should_expand = true; + } + } + + TreeItem *file_item = tree->create_item(subdirectory_item); + file_item->set_text(0, file_name); + file_item->set_icon(0, _get_tree_item_icon(p_dir, i)); + String file_metadata = lpath.plus_file(file_name); + file_item->set_metadata(0, file_metadata); + if (path == file_metadata) { + file_item->select(0); + file_item->set_as_cursor(0); } + Array udata; + udata.push_back(tree_update_id); + udata.push_back(file_item); + EditorResourcePreview::get_singleton()->queue_resource_preview(file_metadata, this, "_tree_thumbnail_done", udata); } - item->set_collapsed(is_collapsed); } - for (int i = 0; i < p_dir->get_subdir_count(); i++) - _create_tree(item, p_dir->get_subdir(i), uncollapsed_paths); + if (searched_string.length() > 0) { + if (parent_should_expand) { + subdirectory_item->set_collapsed(false); + } else if (dname != "res://") { + subdirectory_item->get_parent()->remove_child(subdirectory_item); + } + } - return true; + return parent_should_expand; } -void FileSystemDock::_update_tree(bool keep_collapse_state, bool p_uncollapse_root) { - +Vector<String> FileSystemDock::_compute_uncollapsed_paths() { + // Register currently collapsed paths Vector<String> uncollapsed_paths; - if (keep_collapse_state) { - TreeItem *root = tree->get_root(); - if (root) { - TreeItem *resTree = root->get_children()->get_next(); + TreeItem *root = tree->get_root(); + if (root) { + TreeItem *favorites_item = root->get_children(); + if (!favorites_item->is_collapsed()) { + uncollapsed_paths.push_back(favorites_item->get_metadata(0)); + } + TreeItem *resTree = root->get_children()->get_next(); + if (resTree) { Vector<TreeItem *> needs_check; needs_check.push_back(resTree); @@ -102,84 +153,111 @@ void FileSystemDock::_update_tree(bool keep_collapse_state, bool p_uncollapse_ro } } } + return uncollapsed_paths; +} + +void FileSystemDock::_update_tree(const Vector<String> p_uncollapsed_paths, bool p_uncollapse_root) { + // Recreate the tree tree->clear(); + tree_update_id++; updating_tree = true; - TreeItem *root = tree->create_item(); + + // Handles the favorites TreeItem *favorites = tree->create_item(root); favorites->set_icon(0, get_icon("Favorites", "EditorIcons")); favorites->set_text(0, TTR("Favorites:")); - favorites->set_selectable(0, false); + favorites->set_metadata(0, "Favorites"); + favorites->set_collapsed(p_uncollapsed_paths.find("Favorites") < 0); - Vector<String> favorite_paths = EditorSettings::get_singleton()->get_favorite_dirs(); - String res_path = "res://"; - Ref<Texture> folder_icon = get_icon("Folder", "EditorIcons"); + Vector<String> favorite_paths = EditorSettings::get_singleton()->get_favorites(); for (int i = 0; i < favorite_paths.size(); i++) { String fave = favorite_paths[i]; - if (!fave.begins_with(res_path)) + if (!fave.begins_with("res://")) continue; - TreeItem *ti = tree->create_item(favorites); - if (fave == res_path) - ti->set_text(0, "/"); - else - ti->set_text(0, fave.get_file()); - ti->set_icon(0, folder_icon); - ti->set_selectable(0, true); - ti->set_metadata(0, fave); + Ref<Texture> folder_icon = get_icon("Folder", "EditorIcons"); + + String text; + Ref<Texture> icon; + if (fave == "res://") { + text = "/"; + icon = folder_icon; + } else if (fave.ends_with("/")) { + text = fave.substr(0, fave.length() - 1).get_file(); + icon = folder_icon; + } else { + text = fave.get_file(); + int index; + EditorFileSystemDirectory *dir = EditorFileSystem::get_singleton()->find_file(fave, &index); + if (dir) { + icon = _get_tree_item_icon(dir, index); + } else { + icon = get_icon("File", "EditorIcons"); + } + } + + if (searched_string.length() == 0 || text.to_lower().find(searched_string) >= 0) { + TreeItem *ti = tree->create_item(favorites); + ti->set_text(0, text); + ti->set_icon(0, icon); + ti->set_tooltip(0, fave); + ti->set_selectable(0, true); + ti->set_metadata(0, fave); + if (!fave.ends_with("/")) { + Array udata; + udata.push_back(tree_update_id); + udata.push_back(ti); + EditorResourcePreview::get_singleton()->queue_resource_preview(fave, this, "_tree_thumbnail_done", udata); + } + } } + Vector<String> uncollapsed_paths = p_uncollapsed_paths; if (p_uncollapse_root) { uncollapsed_paths.push_back("res://"); } + // Create the remaining of the tree _create_tree(root, EditorFileSystem::get_singleton()->get_filesystem(), uncollapsed_paths); tree->ensure_cursor_is_visible(); updating_tree = false; } -void FileSystemDock::_update_display_mode() { - - bool disable_split = bool(EditorSettings::get_singleton()->get("docks/filesystem/disable_split")); - bool compact_mode = get_size().height < int(EditorSettings::get_singleton()->get("docks/filesystem/split_mode_minimum_height")); - DisplayMode new_mode; - if (disable_split || compact_mode) { - new_mode = file_list_view ? DISPLAY_FILE_LIST_ONLY : DISPLAY_TREE_ONLY; - } else { - new_mode = DISPLAY_SPLIT; - } +void FileSystemDock::_update_display_mode(bool p_force) { + // Compute the new display mode + DisplayMode new_display_mode = (display_mode_setting == DISPLAY_MODE_SETTING_TREE_ONLY) ? DISPLAY_MODE_TREE_ONLY : DISPLAY_MODE_SPLIT; - if (new_mode != display_mode) { - switch (new_mode) { - case DISPLAY_TREE_ONLY: + if (p_force || new_display_mode != display_mode || old_display_mode_setting != display_mode_setting) { + display_mode = new_display_mode; + old_display_mode_setting = display_mode_setting; + button_toggle_display_mode->set_pressed(display_mode_setting == DISPLAY_MODE_SETTING_SPLIT ? true : false); + switch (display_mode) { + case DISPLAY_MODE_TREE_ONLY: tree->show(); tree->set_v_size_flags(SIZE_EXPAND_FILL); - _update_tree(true); + if (display_mode_setting == DISPLAY_MODE_SETTING_TREE_ONLY) { + tree_search_box->show(); + } else { + tree_search_box->hide(); + } + _update_tree(_compute_uncollapsed_paths()); file_list_vb->hide(); break; - case DISPLAY_FILE_LIST_ONLY: - tree->hide(); - button_tree->show(); - - file_list_vb->show(); - _update_files(true); - break; - - case DISPLAY_SPLIT: + case DISPLAY_MODE_SPLIT: tree->show(); tree->set_v_size_flags(SIZE_EXPAND_FILL); - button_tree->hide(); tree->ensure_cursor_is_visible(); - _update_tree(true); + tree_search_box->hide(); + _update_tree(_compute_uncollapsed_paths()); file_list_vb->show(); - _update_files(true); + _update_file_list(true); break; } - display_mode = new_mode; } } @@ -201,34 +279,34 @@ void FileSystemDock::_notification(int p_what) { String ei = "EditorIcons"; button_reload->set_icon(get_icon("Reload", ei)); - button_favorite->set_icon(get_icon("Favorites", ei)); - //button_instance->set_icon(get_icon("Add", ei)); - //button_open->set_icon(get_icon("Folder", ei)); - button_tree->set_icon(get_icon("Filesystem", ei)); + button_toggle_display_mode->set_icon(get_icon("Panels2", ei)); _update_file_list_display_mode_button(); button_file_list_display_mode->connect("pressed", this, "_change_file_display"); - //file_options->set_icon( get_icon("Tools","ei")); - files->connect("item_activated", this, "_select_file"); + + files->connect("item_activated", this, "_file_list_activate_file"); button_hist_next->connect("pressed", this, "_fw_history"); button_hist_prev->connect("pressed", this, "_bw_history"); - search_box->set_right_icon(get_icon("Search", ei)); - search_box->set_clear_button_enabled(true); + tree_search_box->set_right_icon(get_icon("Search", ei)); + tree_search_box->set_clear_button_enabled(true); + file_list_search_box->set_right_icon(get_icon("Search", ei)); + file_list_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)); - button_show->set_icon(get_icon("GuiVisibilityVisible", "EditorIcons")); - file_options->connect("id_pressed", this, "_file_option"); - folder_options->connect("id_pressed", this, "_folder_option"); + file_list_popup->connect("id_pressed", this, "_file_list_rmb_option"); + tree_popup->connect("id_pressed", this, "_tree_rmb_option"); - button_tree->connect("pressed", this, "_go_to_tree", varray(), CONNECT_DEFERRED); current_path->connect("text_entered", this, "navigate_to_path"); + display_mode_setting = DisplayModeSetting(int(EditorSettings::get_singleton()->get("docks/filesystem/display_mode"))); + always_show_folders = bool(EditorSettings::get_singleton()->get("docks/filesystem/always_show_folders")); + _update_display_mode(); if (EditorFileSystem::get_singleton()->is_scanning()) { _set_scanning_mode(); } else { - _update_tree(false, true); + _update_tree(Vector<String>(), true); } } break; @@ -245,7 +323,7 @@ void FileSystemDock::_notification(int p_what) { Dictionary dd = get_viewport()->gui_get_drag_data(); if (tree->is_visible_in_tree() && dd.has("type")) { if ((String(dd["type"]) == "files") || (String(dd["type"]) == "files_and_dirs") || (String(dd["type"]) == "resource")) { - tree->set_drop_mode_flags(Tree::DROP_MODE_ON_ITEM); + tree->set_drop_mode_flags(Tree::DROP_MODE_ON_ITEM | Tree::DROP_MODE_INBETWEEN); } else if ((String(dd["type"]) == "favorite")) { tree->set_drop_mode_flags(Tree::DROP_MODE_INBETWEEN); } @@ -257,25 +335,51 @@ void FileSystemDock::_notification(int p_what) { tree->set_drop_mode_flags(0); } break; + case NOTIFICATION_THEME_CHANGED: { + if (is_visible_in_tree()) { + _update_display_mode(true); + } + } break; case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { // Update icons String ei = "EditorIcons"; button_reload->set_icon(get_icon("Reload", ei)); - button_favorite->set_icon(get_icon("Favorites", ei)); - button_tree->set_icon(get_icon("Filesystem", ei)); + button_toggle_display_mode->set_icon(get_icon("Panels2", ei)); button_hist_next->set_icon(get_icon("Forward", ei)); button_hist_prev->set_icon(get_icon("Back", ei)); + if (button_file_list_display_mode->is_pressed()) { + button_file_list_display_mode->set_icon(get_icon("FileThumbnail", "EditorIcons")); + } else { + button_file_list_display_mode->set_icon(get_icon("FileList", "EditorIcons")); + } + + tree_search_box->set_right_icon(get_icon("Search", ei)); + tree_search_box->set_clear_button_enabled(true); + file_list_search_box->set_right_icon(get_icon("Search", ei)); + file_list_search_box->set_clear_button_enabled(true); - search_box->set_right_icon(get_icon("Search", ei)); - search_box->set_clear_button_enabled(true); + bool should_update_files = false; - // Change size mode - int new_file_list_mode = int(EditorSettings::get_singleton()->get("docks/filesystem/display_mode")); + // Update file list display mode + int new_file_list_mode = int(EditorSettings::get_singleton()->get("docks/filesystem/files_display_mode")); if (new_file_list_mode != file_list_display_mode) { set_file_list_display_mode(new_file_list_mode); - } else { _update_file_list_display_mode_button(); - _update_files(true); + should_update_files = true; + } + + // Update display of files in tree + display_mode_setting = DisplayModeSetting(int(EditorSettings::get_singleton()->get("docks/filesystem/display_mode"))); + + // Update allways showfolders + bool new_always_show_folders = bool(EditorSettings::get_singleton()->get("docks/filesystem/always_show_folders")); + if (new_always_show_folders != always_show_folders) { + always_show_folders = new_always_show_folders; + should_update_files = true; + } + + if (should_update_files) { + _update_file_list(true); } // Change full tree mode @@ -285,74 +389,44 @@ void FileSystemDock::_notification(int p_what) { } } -void FileSystemDock::_dir_selected() { +void FileSystemDock::_tree_multi_selected(Object *p_item, int p_column, bool p_selected) { + // Update the import dock + import_dock_needs_update = true; + call_deferred("_update_import_dock"); - TreeItem *sel = tree->get_selected(); - if (!sel) + // Return if we don't select something new + if (!p_selected) return; - path = sel->get_metadata(0); - - bool found = false; - Vector<String> favorites = EditorSettings::get_singleton()->get_favorite_dirs(); - for (int i = 0; i < favorites.size(); i++) { - - if (favorites[i] == path) { - found = true; - break; - } - } - - button_favorite->set_pressed(found); - current_path->set_text(path); - _push_to_history(); - - if (display_mode == DISPLAY_SPLIT) { - _update_files(false); - } -} -void FileSystemDock::_favorites_pressed() { - - TreeItem *sel = tree->get_selected(); - if (!sel) + // Tree item selected + TreeItem *selected = tree->get_selected(); + if (!selected) return; - path = sel->get_metadata(0); - - int idx = -1; - Vector<String> favorites = EditorSettings::get_singleton()->get_favorite_dirs(); - for (int i = 0; i < favorites.size(); i++) { - - if (favorites[i] == path) { - idx = i; - break; - } - } - if (idx == -1) { - favorites.push_back(path); + TreeItem *favorites_item = tree->get_root()->get_children(); + if (selected->get_parent() == favorites_item && !String(selected->get_metadata(0)).ends_with("/")) { + // Go to the favorites if we click in the favorites and the path has changed + path = "Favorites"; } else { - favorites.remove(idx); + path = selected->get_metadata(0); + // Note: the "Favorites" item also leads to this path } - EditorSettings::get_singleton()->set_favorite_dirs(favorites); - _update_tree(true); -} -void FileSystemDock::_show_current_scene_file() { + // Set the current path + _set_current_path_text(path); + _push_to_history(); - int index = EditorNode::get_editor_data().get_edited_scene(); - String path = EditorNode::get_editor_data().get_scene_path(index); - if (path != String()) { - navigate_to_path(path); + // Update the file list + if (!updating_tree && display_mode == DISPLAY_MODE_SPLIT) { + _update_file_list(false); } } String FileSystemDock::get_selected_path() const { - - TreeItem *sel = tree->get_selected(); - if (!sel) - return ""; - - return sel->get_metadata(0); + if (path.ends_with("/")) + return path; + else + return path.get_base_dir(); } String FileSystemDock::get_current_path() const { @@ -360,30 +434,44 @@ String FileSystemDock::get_current_path() const { return path; } +void FileSystemDock::_set_current_path_text(const String &p_path) { + if (p_path == "Favorites") { + current_path->set_text(TTR("Favorites")); + } else { + current_path->set_text(path); + } +} + void FileSystemDock::navigate_to_path(const String &p_path) { - // If the path is a file, do not only go to the directory in the tree, also select the file in the file list. - String file_name = ""; - DirAccess *dirAccess = DirAccess::open("res://"); - if (dirAccess->file_exists(p_path)) { - path = p_path.get_base_dir(); - file_name = p_path.get_file(); - } else if (dirAccess->dir_exists(p_path)) { + + if (p_path == "Favorites") { path = p_path; } else { - ERR_EXPLAIN(vformat(TTR("Cannot navigate to '%s' as it has not been found in the file system!"), p_path)); - ERR_FAIL(); + String target_path = p_path; + // If the path is a file, do not only go to the directory in the tree, also select the file in the file list. + if (target_path.ends_with("/")) { + target_path = target_path.substr(0, target_path.length() - 1); + } + DirAccess *dirAccess = DirAccess::open("res://"); + if (dirAccess->file_exists(p_path)) { + path = target_path; + } else if (dirAccess->dir_exists(p_path)) { + path = target_path + "/"; + } else { + ERR_EXPLAIN(vformat(TTR("Cannot navigate to '%s' as it has not been found in the file system!"), p_path)); + ERR_FAIL(); + } } - current_path->set_text(path); + _set_current_path_text(path); _push_to_history(); - if (display_mode == DISPLAY_SPLIT) { - _update_tree(true); - _update_files(false); - } else { - _go_to_file_list(); + _update_tree(_compute_uncollapsed_paths()); + if (display_mode == DISPLAY_MODE_SPLIT) { + _update_file_list(false); } + String file_name = p_path.get_file(); if (!file_name.empty()) { for (int i = 0; i < files->get_item_count(); i++) { if (files->get_item_text(i) == file_name) { @@ -395,15 +483,32 @@ void FileSystemDock::navigate_to_path(const String &p_path) { } } -void FileSystemDock::_thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Variant &p_udata) { +void FileSystemDock::_file_list_thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Ref<Texture> &p_small_preview, const Variant &p_udata) { if ((file_list_vb->is_visible_in_tree() || path == p_path.get_base_dir()) && p_preview.is_valid()) { - Array uarr = p_udata; int idx = uarr[0]; String file = uarr[1]; - if (idx < files->get_item_count() && files->get_item_text(idx) == file && files->get_item_metadata(idx) == p_path) - files->set_item_icon(idx, p_preview); + if (idx < files->get_item_count() && files->get_item_text(idx) == file && files->get_item_metadata(idx) == p_path) { + if (file_list_display_mode == FILE_LIST_DISPLAY_LIST) { + if (p_small_preview.is_valid()) + files->set_item_icon(idx, p_small_preview); + } else { + files->set_item_icon(idx, p_preview); + } + } + } +} + +void FileSystemDock::_tree_thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Ref<Texture> &p_small_preview, const Variant &p_udata) { + if (p_small_preview.is_valid()) { + Array uarr = p_udata; + if (tree_update_id == (int)uarr[0]) { + TreeItem *file_item = Object::cast_to<TreeItem>(uarr[1]); + if (file_item) { + file_item->set_icon(0, p_small_preview); + } + } } } @@ -424,9 +529,9 @@ void FileSystemDock::_change_file_display() { _update_file_list_display_mode_button(); - EditorSettings::get_singleton()->set("docks/filesystem/display_mode", file_list_display_mode); + EditorSettings::get_singleton()->set("docks/filesystem/files_display_mode", file_list_display_mode); - _update_files(true); + _update_file_list(true); } void FileSystemDock::_search(EditorFileSystemDirectory *p_path, List<FileInfo> *matches, int p_max_items) { @@ -438,12 +543,10 @@ void FileSystemDock::_search(EditorFileSystemDirectory *p_path, List<FileInfo> * _search(p_path->get_subdir(i), matches, p_max_items); } - String match = search_box->get_text().to_lower(); - for (int i = 0; i < p_path->get_file_count(); i++) { String file = p_path->get_file(i); - if (file.to_lower().find(match) != -1) { + if (file.to_lower().find(searched_string) != -1) { FileInfo fi; fi.name = file; @@ -459,14 +562,12 @@ void FileSystemDock::_search(EditorFileSystemDirectory *p_path, List<FileInfo> * } } -void FileSystemDock::_update_files(bool p_keep_selection) { +void FileSystemDock::_update_file_list(bool p_keep_selection) { + // Register the previously selected items Set<String> cselection; - if (p_keep_selection) { - for (int i = 0; i < files->get_item_count(); i++) { - if (files->is_selected(i)) cselection.insert(files->get_item_text(i)); } @@ -474,11 +575,10 @@ void FileSystemDock::_update_files(bool p_keep_selection) { files->clear(); - current_path->set_text(path); + _set_current_path_text(path); - EditorFileSystemDirectory *efd = EditorFileSystem::get_singleton()->get_filesystem_path(path); - if (!efd) - return; + String directory = path; + String file = ""; String ei = "EditorIcons"; int thumbnail_size = EditorSettings::get_singleton()->get("docks/filesystem/thumbnail_size"); @@ -487,13 +587,10 @@ void FileSystemDock::_update_files(bool p_keep_selection) { Ref<Texture> file_thumbnail; Ref<Texture> file_thumbnail_broken; - bool always_show_folders = EditorSettings::get_singleton()->get("docks/filesystem/always_show_folders"); - bool use_thumbnails = (file_list_display_mode == FILE_LIST_DISPLAY_THUMBNAILS); - bool use_folders = search_box->get_text().length() == 0 && ((display_mode == DISPLAY_FILE_LIST_ONLY || display_mode == DISPLAY_TREE_ONLY) || always_show_folders); if (use_thumbnails) { - + // Thumbnails mode files->set_max_columns(0); files->set_icon_mode(ItemList::ICON_MODE_TOP); files->set_fixed_column_width(thumbnail_size * 3 / 2); @@ -511,6 +608,7 @@ void FileSystemDock::_update_files(bool p_keep_selection) { } } else { + // No thumbnails files->set_icon_mode(ItemList::ICON_MODE_LEFT); files->set_max_columns(1); files->set_max_text_lines(1); @@ -518,55 +616,117 @@ void FileSystemDock::_update_files(bool p_keep_selection) { files->set_fixed_icon_size(Size2()); } - if (use_folders) { - Ref<Texture> folderIcon = (use_thumbnails) ? folder_thumbnail : get_icon("folder", "FileDialog"); + Ref<Texture> folder_icon = (use_thumbnails) ? folder_thumbnail : get_icon("folder", "FileDialog"); - if (path != "res://") { - files->add_item("..", folderIcon, true); + // Build the FileInfo list + List<FileInfo> filelist; + if (path == "Favorites") { + // Display the favorites + Vector<String> favorites = EditorSettings::get_singleton()->get_favorites(); + for (int i = 0; i < favorites.size(); i++) { + String favorite = favorites[i]; + String text; + Ref<Texture> icon; + if (favorite == "res://") { + text = "/"; + icon = folder_icon; + if (searched_string.length() == 0 || text.to_lower().find(searched_string) >= 0) { + files->add_item(text, icon, true); + files->set_item_metadata(files->get_item_count() - 1, favorite); + } + } else if (favorite.ends_with("/")) { + text = favorite.substr(0, favorite.length() - 1).get_file(); + icon = folder_icon; + if (searched_string.length() == 0 || text.to_lower().find(searched_string) >= 0) { + files->add_item(text, icon, true); + files->set_item_metadata(files->get_item_count() - 1, favorite); + } + } else { + int index; + EditorFileSystemDirectory *efd = EditorFileSystem::get_singleton()->find_file(favorite, &index); + + FileInfo fi; + fi.name = favorite.get_file(); + fi.path = favorite; + if (efd) { + fi.type = efd->get_file_type(index); + fi.import_broken = !efd->get_file_import_is_valid(index); + } else { + fi.type = ""; + fi.import_broken = true; + } + fi.import_status = 0; - String bd = path.get_base_dir(); - if (bd != "res://" && !bd.ends_with("/")) - bd += "/"; + if (searched_string.length() == 0 || fi.name.to_lower().find(searched_string) >= 0) { + filelist.push_back(fi); + } + } + } + } else { - files->set_item_metadata(files->get_item_count() - 1, bd); + // Get infos on the directory + file + if (directory.ends_with("/") && directory != "res://") { + directory = directory.substr(0, directory.length() - 1); + } + EditorFileSystemDirectory *efd = EditorFileSystem::get_singleton()->get_filesystem_path(directory); + if (!efd) { + directory = path.get_base_dir(); + file = path.get_file(); + efd = EditorFileSystem::get_singleton()->get_filesystem_path(directory); } + if (!efd) + return; - for (int i = 0; i < efd->get_subdir_count(); i++) { + if (searched_string.length() > 0) { + // Display the search results + _search(EditorFileSystem::get_singleton()->get_filesystem(), &filelist, 128); + } else { - String dname = efd->get_subdir(i)->get_name(); + if (display_mode == DISPLAY_MODE_TREE_ONLY || always_show_folders) { + // Display folders in the list - files->add_item(dname, folderIcon, true); - files->set_item_metadata(files->get_item_count() - 1, path.plus_file(dname) + "/"); + if (directory != "res://") { + files->add_item("..", folder_icon, true); - if (cselection.has(dname)) - files->select(files->get_item_count() - 1, false); - } - } + String bd = directory.get_base_dir(); + if (bd != "res://" && !bd.ends_with("/")) + bd += "/"; - List<FileInfo> filelist; + files->set_item_metadata(files->get_item_count() - 1, bd); + files->set_item_selectable(files->get_item_count() - 1, false); + } - if (search_box->get_text().length() > 0) { + for (int i = 0; i < efd->get_subdir_count(); i++) { - _search(EditorFileSystem::get_singleton()->get_filesystem(), &filelist, 128); - filelist.sort(); - } else { + String dname = efd->get_subdir(i)->get_name(); - for (int i = 0; i < efd->get_file_count(); i++) { + files->add_item(dname, folder_icon, true); + files->set_item_metadata(files->get_item_count() - 1, directory.plus_file(dname) + "/"); - FileInfo fi; - fi.name = efd->get_file(i); - fi.path = path.plus_file(fi.name); - fi.type = efd->get_file_type(i); - fi.import_broken = !efd->get_file_import_is_valid(i); - fi.import_status = 0; + if (cselection.has(dname)) { + files->select(files->get_item_count() - 1, false); + } + } + } + + // Display the folder content + for (int i = 0; i < efd->get_file_count(); i++) { - filelist.push_back(fi); + FileInfo fi; + fi.name = efd->get_file(i); + fi.path = directory.plus_file(fi.name); + fi.type = efd->get_file_type(i); + fi.import_broken = !efd->get_file_import_is_valid(i); + fi.import_status = 0; + + filelist.push_back(fi); + } } filelist.sort(); } + // Fills the ItemList control node from the FileInfos String oi = "Object"; - for (List<FileInfo>::Element *E = filelist.front(); E; E = E->next()) { FileInfo *finfo = &(E->get()); String fname = finfo->name; @@ -578,6 +738,7 @@ void FileSystemDock::_update_files(bool p_keep_selection) { String tooltip = fname; + // Select the icons if (!finfo->import_broken) { type_icon = (has_icon(ftype, ei)) ? get_icon(ftype, ei) : get_icon(oi, ei); big_icon = file_thumbnail; @@ -587,28 +748,39 @@ void FileSystemDock::_update_files(bool p_keep_selection) { tooltip += "\n" + TTR("Status: Import of file failed. Please fix file and reimport manually."); } + // Add the item to the ItemList int item_index; if (use_thumbnails) { files->add_item(fname, big_icon, true); item_index = files->get_item_count() - 1; files->set_item_metadata(item_index, fpath); files->set_item_tag_icon(item_index, type_icon); - if (!finfo->import_broken) { - Array udata; - udata.resize(2); - udata[0] = item_index; - udata[1] = fname; - EditorResourcePreview::get_singleton()->queue_resource_preview(fpath, this, "_thumbnail_done", udata); - } + } else { files->add_item(fname, type_icon, true); item_index = files->get_item_count() - 1; files->set_item_metadata(item_index, fpath); } + // Generate the preview + if (!finfo->import_broken) { + Array udata; + udata.resize(2); + udata[0] = item_index; + udata[1] = fname; + EditorResourcePreview::get_singleton()->queue_resource_preview(fpath, this, "_file_list_thumbnail_done", udata); + } + + // Select the items if (cselection.has(fname)) files->select(item_index, false); + if (!p_keep_selection && file != "" && fname == file) { + files->select(item_index, true); + files->ensure_current_is_visible(); + } + + // Tooltip if (finfo->sources.size()) { for (int j = 0; j < finfo->sources.size(); j++) { tooltip += "\nSource: " + finfo->sources[j]; @@ -618,44 +790,41 @@ void FileSystemDock::_update_files(bool p_keep_selection) { } } -void FileSystemDock::_select_file(int p_idx) { - String fpath = files->get_item_metadata(p_idx); +void FileSystemDock::_select_file(const String p_path) { + String fpath = p_path; if (fpath.ends_with("/")) { if (fpath != "res://") { fpath = fpath.substr(0, fpath.length() - 1); } - navigate_to_path(fpath); - } else { + } else if (fpath != "Favorites") { if (ResourceLoader::get_resource_type(fpath) == "PackedScene") { editor->open_request(fpath); } else { editor->load_resource(fpath); } } + navigate_to_path(fpath); } -void FileSystemDock::_go_to_file_list() { +void FileSystemDock::_tree_activate_file() { + TreeItem *selected = tree->get_selected(); + if (selected) { + call_deferred("_select_file", selected->get_metadata(0)); - if (display_mode == DISPLAY_TREE_ONLY) { - file_list_view = true; - _update_display_mode(); - } else { - bool collapsed = tree->get_selected()->is_collapsed(); - tree->get_selected()->set_collapsed(!collapsed); - _update_files(false); + if (path.ends_with("/") || path == "Favorites") { + bool collapsed = selected->is_collapsed(); + selected->set_collapsed(!collapsed); + } } } -void FileSystemDock::_go_to_tree() { - file_list_view = false; - tree->grab_focus(); - _update_display_mode(); - tree->ensure_cursor_is_visible(); +void FileSystemDock::_file_list_activate_file(int p_idx) { + _select_file(files->get_item_metadata(p_idx)); } void FileSystemDock::_preview_invalidated(const String &p_path) { - if (file_list_display_mode == FILE_LIST_DISPLAY_THUMBNAILS && p_path.get_base_dir() == path && search_box->get_text() == String() && file_list_vb->is_visible_in_tree()) { + if (file_list_display_mode == FILE_LIST_DISPLAY_THUMBNAILS && p_path.get_base_dir() == path && searched_string.length() == 0 && file_list_vb->is_visible_in_tree()) { for (int i = 0; i < files->get_item_count(); i++) { @@ -665,7 +834,7 @@ void FileSystemDock::_preview_invalidated(const String &p_path) { udata.resize(2); udata[0] = i; udata[1] = files->get_item_text(i); - EditorResourcePreview::get_singleton()->queue_resource_preview(p_path, this, "_thumbnail_done", udata); + EditorResourcePreview::get_singleton()->queue_resource_preview(p_path, this, "_file_list_thumbnail_done", udata); break; } } @@ -680,11 +849,11 @@ void FileSystemDock::_fs_changed() { split_box->show(); if (tree->is_visible()) { - _update_tree(true); + _update_tree(_compute_uncollapsed_paths()); } if (file_list_vb->is_visible()) { - _update_files(true); + _update_file_list(true); } set_process(false); @@ -721,16 +890,16 @@ void FileSystemDock::_bw_history() { void FileSystemDock::_update_history() { path = history[history_pos]; - current_path->set_text(path); + _set_current_path_text(path); if (tree->is_visible()) { - _update_tree(true); + _update_tree(_compute_uncollapsed_paths()); tree->grab_focus(); tree->ensure_cursor_is_visible(); } if (file_list_vb->is_visible()) { - _update_files(false); + _update_file_list(false); } button_hist_prev->set_disabled(history_pos == 0); @@ -965,26 +1134,43 @@ void FileSystemDock::_update_project_settings_after_move(const Map<String, Strin } }; } + + // Also search for the file in autoload, as they are stored differently from normal files. + List<PropertyInfo> property_list; + ProjectSettings::get_singleton()->get_property_list(&property_list); + for (const List<PropertyInfo>::Element *E = property_list.front(); E; E = E->next()) { + if (E->get().name.begins_with("autoload/")) { + // If the autoload resource paths has a leading "*", it indicates that it is a Singleton, + // so we have to handle both cases when updating. + String autoload = GLOBAL_GET(E->get().name); + String autoload_singleton = autoload.substr(1, autoload.length()); + if (p_renames.has(autoload)) { + ProjectSettings::get_singleton()->set_setting(E->get().name, p_renames[autoload]); + } else if (autoload.begins_with("*") && p_renames.has(autoload_singleton)) { + ProjectSettings::get_singleton()->set_setting(E->get().name, "*" + p_renames[autoload_singleton]); + } + } + } ProjectSettings::get_singleton()->save(); } -void FileSystemDock::_update_favorite_dirs_list_after_move(const Map<String, String> &p_renames) const { - - Vector<String> favorite_dirs = EditorSettings::get_singleton()->get_favorite_dirs(); - Vector<String> new_favorite_dirs; +void FileSystemDock::_update_favorites_list_after_move(const Map<String, String> &p_files_renames, const Map<String, String> &p_folders_renames) const { - for (int i = 0; i < favorite_dirs.size(); i++) { - String old_path = favorite_dirs[i] + "/"; + Vector<String> favorites = EditorSettings::get_singleton()->get_favorites(); + Vector<String> new_favorites; - if (p_renames.has(old_path)) { - String new_path = p_renames[old_path]; - new_favorite_dirs.push_back(new_path.substr(0, new_path.length() - 1)); + for (int i = 0; i < favorites.size(); i++) { + String old_path = favorites[i]; + if (p_folders_renames.has(old_path)) { + new_favorites.push_back(p_folders_renames[old_path]); + } else if (p_files_renames.has(old_path)) { + new_favorites.push_back(p_files_renames[old_path]); } else { - new_favorite_dirs.push_back(favorite_dirs[i]); + new_favorites.push_back(old_path); } } - EditorSettings::get_singleton()->set_favorite_dirs(new_favorite_dirs); + EditorSettings::get_singleton()->set_favorites(new_favorites); } void FileSystemDock::_make_dir_confirm() { @@ -993,14 +1179,19 @@ void FileSystemDock::_make_dir_confirm() { if (dir_name.length() == 0) { EditorNode::get_singleton()->show_warning(TTR("No name provided")); return; - } else if (dir_name.find("/") != -1 || dir_name.find("\\") != -1 || dir_name.find(":") != -1 || dir_name.ends_with(".") || dir_name.ends_with(" ")) { + } else if (dir_name.find("/") != -1 || dir_name.find("\\") != -1 || dir_name.find(":") != -1 || dir_name.find("*") != -1 || + dir_name.find("|") != -1 || dir_name.find(">") != -1 || dir_name.ends_with(".") || dir_name.ends_with(" ")) { EditorNode::get_singleton()->show_warning(TTR("Provided name contains invalid characters")); return; } - print_verbose("Making folder " + dir_name + " in " + path); + String directory = path; + if (!directory.ends_with("/")) { + directory = directory.get_base_dir(); + } + print_verbose("Making folder " + dir_name + " in " + directory); DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES); - Error err = da->change_dir(path); + Error err = da->change_dir(directory); if (err == OK) { err = da->make_dir(dir_name); } @@ -1051,7 +1242,7 @@ void FileSystemDock::_rename_operation_confirm() { _update_dependencies_after_move(file_renames); _update_resource_paths_after_move(file_renames); _update_project_settings_after_move(file_renames); - _update_favorite_dirs_list_after_move(folder_renames); + _update_favorites_list_after_move(file_renames, folder_renames); //Rescan everything print_verbose("FileSystem: calling rescan."); @@ -1144,111 +1335,212 @@ void FileSystemDock::_move_operation_confirm(const String &p_to_path, bool overw _update_dependencies_after_move(file_renames); _update_resource_paths_after_move(file_renames); _update_project_settings_after_move(file_renames); - _update_favorite_dirs_list_after_move(folder_renames); + _update_favorites_list_after_move(file_renames, folder_renames); print_verbose("FileSystem: calling rescan."); _rescan(); } } -void FileSystemDock::_file_option(int p_option) { +Vector<String> FileSystemDock::_tree_get_selected(bool remove_self_inclusion) { + // Build a list of selected items with the active one at the first position + Vector<String> selected_strings; + + TreeItem *favorites_item = tree->get_root()->get_children(); + TreeItem *active_selected = tree->get_selected(); + if (active_selected && active_selected != favorites_item) { + selected_strings.push_back(active_selected->get_metadata(0)); + } + + TreeItem *selected = tree->get_root(); + selected = tree->get_next_selected(selected); + while (selected) { + if (selected != active_selected && selected != favorites_item) { + selected_strings.push_back(selected->get_metadata(0)); + } + selected = tree->get_next_selected(selected); + } + + // Remove paths or files that are included into another + if (remove_self_inclusion && selected_strings.size() > 1) { + selected_strings.sort_custom<NaturalNoCaseComparator>(); + String last_path = ""; + for (int i = 0; i < selected_strings.size(); i++) { + if (last_path != "" && selected_strings[i].begins_with(last_path)) { + selected_strings.remove(i); + i--; + } + if (selected_strings[i].ends_with("/")) { + last_path = selected_strings[i]; + } + } + } + return selected_strings; +} + +void FileSystemDock::_tree_rmb_option(int p_option) { + + Vector<String> selected_strings = _tree_get_selected(); + + // Execute the current option switch (p_option) { - case FILE_SHOW_IN_EXPLORER: { + case FOLDER_EXPAND_ALL: + case FOLDER_COLLAPSE_ALL: { + // Expand or collapse the folder + if (selected_strings.size() == 1) { + bool is_collapsed = (p_option == FOLDER_COLLAPSE_ALL); - String path = this->path; + Vector<TreeItem *> needs_check; + needs_check.push_back(tree->get_selected()); - // first try to grab directory from selected file, so that it works for searched files - int idx = files->get_current(); + while (needs_check.size()) { + needs_check[0]->set_collapsed(is_collapsed); - if (idx >= 0 && idx < files->get_item_count()) { - path = files->get_item_metadata(idx); - path = path.get_base_dir(); + TreeItem *child = needs_check[0]->get_children(); + while (child) { + needs_check.push_back(child); + child = child->get_next(); + } + + needs_check.remove(0); + } } + } break; + default: { + _file_option(p_option, selected_strings); + } break; + } +} + +void FileSystemDock::_file_list_rmb_option(int p_option) { + Vector<int> selected_id = files->get_selected_items(); + Vector<String> selected; + for (int i = 0; i < selected_id.size(); i++) { + selected.push_back(files->get_item_metadata(selected_id[i])); + } + _file_option(p_option, selected); +} - path = ProjectSettings::get_singleton()->globalize_path(path); - OS::get_singleton()->shell_open(String("file://") + path); +void FileSystemDock::_file_option(int p_option, const Vector<String> p_selected) { + // The first one should be the active item + + switch (p_option) { + case FILE_SHOW_IN_EXPLORER: { + // Show the file / folder in the OS explorer + String fpath = path; + if (!fpath.ends_with("/")) { + fpath = fpath.get_base_dir(); + } + String dir = ProjectSettings::get_singleton()->globalize_path(fpath); + OS::get_singleton()->shell_open(String("file://") + dir); } break; + case FILE_OPEN: { - for (int i = 0; i < files->get_item_count(); i++) { - if (files->is_selected(i)) { - _select_file(i); - } + // Open the file + for (int i = 0; i < p_selected.size(); i++) { + _select_file(p_selected[i]); } } break; - case FILE_INSTANCE: { + case FILE_INSTANCE: { + // Instance all selected scenes Vector<String> paths; - - for (int i = 0; i < files->get_item_count(); i++) { - if (!files->is_selected(i)) - continue; - String fpath = files->get_item_metadata(i); + for (int i = 0; i < p_selected.size(); i++) { + String fpath = p_selected[i]; if (EditorFileSystem::get_singleton()->get_file_type(fpath) == "PackedScene") { paths.push_back(fpath); } } - if (!paths.empty()) { emit_signal("instance", paths); } } break; - case FILE_DEPENDENCIES: { - int idx = files->get_current(); - if (idx < 0 || idx >= files->get_item_count()) - break; - String fpath = files->get_item_metadata(idx); - deps_editor->edit(fpath); + case FILE_ADD_FAVORITE: { + // Add the files from favorites + Vector<String> favorites = EditorSettings::get_singleton()->get_favorites(); + for (int i = 0; i < p_selected.size(); i++) { + if (favorites.find(p_selected[i]) == -1) { + favorites.push_back(p_selected[i]); + } + } + EditorSettings::get_singleton()->set_favorites(favorites); + _update_tree(_compute_uncollapsed_paths()); } break; - case FILE_OWNERS: { - int idx = files->get_current(); - if (idx < 0 || idx >= files->get_item_count()) - break; - String fpath = files->get_item_metadata(idx); - owners_editor->show(fpath); + case FILE_REMOVE_FAVORITE: { + // Remove the files from favorites + Vector<String> favorites = EditorSettings::get_singleton()->get_favorites(); + for (int i = 0; i < p_selected.size(); i++) { + favorites.erase(p_selected[i]); + } + EditorSettings::get_singleton()->set_favorites(favorites); + _update_tree(_compute_uncollapsed_paths()); + if (path == "Favorites") + _update_file_list(true); } break; + + case FILE_DEPENDENCIES: { + // Checkout the file dependencies + if (!p_selected.empty()) { + String fpath = p_selected[0]; + deps_editor->edit(fpath); + } + } break; + + case FILE_OWNERS: { + // Checkout the file owners + if (!p_selected.empty()) { + String fpath = p_selected[0]; + owners_editor->show(fpath); + } + } break; + case FILE_MOVE: { + // Move the files to a given location to_move.clear(); - for (int i = 0; i < files->get_item_count(); i++) { - if (!files->is_selected(i)) - continue; - - String fpath = files->get_item_metadata(i); - to_move.push_back(FileOrFolder(fpath, !fpath.ends_with("/"))); + for (int i = 0; i < p_selected.size(); i++) { + String fpath = p_selected[i]; + if (fpath != "res://") { + to_move.push_back(FileOrFolder(fpath, !fpath.ends_with("/"))); + } } if (to_move.size() > 0) { move_dialog->popup_centered_ratio(); } } break; - case FILE_RENAME: { - int idx = files->get_current(); - if (idx < 0 || idx >= files->get_item_count()) - break; - to_rename.path = files->get_item_metadata(idx); - to_rename.is_file = !to_rename.path.ends_with("/"); - if (to_rename.is_file) { - String name = to_rename.path.get_file(); - rename_dialog->set_title(TTR("Renaming file:") + " " + name); - rename_dialog_text->set_text(name); - rename_dialog_text->select(0, name.find_last(".")); - } else { - String name = to_rename.path.substr(0, to_rename.path.length() - 1).get_file(); - rename_dialog->set_title(TTR("Renaming folder:") + " " + name); - rename_dialog_text->set_text(name); - rename_dialog_text->select(0, name.length()); + case FILE_RENAME: { + // Rename the active file + if (!p_selected.empty()) { + to_rename.path = p_selected[0]; + if (to_rename.path != "res://") { + to_rename.is_file = !to_rename.path.ends_with("/"); + if (to_rename.is_file) { + String name = to_rename.path.get_file(); + rename_dialog->set_title(TTR("Renaming file:") + " " + name); + rename_dialog_text->set_text(name); + rename_dialog_text->select(0, name.find_last(".")); + } else { + String name = to_rename.path.substr(0, to_rename.path.length() - 1).get_file(); + rename_dialog->set_title(TTR("Renaming folder:") + " " + name); + rename_dialog_text->set_text(name); + rename_dialog_text->select(0, name.length()); + } + rename_dialog->popup_centered_minsize(Size2(250, 80) * EDSCALE); + rename_dialog_text->grab_focus(); + } } - rename_dialog->popup_centered_minsize(Size2(250, 80) * EDSCALE); - rename_dialog_text->grab_focus(); } break; + case FILE_REMOVE: { + // Remove the selected files Vector<String> remove_files; Vector<String> remove_folders; - for (int i = 0; i < files->get_item_count(); i++) { - String fpath = files->get_item_metadata(i); - if (files->is_selected(i) && fpath != "res://") { + for (int i = 0; i < p_selected.size(); i++) { + String fpath = p_selected[i]; + if (fpath != "res://") { if (fpath.ends_with("/")) { remove_folders.push_back(fpath); } else { @@ -1259,166 +1551,77 @@ void FileSystemDock::_file_option(int p_option) { if (remove_files.size() + remove_folders.size() > 0) { remove_dialog->show(remove_folders, remove_files); - //1) find if used - //2) warn } } break; - case FILE_DUPLICATE: { - int idx = files->get_current(); - if (idx < 0 || idx >= files->get_item_count()) - break; - to_duplicate.path = files->get_item_metadata(idx); - to_duplicate.is_file = !to_duplicate.path.ends_with("/"); - if (to_duplicate.is_file) { - String name = to_duplicate.path.get_file(); - duplicate_dialog->set_title(TTR("Duplicating file:") + " " + name); - duplicate_dialog_text->set_text(name); - duplicate_dialog_text->select(0, name.find_last(".")); - } else { - String name = to_duplicate.path.substr(0, to_duplicate.path.length() - 1).get_file(); - duplicate_dialog->set_title(TTR("Duplicating folder:") + " " + name); - duplicate_dialog_text->set_text(name); - duplicate_dialog_text->select(0, name.length()); + case FILE_DUPLICATE: { + // Duplicate the selected files + for (int i = 0; i < p_selected.size(); i++) { + to_duplicate.path = p_selected[i]; + to_duplicate.is_file = !to_duplicate.path.ends_with("/"); + if (to_duplicate.is_file) { + String name = to_duplicate.path.get_file(); + duplicate_dialog->set_title(TTR("Duplicating file:") + " " + name); + duplicate_dialog_text->set_text(name); + duplicate_dialog_text->select(0, name.find_last(".")); + } else { + String name = to_duplicate.path.substr(0, to_duplicate.path.length() - 1).get_file(); + duplicate_dialog->set_title(TTR("Duplicating folder:") + " " + name); + duplicate_dialog_text->set_text(name); + duplicate_dialog_text->select(0, name.length()); + } + duplicate_dialog->popup_centered_minsize(Size2(250, 80) * EDSCALE); + duplicate_dialog_text->grab_focus(); } - duplicate_dialog->popup_centered_minsize(Size2(250, 80) * EDSCALE); - duplicate_dialog_text->grab_focus(); } break; + case FILE_INFO: { } break; - case FILE_REIMPORT: { + case FILE_REIMPORT: { + // Reimport all selected files Vector<String> reimport; - for (int i = 0; i < files->get_item_count(); i++) { - - if (!files->is_selected(i)) - continue; - - String fpath = files->get_item_metadata(i); - reimport.push_back(fpath); + for (int i = 0; i < p_selected.size(); i++) { + reimport.push_back(p_selected[i]); } ERR_FAIL_COND(reimport.size() == 0); - /* - Ref<ResourceImportMetadata> rimd = ResourceLoader::load_import_metadata(reimport[0]); - ERR_FAIL_COND(!rimd.is_valid()); - String editor=rimd->get_editor(); - - if (editor.begins_with("texture_")) { //compatibility fix for old texture format - editor="texture"; - } - - Ref<EditorImportPlugin> rimp = EditorImportExport::get_singleton()->get_import_plugin_by_name(editor); - ERR_FAIL_COND(!rimp.is_valid()); - - if (reimport.size()==1) { - rimp->import_dialog(reimport[0]); - } else { - rimp->reimport_multiple_files(reimport); - - } - */ } break; + case FILE_NEW_FOLDER: { + // Create a new folder make_dir_dialog_text->set_text("new folder"); make_dir_dialog_text->select_all(); make_dir_dialog->popup_centered_minsize(Size2(250, 80) * EDSCALE); make_dir_dialog_text->grab_focus(); } break; + case FILE_NEW_SCRIPT: { - String tarDir = path; - if (tarDir != "res://" && !tarDir.ends_with("/")) { - tarDir += "/"; + // Create a new script + String fpath = path; + if (!fpath.ends_with("/")) { + fpath = fpath.get_base_dir(); } - - make_script_dialog_text->config("Node", tarDir + "new_script.gd"); + make_script_dialog_text->config("Node", fpath.plus_file("new_script.gd"), false); make_script_dialog_text->popup_centered(Size2(300, 300) * EDSCALE); } break; + case FILE_COPY_PATH: { - int idx = files->get_current(); - if (idx < 0 || idx >= files->get_item_count()) - break; - String fpath = files->get_item_metadata(idx); - OS::get_singleton()->set_clipboard(fpath); + // Copy the file path + if (!p_selected.empty()) { + String fpath = p_selected[0]; + OS::get_singleton()->set_clipboard(fpath); + } } break; + case FILE_NEW_RESOURCE: { + // Create a new resource new_resource_dialog->popup_create(true); } break; } } -void FileSystemDock::_folder_option(int p_option) { - - TreeItem *selected = tree->get_selected(); - - switch (p_option) { - case FOLDER_EXPAND_ALL: - case FOLDER_COLLAPSE_ALL: { - bool is_collapsed = (p_option == FOLDER_COLLAPSE_ALL); - Vector<TreeItem *> needs_check; - needs_check.push_back(selected); - - while (needs_check.size()) { - needs_check[0]->set_collapsed(is_collapsed); - - TreeItem *child = needs_check[0]->get_children(); - while (child) { - needs_check.push_back(child); - child = child->get_next(); - } - - needs_check.remove(0); - } - } break; - case FOLDER_MOVE: { - to_move.clear(); - String fpath = selected->get_metadata(tree->get_selected_column()); - if (fpath != "res://") { - fpath = fpath.ends_with("/") ? fpath.substr(0, fpath.length() - 1) : fpath; - to_move.push_back(FileOrFolder(fpath, false)); - move_dialog->popup_centered_ratio(); - } - } break; - case FOLDER_RENAME: { - to_rename.path = selected->get_metadata(tree->get_selected_column()); - to_rename.is_file = false; - if (to_rename.path != "res://") { - String name = to_rename.path.ends_with("/") ? to_rename.path.substr(0, to_rename.path.length() - 1).get_file() : to_rename.path.get_file(); - rename_dialog->set_title(TTR("Renaming folder:") + " " + name); - rename_dialog_text->set_text(name); - rename_dialog_text->select(0, name.length()); - rename_dialog->popup_centered_minsize(Size2(250, 80) * EDSCALE); - rename_dialog_text->grab_focus(); - } - } break; - case FOLDER_REMOVE: { - Vector<String> remove_folders; - Vector<String> remove_files; - String fpath = selected->get_metadata(tree->get_selected_column()); - if (fpath != "res://") { - remove_folders.push_back(fpath); - remove_dialog->show(remove_folders, remove_files); - } - } break; - case FOLDER_NEW_FOLDER: { - make_dir_dialog_text->set_text("new folder"); - make_dir_dialog_text->select_all(); - make_dir_dialog->popup_centered_minsize(Size2(250, 80) * EDSCALE); - make_dir_dialog_text->grab_focus(); - } break; - case FOLDER_COPY_PATH: { - String fpath = selected->get_metadata(tree->get_selected_column()); - OS::get_singleton()->set_clipboard(fpath); - } break; - case FOLDER_SHOW_IN_EXPLORER: { - String fpath = selected->get_metadata(tree->get_selected_column()); - String dir = ProjectSettings::get_singleton()->globalize_path(fpath); - OS::get_singleton()->shell_open(String("file://") + dir); - } break; - } -} - void FileSystemDock::_resource_created() const { Object *c = new_resource_dialog->instance_selected(); @@ -1431,38 +1634,36 @@ void FileSystemDock::_resource_created() const { RES current_res = RES(r); - editor->save_resource_as(current_res, path); -} - -void FileSystemDock::_dir_rmb_pressed(const Vector2 &p_pos) { - folder_options->clear(); - folder_options->set_size(Size2(1, 1)); + String fpath = path; + if (!fpath.ends_with("/")) { + fpath = fpath.get_base_dir(); + } - folder_options->add_item(TTR("Expand all"), FOLDER_EXPAND_ALL); - folder_options->add_item(TTR("Collapse all"), FOLDER_COLLAPSE_ALL); + editor->save_resource_as(current_res, fpath); +} - TreeItem *item = tree->get_selected(); - if (item) { - String fpath = item->get_metadata(tree->get_selected_column()); - folder_options->add_separator(); - folder_options->add_item(TTR("Copy Path"), FOLDER_COPY_PATH); - if (fpath != "res://") { - folder_options->add_item(TTR("Rename..."), FOLDER_RENAME); - folder_options->add_item(TTR("Move To..."), FOLDER_MOVE); - folder_options->add_item(TTR("Delete"), FOLDER_REMOVE); - } - folder_options->add_separator(); - folder_options->add_item(TTR("New Folder..."), FOLDER_NEW_FOLDER); - folder_options->add_item(TTR("Open In File Manager"), FOLDER_SHOW_IN_EXPLORER); +void FileSystemDock::_search_changed(const String &p_text, const Control *p_from) { + if (searched_string.length() == 0 && p_text.length() > 0) { + // Register the uncollapsed paths before they change + uncollapsed_paths_before_search = _compute_uncollapsed_paths(); } - folder_options->set_position(tree->get_global_position() + p_pos); - folder_options->popup(); -} -void FileSystemDock::_search_changed(const String &p_text) { + searched_string = p_text.to_lower(); + + if (p_from == tree_search_box) + file_list_search_box->set_text(searched_string); + else // file_list_search_box + tree_search_box->set_text(searched_string); - if (file_list_vb->is_visible()) - _update_files(false); + switch (display_mode) { + case DISPLAY_MODE_TREE_ONLY: { + _update_tree(searched_string.length() == 0 ? uncollapsed_paths_before_search : Vector<String>()); + } break; + case DISPLAY_MODE_SPLIT: { + _update_file_list(false); + _update_tree(searched_string.length() == 0 ? uncollapsed_paths_before_search : Vector<String>()); + } break; + } } void FileSystemDock::_rescan() { @@ -1471,20 +1672,19 @@ void FileSystemDock::_rescan() { EditorFileSystem::get_singleton()->scan(); } +void FileSystemDock::_toggle_split_mode(bool p_active) { + display_mode_setting = p_active ? DISPLAY_MODE_SETTING_SPLIT : DISPLAY_MODE_SETTING_TREE_ONLY; + EditorSettings::get_singleton()->set("docks/filesystem/display_mode", int(display_mode_setting)); + _update_display_mode(); +} + void FileSystemDock::fix_dependencies(const String &p_for_file) { deps_editor->edit(p_for_file); } void FileSystemDock::focus_on_filter() { - if (display_mode == DISPLAY_FILE_LIST_ONLY && tree->is_visible()) { - // Tree mode, switch to files list with search box - tree->hide(); - file_list_vb->show(); - button_favorite->hide(); - } - - search_box->grab_focus(); + file_list_search_box->grab_focus(); } void FileSystemDock::set_file_list_display_mode(int p_mode) { @@ -1497,33 +1697,49 @@ void FileSystemDock::set_file_list_display_mode(int p_mode) { } Variant FileSystemDock::get_drag_data_fw(const Point2 &p_point, Control *p_from) { - bool is_favorite = false; + bool all_favorites = true; + bool all_not_favorites = true; + Vector<String> paths; if (p_from == tree) { - TreeItem *selected = tree->get_selected(); - if (!selected) - return Variant(); - - String folder = selected->get_metadata(0); - if (folder == String()) - return Variant(); + // Check if the first selected is in favorite + TreeItem *selected = tree->get_next_selected(tree->get_root()); + while (selected) { + TreeItem *favorites_item = tree->get_root()->get_children(); + if (selected == favorites_item) { + // The "Favorites" item is not draggable + return Variant(); + } - paths.push_back(folder.ends_with("/") ? folder : (folder + "/")); - is_favorite = selected->get_parent() != NULL && tree->get_root()->get_children() == selected->get_parent(); + bool is_favorite = selected->get_parent() != NULL && tree->get_root()->get_children() == selected->get_parent(); + all_favorites &= is_favorite; + all_not_favorites &= !is_favorite; + selected = tree->get_next_selected(selected); + } + if (all_favorites) { + paths = _tree_get_selected(false); + } else { + paths = _tree_get_selected(); + } } else if (p_from == files) { for (int i = 0; i < files->get_item_count(); i++) { if (files->is_selected(i)) { paths.push_back(files->get_item_metadata(i)); } } + all_favorites = false; + all_not_favorites = true; } if (paths.empty()) return Variant(); + if (!all_favorites && !all_not_favorites) + return Variant(); + Dictionary drag_data = EditorNode::get_singleton()->drag_files_and_dirs(paths, p_from); - if (is_favorite) { + if (all_favorites) { drag_data["type"] = "favorite"; } return drag_data; @@ -1535,34 +1751,46 @@ bool FileSystemDock::can_drop_data_fw(const Point2 &p_point, const Variant &p_da if (drag_data.has("type") && String(drag_data["type"]) == "favorite") { - //moving favorite around + // Moving favorite around TreeItem *ti = tree->get_item_at_position(p_point); if (!ti) return false; - int what = tree->get_drop_section_at_position(p_point); + int drop_section = tree->get_drop_section_at_position(p_point); + TreeItem *favorites_item = tree->get_root()->get_children(); - if (ti == tree->get_root()->get_children()) { - return (what == 1); //the parent, first fav + TreeItem *resources_item = favorites_item->get_next(); + + if (ti == favorites_item) { + return (drop_section == 1); // The parent, first fav } - if (ti->get_parent() && tree->get_root()->get_children() == ti->get_parent()) { - return true; // a favorite + if (ti->get_parent() && favorites_item == ti->get_parent()) { + return true; // A favorite } - - if (ti == tree->get_root()->get_children()->get_next()) { - return (what == -1); //the tree, last fav + if (ti == resources_item) { + return (drop_section == -1); // The tree, last fav } return false; } if (drag_data.has("type") && String(drag_data["type"]) == "resource") { - String to_dir = _get_drag_target_folder(p_point, p_from); + // Move resources + String to_dir; + bool favorite; + _get_drag_target_folder(to_dir, favorite, p_point, p_from); return !to_dir.empty(); } if (drag_data.has("type") && (String(drag_data["type"]) == "files" || String(drag_data["type"]) == "files_and_dirs")) { - String to_dir = _get_drag_target_folder(p_point, p_from); + // Move files or dir + String to_dir; + bool favorite; + _get_drag_target_folder(to_dir, favorite, p_point, p_from); + + if (favorite) + return true; + if (to_dir.empty()) return false; @@ -1587,71 +1815,70 @@ void FileSystemDock::drop_data_fw(const Point2 &p_point, const Variant &p_data, return; Dictionary drag_data = p_data; - if (drag_data.has("type") && String(drag_data["type"]) == "favorite") { + Vector<String> dirs = EditorSettings::get_singleton()->get_favorites(); - //moving favorite around + if (drag_data.has("type") && String(drag_data["type"]) == "favorite") { + // Moving favorite around TreeItem *ti = tree->get_item_at_position(p_point); if (!ti) return; + int drop_section = tree->get_drop_section_at_position(p_point); + int drop_position; Vector<String> files = drag_data["files"]; - - ERR_FAIL_COND(files.size() != 1); - - String swap = files[0]; - if (swap != "res://" && swap.ends_with("/")) { - swap = swap.substr(0, swap.length() - 1); - } - - int what = tree->get_drop_section_at_position(p_point); - - TreeItem *swap_item = NULL; - - if (ti == tree->get_root()->get_children()) { - swap_item = tree->get_root()->get_children()->get_children(); - - } else if (ti->get_parent() && tree->get_root()->get_children() == ti->get_parent()) { - if (what == -1) { - swap_item = ti; - } else { - swap_item = ti->get_next(); + TreeItem *favorites_item = tree->get_root()->get_children(); + TreeItem *resources_item = favorites_item->get_next(); + + if (ti == favorites_item) { + // Drop on the favorite folder + drop_position = 0; + } else if (ti == resources_item) { + // Drop on the resouce item + drop_position = dirs.size(); + } else { + // Drop in the list + drop_position = dirs.find(ti->get_metadata(0)); + if (drop_section == 1) { + drop_position++; } } - String swap_with; - - if (swap_item) { - swap_with = swap_item->get_metadata(0); - if (swap_with != "res://" && swap_with.ends_with("/")) { - swap_with = swap_with.substr(0, swap_with.length() - 1); + // Remove dragged favorites + Vector<int> to_remove; + int offset = 0; + for (int i = 0; i < files.size(); i++) { + int to_remove_pos = dirs.find(files[i]); + to_remove.push_back(to_remove_pos); + if (to_remove_pos < drop_position) { + offset++; } } + drop_position -= offset; + to_remove.sort(); + for (int i = 0; i < to_remove.size(); i++) { + dirs.remove(to_remove[i] - i); + } - if (swap == swap_with) - return; - - Vector<String> dirs = EditorSettings::get_singleton()->get_favorite_dirs(); - - ERR_FAIL_COND(dirs.find(swap) == -1); - ERR_FAIL_COND(swap_with != String() && dirs.find(swap_with) == -1); - - dirs.erase(swap); - - if (swap_with == String()) { - dirs.push_back(swap); - } else { - int idx = dirs.find(swap_with); - dirs.insert(idx, swap); + // Re-add them at the right position + for (int i = 0; i < files.size(); i++) { + dirs.insert(drop_position, files[i]); + drop_position++; } - EditorSettings::get_singleton()->set_favorite_dirs(dirs); - _update_tree(true); + EditorSettings::get_singleton()->set_favorites(dirs); + _update_tree(_compute_uncollapsed_paths()); + + if (display_mode == DISPLAY_MODE_SPLIT && path == "Favorites") + _update_file_list(true); return; } if (drag_data.has("type") && String(drag_data["type"]) == "resource") { + // Moving resource Ref<Resource> res = drag_data["resource"]; - String to_dir = _get_drag_target_folder(p_point, p_from); + String to_dir; + bool favorite; + _get_drag_target_folder(to_dir, favorite, p_point, p_from); if (res.is_valid() && !to_dir.empty()) { EditorNode::get_singleton()->push_item(res.ptr()); EditorNode::get_singleton()->save_resource_as(res, to_dir); @@ -1659,7 +1886,10 @@ void FileSystemDock::drop_data_fw(const Point2 &p_point, const Variant &p_data, } if (drag_data.has("type") && (String(drag_data["type"]) == "files" || String(drag_data["type"]) == "files_and_dirs")) { - String to_dir = _get_drag_target_folder(p_point, p_from); + // Move files or add to favorites + String to_dir; + bool favorite; + _get_drag_target_folder(to_dir, favorite, p_point, p_from); if (!to_dir.empty()) { Vector<String> fnames = drag_data["files"]; to_move.clear(); @@ -1667,50 +1897,93 @@ void FileSystemDock::drop_data_fw(const Point2 &p_point, const Variant &p_data, to_move.push_back(FileOrFolder(fnames[i], !fnames[i].ends_with("/"))); } _move_operation_confirm(to_dir); + } else if (favorite) { + // Add the files from favorites + Vector<String> fnames = drag_data["files"]; + Vector<String> favorites = EditorSettings::get_singleton()->get_favorites(); + for (int i = 0; i < fnames.size(); i++) { + if (favorites.find(fnames[i]) == -1) { + favorites.push_back(fnames[i]); + } + } + EditorSettings::get_singleton()->set_favorites(favorites); + _update_tree(_compute_uncollapsed_paths()); } } } -String FileSystemDock::_get_drag_target_folder(const Point2 &p_point, Control *p_from) const { +void FileSystemDock::_get_drag_target_folder(String &target, bool &target_favorites, const Point2 &p_point, Control *p_from) const { + target = String(); + target_favorites = false; + + // In the file list if (p_from == files) { int pos = files->get_item_at_position(p_point, true); - if (pos == -1) - return path; + if (pos == -1) { + return; + } - String target = files->get_item_metadata(pos); - return target.ends_with("/") ? target : path; + String ltarget = files->get_item_metadata(pos); + target = ltarget.ends_with("/") ? ltarget : path.get_base_dir(); + return; } + // In the tree if (p_from == tree) { TreeItem *ti = tree->get_item_at_position(p_point); - if (ti && ti != tree->get_root()->get_children()) - return ti->get_metadata(0); + int section = tree->get_drop_section_at_position(p_point); + if (ti) { + // Check the favorites first + if (ti == tree->get_root()->get_children() && section >= 0) { + target_favorites = true; + return; + } else if (ti->get_parent() == tree->get_root()->get_children()) { + target_favorites = true; + return; + } else { + String fpath = ti->get_metadata(0); + if (section == 0) { + if (fpath.ends_with("/")) { + // We drop on a folder + target = fpath; + return; + } + } else { + if (ti->get_parent() != tree->get_root()->get_children()) { + // Not in the favorite section + if (fpath != "res://") { + // We drop between two files + if (fpath.ends_with("/")) { + fpath = fpath.substr(0, fpath.length() - 1); + } + target = fpath.get_base_dir(); + return; + } + } + } + } + } } - return String(); + return; } -void FileSystemDock::_files_list_rmb_select(int p_item, const Vector2 &p_pos) { - - //Right clicking ".." should clear current selection - if (files->get_item_text(p_item) == "..") { - for (int i = 0; i < files->get_item_count(); i++) { - files->unselect(i); - } - } +void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, Vector<String> p_paths, bool p_display_path_dependent_options) { + // Add options for files and folders + ERR_FAIL_COND(p_paths.empty()) Vector<String> filenames; Vector<String> foldernames; + Vector<String> favorites = EditorSettings::get_singleton()->get_favorites(); + bool all_files = true; bool all_files_scenes = true; bool all_folders = true; - for (int i = 0; i < files->get_item_count(); i++) { - if (!files->is_selected(i)) { - continue; - } - - String fpath = files->get_item_metadata(i); + bool all_favorites = true; + bool all_not_favorites = true; + for (int i = 0; i < p_paths.size(); i++) { + String fpath = p_paths[i]; if (fpath.ends_with("/")) { foldernames.push_back(fpath); all_files = false; @@ -1719,68 +1992,140 @@ void FileSystemDock::_files_list_rmb_select(int p_item, const Vector2 &p_pos) { all_folders = false; all_files_scenes &= (EditorFileSystem::get_singleton()->get_file_type(fpath) == "PackedScene"); } + + // Check if in favorites + bool found = false; + for (int j = 0; j < favorites.size(); j++) { + if (favorites[j] == fpath) { + found = true; + break; + } + } + if (found) { + all_not_favorites = false; + } else { + all_favorites = false; + } } - file_options->clear(); - file_options->set_size(Size2(1, 1)); if (all_files) { if (all_files_scenes && filenames.size() >= 1) { - file_options->add_item(TTR("Open Scene(s)"), FILE_OPEN); - file_options->add_item(TTR("Instance"), FILE_INSTANCE); - file_options->add_separator(); + p_popup->add_item(TTR("Open Scene(s)"), FILE_OPEN); + p_popup->add_item(TTR("Instance"), FILE_INSTANCE); + p_popup->add_separator(); } if (!all_files_scenes && filenames.size() == 1) { - file_options->add_item(TTR("Open"), FILE_OPEN); - file_options->add_separator(); + p_popup->add_item(TTR("Open"), FILE_OPEN); + p_popup->add_separator(); } + } + if (p_paths.size() >= 1) { + if (!all_favorites) { + p_popup->add_item(TTR("Add to favorites"), FILE_ADD_FAVORITE); + } + if (!all_not_favorites) { + p_popup->add_item(TTR("Remove from favorites"), FILE_REMOVE_FAVORITE); + } + p_popup->add_separator(); + } + + if (all_files) { if (filenames.size() == 1) { - file_options->add_item(TTR("Edit Dependencies..."), FILE_DEPENDENCIES); - file_options->add_item(TTR("View Owners..."), FILE_OWNERS); - file_options->add_separator(); + p_popup->add_item(TTR("Edit Dependencies..."), FILE_DEPENDENCIES); + p_popup->add_item(TTR("View Owners..."), FILE_OWNERS); + p_popup->add_separator(); } } else if (all_folders && foldernames.size() > 0) { - file_options->add_item(TTR("Open"), FILE_OPEN); - file_options->add_separator(); + p_popup->add_item(TTR("Open"), FILE_OPEN); + p_popup->add_separator(); } - int num_items = filenames.size() + foldernames.size(); - if (num_items >= 1) { - if (num_items == 1) { - file_options->add_item(TTR("Copy Path"), FILE_COPY_PATH); - file_options->add_item(TTR("Rename..."), FILE_RENAME); - file_options->add_item(TTR("Duplicate..."), FILE_DUPLICATE); + if (p_paths.size() == 1) { + p_popup->add_item(TTR("Copy Path"), FILE_COPY_PATH); + p_popup->add_item(TTR("Rename..."), FILE_RENAME); + p_popup->add_item(TTR("Duplicate..."), FILE_DUPLICATE); + } + + p_popup->add_item(TTR("Move To..."), FILE_MOVE); + p_popup->add_item(TTR("Delete"), FILE_REMOVE); + + if (p_paths.size() == 1) { + p_popup->add_separator(); + if (p_display_path_dependent_options) { + p_popup->add_item(TTR("New Folder..."), FILE_NEW_FOLDER); + p_popup->add_item(TTR("New Script..."), FILE_NEW_SCRIPT); + p_popup->add_item(TTR("New Resource..."), FILE_NEW_RESOURCE); + } + + String fpath = p_paths[0]; + String item_text = fpath.ends_with("/") ? TTR("Open in File Manager") : TTR("Show in File Manager"); + p_popup->add_item(item_text, FILE_SHOW_IN_EXPLORER); + } +} + +void FileSystemDock::_tree_rmb_select(const Vector2 &p_pos) { + // Right click is pressed in the tree + Vector<String> paths = _tree_get_selected(); + + if (paths.size() == 1) { + if (paths[0].ends_with("/")) { + tree_popup->add_item(TTR("Expand All"), FOLDER_EXPAND_ALL); + tree_popup->add_item(TTR("Collapse All"), FOLDER_COLLAPSE_ALL); + tree_popup->add_separator(); } - file_options->add_item(TTR("Move To..."), FILE_MOVE); - file_options->add_item(TTR("Delete"), FILE_REMOVE); - file_options->add_separator(); } - file_options->add_item(TTR("New Folder..."), FILE_NEW_FOLDER); - file_options->add_item(TTR("New Script..."), FILE_NEW_SCRIPT); - file_options->add_item(TTR("New Resource..."), FILE_NEW_RESOURCE); + // Popup + if (!paths.empty()) { + tree_popup->clear(); + tree_popup->set_size(Size2(1, 1)); + _file_and_folders_fill_popup(tree_popup, paths); + tree_popup->set_position(tree->get_global_position() + p_pos); + tree_popup->popup(); + } +} - String fpath = files->get_item_metadata(p_item); - String item_text = fpath.ends_with("/") ? TTR("Open In File Manager") : TTR("Show In File Manager"); - file_options->add_item(item_text, FILE_SHOW_IN_EXPLORER); +void FileSystemDock::_file_list_rmb_select(int p_item, const Vector2 &p_pos) { + // Right click is pressed in the file list + Vector<String> paths; + for (int i = 0; i < files->get_item_count(); i++) { + if (!files->is_selected(i)) + continue; + if (files->get_item_text(p_item) == "..") { + files->unselect(i); + continue; + } + paths.push_back(files->get_item_metadata(i)); + } - file_options->set_position(files->get_global_position() + p_pos); - file_options->popup(); + // Popup + if (!paths.empty()) { + file_list_popup->clear(); + file_list_popup->set_size(Size2(1, 1)); + _file_and_folders_fill_popup(file_list_popup, paths, searched_string.length() == 0); + file_list_popup->set_position(files->get_global_position() + p_pos); + file_list_popup->popup(); + } } -void FileSystemDock::_rmb_pressed(const Vector2 &p_pos) { - file_options->clear(); - file_options->set_size(Size2(1, 1)); +void FileSystemDock::_file_list_rmb_pressed(const Vector2 &p_pos) { + // Right click on empty space for file list + if (searched_string.length() > 0) + return; - file_options->add_item(TTR("New Folder..."), FILE_NEW_FOLDER); - file_options->add_item(TTR("New Script..."), FILE_NEW_SCRIPT); - file_options->add_item(TTR("New Resource..."), FILE_NEW_RESOURCE); - file_options->add_item(TTR("Show In File Manager"), FILE_SHOW_IN_EXPLORER); - file_options->set_position(files->get_global_position() + p_pos); - file_options->popup(); + file_list_popup->clear(); + file_list_popup->set_size(Size2(1, 1)); + + file_list_popup->add_item(TTR("New Folder..."), FILE_NEW_FOLDER); + file_list_popup->add_item(TTR("New Script..."), FILE_NEW_SCRIPT); + file_list_popup->add_item(TTR("New Resource..."), FILE_NEW_RESOURCE); + file_list_popup->add_item(TTR("Show in File Manager"), FILE_SHOW_IN_EXPLORER); + file_list_popup->set_position(files->get_global_position() + p_pos); + file_list_popup->popup(); } void FileSystemDock::select_file(const String &p_file) { @@ -1790,11 +2135,24 @@ void FileSystemDock::select_file(const String &p_file) { void FileSystemDock::_file_multi_selected(int p_index, bool p_selected) { + // Set the path to the current focussed item + int current = files->get_current(); + if (current == p_index) { + String fpath = files->get_item_metadata(current); + if (!fpath.ends_with("/")) { + path = fpath; + if (display_mode == DISPLAY_MODE_SPLIT) { + _update_tree(_compute_uncollapsed_paths()); + } + } + } + + // Update the import dock import_dock_needs_update = true; call_deferred("_update_import_dock"); } -void FileSystemDock::_files_gui_input(Ref<InputEvent> p_event) { +void FileSystemDock::_tree_gui_input(Ref<InputEvent> p_event) { if (get_viewport()->get_modal_stack_top()) return; //ignore because of modal window @@ -1802,21 +2160,34 @@ void FileSystemDock::_files_gui_input(Ref<InputEvent> p_event) { Ref<InputEventKey> key = p_event; if (key.is_valid() && key->is_pressed() && !key->is_echo()) { if (ED_IS_SHORTCUT("filesystem_dock/duplicate", p_event)) { - _file_option(FILE_DUPLICATE); + _tree_rmb_option(FILE_DUPLICATE); } else if (ED_IS_SHORTCUT("filesystem_dock/copy_path", p_event)) { - _file_option(FILE_COPY_PATH); + _tree_rmb_option(FILE_COPY_PATH); } else if (ED_IS_SHORTCUT("filesystem_dock/delete", p_event)) { - _file_option(FILE_REMOVE); + _tree_rmb_option(FILE_REMOVE); } else if (ED_IS_SHORTCUT("filesystem_dock/rename", p_event)) { - _file_option(FILE_RENAME); + _tree_rmb_option(FILE_RENAME); } } } -void FileSystemDock::_file_selected() { +void FileSystemDock::_file_list_gui_input(Ref<InputEvent> p_event) { - import_dock_needs_update = true; - _update_import_dock(); + if (get_viewport()->get_modal_stack_top()) + return; //ignore because of modal window + + Ref<InputEventKey> key = p_event; + if (key.is_valid() && key->is_pressed() && !key->is_echo()) { + if (ED_IS_SHORTCUT("filesystem_dock/duplicate", p_event)) { + _file_list_rmb_option(FILE_DUPLICATE); + } else if (ED_IS_SHORTCUT("filesystem_dock/copy_path", p_event)) { + _file_list_rmb_option(FILE_COPY_PATH); + } else if (ED_IS_SHORTCUT("filesystem_dock/delete", p_event)) { + _file_list_rmb_option(FILE_REMOVE); + } else if (ED_IS_SHORTCUT("filesystem_dock/rename", p_event)) { + _file_list_rmb_option(FILE_RENAME); + } + } } void FileSystemDock::_update_import_dock() { @@ -1824,15 +2195,33 @@ void FileSystemDock::_update_import_dock() { if (!import_dock_needs_update) return; - //check import + // List selected + Vector<String> selected; + if (display_mode_setting == DISPLAY_MODE_SETTING_TREE_ONLY) { + // Use the tree + selected = _tree_get_selected(); + + } else { + // Use the file list + for (int i = 0; i < files->get_item_count(); i++) { + if (!files->is_selected(i)) + continue; + + selected.push_back(files->get_item_metadata(i)); + } + } + + // Check import Vector<String> imports; String import_type; + for (int i = 0; i < selected.size(); i++) { + String fpath = selected[i]; - for (int i = 0; i < files->get_item_count(); i++) { - if (!files->is_selected(i)) - continue; + if (fpath.ends_with("/")) { + imports.clear(); + break; + } - String fpath = files->get_item_metadata(i); if (!FileAccess::exists(fpath + ".import")) { imports.clear(); break; @@ -1869,26 +2258,32 @@ void FileSystemDock::_update_import_dock() { void FileSystemDock::_bind_methods() { - ClassDB::bind_method(D_METHOD("_files_gui_input"), &FileSystemDock::_files_gui_input); + ClassDB::bind_method(D_METHOD("_file_list_gui_input"), &FileSystemDock::_file_list_gui_input); + ClassDB::bind_method(D_METHOD("_tree_gui_input"), &FileSystemDock::_tree_gui_input); + ClassDB::bind_method(D_METHOD("_update_tree"), &FileSystemDock::_update_tree); ClassDB::bind_method(D_METHOD("_rescan"), &FileSystemDock::_rescan); - ClassDB::bind_method(D_METHOD("_favorites_pressed"), &FileSystemDock::_favorites_pressed); - ClassDB::bind_method(D_METHOD("_show_current_scene_file"), &FileSystemDock::_show_current_scene_file); - //ClassDB::bind_method(D_METHOD("_instance_pressed"),&ScenesDock::_instance_pressed); - ClassDB::bind_method(D_METHOD("_go_to_file_list"), &FileSystemDock::_go_to_file_list); - ClassDB::bind_method(D_METHOD("_dir_rmb_pressed"), &FileSystemDock::_dir_rmb_pressed); - ClassDB::bind_method(D_METHOD("_thumbnail_done"), &FileSystemDock::_thumbnail_done); + ClassDB::bind_method(D_METHOD("_toggle_split_mode"), &FileSystemDock::_toggle_split_mode); + + ClassDB::bind_method(D_METHOD("_tree_rmb_option", "option"), &FileSystemDock::_tree_rmb_option); + ClassDB::bind_method(D_METHOD("_file_list_rmb_option", "option"), &FileSystemDock::_file_list_rmb_option); + + ClassDB::bind_method(D_METHOD("_tree_rmb_select"), &FileSystemDock::_tree_rmb_select); + ClassDB::bind_method(D_METHOD("_file_list_rmb_select"), &FileSystemDock::_file_list_rmb_select); + ClassDB::bind_method(D_METHOD("_file_list_rmb_pressed"), &FileSystemDock::_file_list_rmb_pressed); + + ClassDB::bind_method(D_METHOD("_file_list_thumbnail_done"), &FileSystemDock::_file_list_thumbnail_done); + ClassDB::bind_method(D_METHOD("_tree_thumbnail_done"), &FileSystemDock::_tree_thumbnail_done); + ClassDB::bind_method(D_METHOD("_file_list_activate_file"), &FileSystemDock::_file_list_activate_file); + ClassDB::bind_method(D_METHOD("_tree_activate_file"), &FileSystemDock::_tree_activate_file); ClassDB::bind_method(D_METHOD("_select_file"), &FileSystemDock::_select_file); - ClassDB::bind_method(D_METHOD("_go_to_tree"), &FileSystemDock::_go_to_tree); ClassDB::bind_method(D_METHOD("navigate_to_path"), &FileSystemDock::navigate_to_path); ClassDB::bind_method(D_METHOD("_change_file_display"), &FileSystemDock::_change_file_display); ClassDB::bind_method(D_METHOD("_fw_history"), &FileSystemDock::_fw_history); ClassDB::bind_method(D_METHOD("_bw_history"), &FileSystemDock::_bw_history); ClassDB::bind_method(D_METHOD("_fs_changed"), &FileSystemDock::_fs_changed); - ClassDB::bind_method(D_METHOD("_dir_selected"), &FileSystemDock::_dir_selected); - ClassDB::bind_method(D_METHOD("_file_option"), &FileSystemDock::_file_option); - ClassDB::bind_method(D_METHOD("_folder_option"), &FileSystemDock::_folder_option); + ClassDB::bind_method(D_METHOD("_tree_multi_selected"), &FileSystemDock::_tree_multi_selected); ClassDB::bind_method(D_METHOD("_make_dir_confirm"), &FileSystemDock::_make_dir_confirm); ClassDB::bind_method(D_METHOD("_resource_created"), &FileSystemDock::_resource_created); ClassDB::bind_method(D_METHOD("_move_operation_confirm", "to_path", "overwrite"), &FileSystemDock::_move_operation_confirm, DEFVAL(false)); @@ -1901,13 +2296,10 @@ void FileSystemDock::_bind_methods() { ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &FileSystemDock::get_drag_data_fw); ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &FileSystemDock::can_drop_data_fw); ClassDB::bind_method(D_METHOD("drop_data_fw"), &FileSystemDock::drop_data_fw); - ClassDB::bind_method(D_METHOD("_files_list_rmb_select"), &FileSystemDock::_files_list_rmb_select); ClassDB::bind_method(D_METHOD("_preview_invalidated"), &FileSystemDock::_preview_invalidated); - ClassDB::bind_method(D_METHOD("_file_selected"), &FileSystemDock::_file_selected); ClassDB::bind_method(D_METHOD("_file_multi_selected"), &FileSystemDock::_file_multi_selected); ClassDB::bind_method(D_METHOD("_update_import_dock"), &FileSystemDock::_update_import_dock); - ClassDB::bind_method(D_METHOD("_rmb_pressed"), &FileSystemDock::_rmb_pressed); ADD_SIGNAL(MethodInfo("instance", PropertyInfo(Variant::POOL_STRING_ARRAY, "files"))); ADD_SIGNAL(MethodInfo("open")); @@ -1924,9 +2316,12 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { ED_SHORTCUT("filesystem_dock/delete", TTR("Delete"), KEY_DELETE); ED_SHORTCUT("filesystem_dock/rename", TTR("Rename")); + VBoxContainer *top_vbc = memnew(VBoxContainer); + add_child(top_vbc); + HBoxContainer *toolbar_hbc = memnew(HBoxContainer); toolbar_hbc->add_constant_override("separation", 0); - add_child(toolbar_hbc); + top_vbc->add_child(toolbar_hbc); button_hist_prev = memnew(ToolButton); button_hist_prev->set_disabled(true); @@ -1942,6 +2337,7 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { current_path = memnew(LineEdit); current_path->set_h_size_flags(SIZE_EXPAND_FILL); + _set_current_path_text(path); toolbar_hbc->add_child(current_path); button_reload = memnew(Button); @@ -1952,29 +2348,32 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { button_reload->hide(); toolbar_hbc->add_child(button_reload); - //toolbar_hbc->add_spacer(); + button_toggle_display_mode = memnew(Button); + button_toggle_display_mode->set_flat(true); + button_toggle_display_mode->set_toggle_mode(true); + button_toggle_display_mode->connect("toggled", this, "_toggle_split_mode"); + button_toggle_display_mode->set_focus_mode(FOCUS_NONE); + button_toggle_display_mode->set_tooltip(TTR("Toggle split mode")); + toolbar_hbc->add_child(button_toggle_display_mode); - button_favorite = memnew(Button); - button_favorite->set_flat(true); - button_favorite->set_toggle_mode(true); - button_favorite->connect("pressed", this, "_favorites_pressed"); - button_favorite->set_tooltip(TTR("Toggle folder status as Favorite.")); - button_favorite->set_focus_mode(FOCUS_NONE); - toolbar_hbc->add_child(button_favorite); - - button_show = memnew(Button); - button_show->set_flat(true); - button_show->connect("pressed", this, "_show_current_scene_file"); - toolbar_hbc->add_child(button_show); - button_show->set_focus_mode(FOCUS_NONE); - button_show->set_tooltip(TTR("Show current scene file.")); + HBoxContainer *toolbar2_hbc = memnew(HBoxContainer); + toolbar2_hbc->add_constant_override("separation", 0); + top_vbc->add_child(toolbar2_hbc); + + tree_search_box = memnew(LineEdit); + tree_search_box->set_h_size_flags(SIZE_EXPAND_FILL); + tree_search_box->set_placeholder(TTR("Search files")); + tree_search_box->connect("text_changed", this, "_search_changed", varray(tree_search_box)); + toolbar2_hbc->add_child(tree_search_box); + + //toolbar_hbc->add_spacer(); //Control *spacer = memnew( Control); /* button_open = memnew( Button ); button_open->set_flat(true); - button_open->connect("pressed",this,"_go_to_file_list"); + button_open->connect("pressed",this,"_tree_toggle_collapsed"); toolbar_hbc->add_child(button_open); button_open->hide(); button_open->set_focus_mode(FOCUS_NONE); @@ -1990,13 +2389,13 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { button_instance->set_tooltip(TTR("Instance the selected scene(s) as child of the selected node.")); */ - file_options = memnew(PopupMenu); - file_options->set_hide_on_window_lose_focus(true); - add_child(file_options); + file_list_popup = memnew(PopupMenu); + file_list_popup->set_hide_on_window_lose_focus(true); + add_child(file_list_popup); - folder_options = memnew(PopupMenu); - folder_options->set_hide_on_window_lose_focus(true); - add_child(folder_options); + tree_popup = memnew(PopupMenu); + tree_popup->set_hide_on_window_lose_focus(true); + add_child(tree_popup); split_box = memnew(VSplitContainer); split_box->set_v_size_flags(SIZE_EXPAND_FILL); @@ -2007,13 +2406,15 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { tree->set_hide_root(true); tree->set_drag_forwarding(this); tree->set_allow_rmb_select(true); - tree->set_custom_minimum_size(Size2(0, 200 * EDSCALE)); + tree->set_select_mode(Tree::SELECT_MULTI); + tree->set_custom_minimum_size(Size2(0, 15 * EDSCALE)); split_box->add_child(tree); tree->connect("item_edited", this, "_favorite_toggled"); - tree->connect("item_activated", this, "_go_to_file_list"); - tree->connect("cell_selected", this, "_dir_selected"); - tree->connect("item_rmb_selected", this, "_dir_rmb_pressed"); + tree->connect("item_activated", this, "_tree_activate_file"); + tree->connect("multi_selected", this, "_tree_multi_selected"); + tree->connect("item_rmb_selected", this, "_tree_rmb_select"); + tree->connect("gui_input", this, "_tree_gui_input"); file_list_vb = memnew(VBoxContainer); file_list_vb->set_v_size_flags(SIZE_EXPAND_FILL); @@ -2022,16 +2423,11 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { path_hb = memnew(HBoxContainer); file_list_vb->add_child(path_hb); - button_tree = memnew(ToolButton); - button_tree->set_tooltip(TTR("Enter tree-view.")); - button_tree->hide(); - path_hb->add_child(button_tree); - - search_box = memnew(LineEdit); - search_box->set_h_size_flags(SIZE_EXPAND_FILL); - search_box->set_placeholder(TTR("Search files")); - search_box->connect("text_changed", this, "_search_changed"); - path_hb->add_child(search_box); + file_list_search_box = memnew(LineEdit); + file_list_search_box->set_h_size_flags(SIZE_EXPAND_FILL); + file_list_search_box->set_placeholder(TTR("Search files")); + file_list_search_box->connect("text_changed", this, "_search_changed", varray(file_list_search_box)); + path_hb->add_child(file_list_search_box); button_file_list_display_mode = memnew(ToolButton); button_file_list_display_mode->set_toggle_mode(true); @@ -2041,11 +2437,11 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { files->set_v_size_flags(SIZE_EXPAND_FILL); files->set_select_mode(ItemList::SELECT_MULTI); files->set_drag_forwarding(this); - files->connect("item_rmb_selected", this, "_files_list_rmb_select"); - files->connect("gui_input", this, "_files_gui_input"); - files->connect("item_selected", this, "_file_selected"); + files->connect("item_rmb_selected", this, "_file_list_rmb_select"); + files->connect("gui_input", this, "_file_list_gui_input"); files->connect("multi_selected", this, "_file_multi_selected"); - files->connect("rmb_clicked", this, "_rmb_pressed"); + files->connect("rmb_clicked", this, "_file_list_rmb_pressed"); + files->set_custom_minimum_size(Size2(0, 15 * EDSCALE)); files->set_allow_rmb_select(true); file_list_vb->add_child(files); @@ -2123,7 +2519,11 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { new_resource_dialog->set_base_type("Resource"); new_resource_dialog->connect("create", this, "_resource_created"); + searched_string = String(); + uncollapsed_paths_before_search = Vector<String>(); + updating_tree = false; + tree_update_id = 0; initialized = false; import_dock_needs_update = false; @@ -2131,8 +2531,12 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { history_max_size = 20; history.push_back("res://"); - display_mode = DISPLAY_SPLIT; + display_mode = DISPLAY_MODE_SPLIT; file_list_display_mode = FILE_LIST_DISPLAY_THUMBNAILS; + + display_mode_setting = DISPLAY_MODE_SETTING_TREE_ONLY; + old_display_mode_setting = DISPLAY_MODE_SETTING_TREE_ONLY; + always_show_folders = false; } FileSystemDock::~FileSystemDock() { diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h index fbbe87fc16..df6fa5f9d2 100644 --- a/editor/filesystem_dock.h +++ b/editor/filesystem_dock.h @@ -44,8 +44,8 @@ #include "scene/gui/tree.h" #include "scene/main/timer.h" -#include "os/dir_access.h" -#include "os/thread.h" +#include "core/os/dir_access.h" +#include "core/os/thread.h" #include "create_dialog.h" @@ -66,15 +66,21 @@ public: }; private: + enum DisplayModeSetting { + DISPLAY_MODE_SETTING_TREE_ONLY, + DISPLAY_MODE_SETTING_SPLIT, + }; + enum DisplayMode { - DISPLAY_TREE_ONLY, - DISPLAY_FILE_LIST_ONLY, - DISPLAY_SPLIT, + DISPLAY_MODE_TREE_ONLY, + DISPLAY_MODE_SPLIT, }; enum FileMenu { FILE_OPEN, FILE_INSTANCE, + FILE_ADD_FAVORITE, + FILE_REMOVE_FAVORITE, FILE_DEPENDENCIES, FILE_OWNERS, FILE_MOVE, @@ -87,18 +93,9 @@ private: FILE_NEW_SCRIPT, FILE_SHOW_IN_EXPLORER, FILE_COPY_PATH, - FILE_NEW_RESOURCE - }; - - enum FolderMenu { + FILE_NEW_RESOURCE, FOLDER_EXPAND_ALL, FOLDER_COLLAPSE_ALL, - FOLDER_MOVE, - FOLDER_RENAME, - FOLDER_REMOVE, - FOLDER_NEW_FOLDER, - FOLDER_SHOW_IN_EXPLORER, - FOLDER_COPY_PATH }; VBoxContainer *scanning_vb; @@ -109,24 +106,28 @@ private: EditorNode *editor; Set<String> favorites; + Button *button_toggle_display_mode; Button *button_reload; - Button *button_favorite; - Button *button_tree; Button *button_file_list_display_mode; Button *button_hist_next; Button *button_hist_prev; - Button *button_show; LineEdit *current_path; - LineEdit *search_box; + LineEdit *tree_search_box; + LineEdit *file_list_search_box; + + String searched_string; + Vector<String> uncollapsed_paths_before_search; + TextureRect *search_icon; HBoxContainer *path_hb; FileListDisplayMode file_list_display_mode; DisplayMode display_mode; - bool file_list_view; + DisplayModeSetting display_mode_setting; + DisplayModeSetting old_display_mode_setting; - PopupMenu *file_options; - PopupMenu *folder_options; + PopupMenu *file_list_popup; + PopupMenu *tree_popup; DependencyEditor *deps_editor; DependencyEditorOwners *owners_editor; @@ -143,6 +144,8 @@ private: ScriptCreateDialog *make_script_dialog_text; CreateDialog *new_resource_dialog; + bool always_show_folders; + class FileOrFolder { public: String path; @@ -169,29 +172,33 @@ private: bool initialized; bool updating_tree; + int tree_update_id; Tree *tree; //directories ItemList *files; bool import_dock_needs_update; + Ref<Texture> _get_tree_item_icon(EditorFileSystemDirectory *p_dir, int p_idx); bool _create_tree(TreeItem *p_parent, EditorFileSystemDirectory *p_dir, Vector<String> &uncollapsed_paths); - void _update_tree(bool keep_collapse_state, bool p_uncollapse_root = false); + Vector<String> _compute_uncollapsed_paths(); + void _update_tree(const Vector<String> p_uncollapsed_paths = Vector<String>(), bool p_uncollapse_root = false); - void _files_gui_input(Ref<InputEvent> p_event); + void _file_list_gui_input(Ref<InputEvent> p_event); + void _tree_gui_input(Ref<InputEvent> p_event); - void _update_files(bool p_keep_selection); + void _update_file_list(bool p_keep_selection); void _update_file_list_display_mode_button(); void _change_file_display(); void _fs_changed(); - void _go_to_tree(); - void _go_to_file_list(); + void _tree_toggle_collapsed(); - void _select_file(int p_idx); + void _select_file(const String p_path); + void _tree_activate_file(); + void _file_list_activate_file(int p_idx); void _file_multi_selected(int p_index, bool p_selected); - void _update_import_dock(); + void _tree_multi_selected(Object *p_item, int p_column, bool p_selected); - void _file_selected(); - void _dir_selected(); + void _update_import_dock(); void _get_all_items_in_dir(EditorFileSystemDirectory *efsd, Vector<String> &files, Vector<String> &folders) const; void _find_remaps(EditorFileSystemDirectory *efsd, const Map<String, String> &renames, Vector<String> &to_remaps) const; @@ -199,8 +206,8 @@ private: void _try_duplicate_item(const FileOrFolder &p_item, const String &p_new_path) const; void _update_dependencies_after_move(const Map<String, String> &p_renames) const; void _update_resource_paths_after_move(const Map<String, String> &p_renames) const; - void _update_favorite_dirs_list_after_move(const Map<String, String> &p_renames) const; - void _update_project_settings_after_move(const Map<String, String> &p_renames) const; + void _update_favorites_list_after_move(const Map<String, String> &p_files_renames, const Map<String, String> &p_folders_renames) const; + void _update_project_settings_after_move(const Map<String, String> &p_folders_renames) const; void _resource_created() const; void _make_dir_confirm(); @@ -210,8 +217,9 @@ private: bool _check_existing(); void _move_operation_confirm(const String &p_to_path, bool overwrite = false); - void _file_option(int p_option); - void _folder_option(int p_option); + void _tree_rmb_option(int p_option); + void _file_list_rmb_option(int p_option); + void _file_option(int p_option, const Vector<String> p_selected); void _fw_history(); void _bw_history(); @@ -221,13 +229,14 @@ private: void _set_scanning_mode(); void _rescan(); - void _favorites_pressed(); - void _show_current_scene_file(); - void _search_changed(const String &p_text); + void _toggle_split_mode(bool p_active); - void _dir_rmb_pressed(const Vector2 &p_pos); - void _files_list_rmb_select(int p_item, const Vector2 &p_pos); - void _rmb_pressed(const Vector2 &p_pos); + void _search_changed(const String &p_text, const Control *p_from); + + void _file_and_folders_fill_popup(PopupMenu *p_popup, Vector<String> p_paths, bool p_display_path_dependent_options = true); + void _tree_rmb_select(const Vector2 &p_pos); + void _file_list_rmb_select(int p_item, const Vector2 &p_pos); + void _file_list_rmb_pressed(const Vector2 &p_pos); struct FileInfo { String name; @@ -238,21 +247,26 @@ private: bool import_broken; bool operator<(const FileInfo &fi) const { - return name < fi.name; + return NaturalNoCaseComparator()(name, fi.name); } }; void _search(EditorFileSystemDirectory *p_path, List<FileInfo> *matches, int p_max_items); + void _set_current_path_text(const String &p_path); + 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; void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); - String _get_drag_target_folder(const Point2 &p_point, Control *p_from) const; + void _get_drag_target_folder(String &target, bool &target_favorites, const Point2 &p_point, Control *p_from) const; void _preview_invalidated(const String &p_path); - void _thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Variant &p_udata); + void _file_list_thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Ref<Texture> &p_small_preview, const Variant &p_udata); + void _tree_thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Ref<Texture> &p_small_preview, const Variant &p_udata); + + void _update_display_mode(bool p_force = false); - void _update_display_mode(); + Vector<String> _tree_get_selected(bool remove_self_inclusion = true); protected: void _notification(int p_what); diff --git a/editor/find_in_files.cpp b/editor/find_in_files.cpp index 9ede8a05bc..705bb1d9c5 100644 --- a/editor/find_in_files.cpp +++ b/editor/find_in_files.cpp @@ -29,8 +29,10 @@ /*************************************************************************/ #include "find_in_files.h" + #include "core/os/dir_access.h" #include "core/os/os.h" +#include "editor_node.h" #include "editor_scale.h" #include "scene/gui/box_container.h" #include "scene/gui/button.h" @@ -88,8 +90,6 @@ static bool find_next(const String &line, String pattern, int from, bool match_c //-------------------------------------------------------------------------------- FindInFiles::FindInFiles() { _root_prefix = ROOT_PREFIX; - _extension_filter.insert("gd"); - _extension_filter.insert("cs"); _searching = false; _whole_words = true; _match_case = true; @@ -300,9 +300,8 @@ const char *FindInFilesDialog::SIGNAL_REPLACE_REQUESTED = "replace_requested"; FindInFilesDialog::FindInFilesDialog() { - set_custom_minimum_size(Size2(400, 190)); - set_resizable(true); - set_title(TTR("Find in files")); + set_custom_minimum_size(Size2(500 * EDSCALE, 0)); + set_title(TTR("Find in Files")); VBoxContainer *vbc = memnew(VBoxContainer); vbc->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_BEGIN, 8 * EDSCALE); @@ -316,7 +315,7 @@ FindInFilesDialog::FindInFilesDialog() { vbc->add_child(gc); Label *find_label = memnew(Label); - find_label->set_text(TTR("Find: ")); + find_label->set_text(TTR("Find:")); gc->add_child(find_label); _search_text_line_edit = memnew(LineEdit); @@ -325,29 +324,24 @@ FindInFilesDialog::FindInFilesDialog() { _search_text_line_edit->connect("text_entered", this, "_on_search_text_entered"); gc->add_child(_search_text_line_edit); - { - Control *placeholder = memnew(Control); - gc->add_child(placeholder); - } + gc->add_child(memnew(Control)); // Space to mantain the grid aligned. { HBoxContainer *hbc = memnew(HBoxContainer); _whole_words_checkbox = memnew(CheckBox); - _whole_words_checkbox->set_text(TTR("Whole words")); - _whole_words_checkbox->set_pressed(true); + _whole_words_checkbox->set_text(TTR("Whole Words")); hbc->add_child(_whole_words_checkbox); _match_case_checkbox = memnew(CheckBox); - _match_case_checkbox->set_text(TTR("Match case")); - _match_case_checkbox->set_pressed(true); + _match_case_checkbox->set_text(TTR("Match Case")); hbc->add_child(_match_case_checkbox); gc->add_child(hbc); } Label *folder_label = memnew(Label); - folder_label->set_text(TTR("Folder: ")); + folder_label->set_text(TTR("Folder:")); gc->add_child(folder_label); { @@ -375,7 +369,7 @@ FindInFilesDialog::FindInFilesDialog() { } Label *filter_label = memnew(Label); - filter_label->set_text(TTR("Filter: ")); + filter_label->set_text(TTR("Filters:")); gc->add_child(filter_label); { @@ -383,7 +377,8 @@ FindInFilesDialog::FindInFilesDialog() { Vector<String> exts; exts.push_back("gd"); - exts.push_back("cs"); + if (Engine::get_singleton()->has_singleton("GodotSharp")) + exts.push_back("cs"); for (int i = 0; i < exts.size(); ++i) { CheckBox *cb = memnew(CheckBox); @@ -396,47 +391,14 @@ FindInFilesDialog::FindInFilesDialog() { gc->add_child(hbc); } - { - Control *placeholder = memnew(Control); - placeholder->set_custom_minimum_size(Size2(0, EDSCALE * 16)); - vbc->add_child(placeholder); - } - - { - HBoxContainer *hbc = memnew(HBoxContainer); - hbc->set_alignment(HBoxContainer::ALIGN_CENTER); - - _find_button = memnew(Button); - _find_button->set_text(TTR("Find...")); - _find_button->connect("pressed", this, "_on_find_button_pressed"); - _find_button->set_disabled(true); - hbc->add_child(_find_button); - - { - Control *placeholder = memnew(Control); - placeholder->set_custom_minimum_size(Size2(EDSCALE * 16, 0)); - hbc->add_child(placeholder); - } + _find_button = add_button(TTR("Find..."), false, "find"); + _find_button->set_disabled(true); - _replace_button = memnew(Button); - _replace_button->set_text(TTR("Replace...")); - _replace_button->connect("pressed", this, "_on_replace_button_pressed"); - _replace_button->set_disabled(true); - hbc->add_child(_replace_button); + _replace_button = add_button(TTR("Replace..."), false, "replace"); + _replace_button->set_disabled(true); - { - Control *placeholder = memnew(Control); - placeholder->set_custom_minimum_size(Size2(EDSCALE * 16, 0)); - hbc->add_child(placeholder); - } - - Button *cancel_button = memnew(Button); - cancel_button->set_text(TTR("Cancel")); - cancel_button->connect("pressed", this, "hide"); - hbc->add_child(cancel_button); - - vbc->add_child(hbc); - } + Button *cancel_button = get_ok(); + cancel_button->set_text(TTR("Cancel")); } void FindInFilesDialog::set_search_text(String text) { @@ -487,14 +449,14 @@ void FindInFilesDialog::_on_folder_button_pressed() { _folder_dialog->popup_centered_ratio(); } -void FindInFilesDialog::_on_find_button_pressed() { - emit_signal(SIGNAL_FIND_REQUESTED); - hide(); -} - -void FindInFilesDialog::_on_replace_button_pressed() { - emit_signal(SIGNAL_REPLACE_REQUESTED); - hide(); +void FindInFilesDialog::custom_action(const String &p_action) { + if (p_action == "find") { + emit_signal(SIGNAL_FIND_REQUESTED); + hide(); + } else if (p_action == "replace") { + emit_signal(SIGNAL_REPLACE_REQUESTED); + hide(); + } } void FindInFilesDialog::_on_search_text_modified(String text) { @@ -509,7 +471,7 @@ void FindInFilesDialog::_on_search_text_modified(String text) { void FindInFilesDialog::_on_search_text_entered(String text) { // This allows to trigger a global search without leaving the keyboard if (!_find_button->is_disabled()) - _on_find_button_pressed(); + custom_action("find"); } void FindInFilesDialog::_on_folder_selected(String path) { @@ -522,8 +484,6 @@ void FindInFilesDialog::_on_folder_selected(String path) { void FindInFilesDialog::_bind_methods() { ClassDB::bind_method("_on_folder_button_pressed", &FindInFilesDialog::_on_folder_button_pressed); - ClassDB::bind_method("_on_find_button_pressed", &FindInFilesDialog::_on_find_button_pressed); - ClassDB::bind_method("_on_replace_button_pressed", &FindInFilesDialog::_on_replace_button_pressed); ClassDB::bind_method("_on_folder_selected", &FindInFilesDialog::_on_folder_selected); ClassDB::bind_method("_on_search_text_modified", &FindInFilesDialog::_on_search_text_modified); ClassDB::bind_method("_on_search_text_entered", &FindInFilesDialog::_on_search_text_entered); @@ -558,7 +518,7 @@ FindInFilesPanel::FindInFilesPanel() { hbc->add_child(find_label); _search_text_label = memnew(Label); - _search_text_label->add_font_override("font", get_font("source", "EditorFonts")); + _search_text_label->add_font_override("font", EditorNode::get_singleton()->get_gui_base()->get_font("source", "EditorFonts")); hbc->add_child(_search_text_label); _progress_bar = memnew(ProgressBar); @@ -572,14 +532,14 @@ FindInFilesPanel::FindInFilesPanel() { _cancel_button = memnew(Button); _cancel_button->set_text(TTR("Cancel")); _cancel_button->connect("pressed", this, "_on_cancel_button_clicked"); - _cancel_button->set_disabled(true); + _cancel_button->hide(); hbc->add_child(_cancel_button); vbc->add_child(hbc); } _results_display = memnew(Tree); - _results_display->add_font_override("font", get_font("source", "EditorFonts")); + _results_display->add_font_override("font", EditorNode::get_singleton()->get_gui_base()->get_font("source", "EditorFonts")); _results_display->set_v_size_flags(SIZE_EXPAND_FILL); _results_display->connect("item_selected", this, "_on_result_selected"); _results_display->connect("item_edited", this, "_on_item_edited"); @@ -651,7 +611,7 @@ void FindInFilesPanel::start_search() { _finder->start(); update_replace_buttons(); - _cancel_button->set_disabled(false); + _cancel_button->show(); } void FindInFilesPanel::stop_search() { @@ -661,7 +621,7 @@ void FindInFilesPanel::stop_search() { _status_label->set_text(""); update_replace_buttons(); set_progress_visible(false); - _cancel_button->set_disabled(true); + _cancel_button->hide(); } void FindInFilesPanel::_notification(int p_what) { @@ -697,7 +657,7 @@ void FindInFilesPanel::_on_result_found(String fpath, int line_number, int begin // Do this first because it resets properties of the cell... item->set_cell_mode(text_index, TreeItem::CELL_MODE_CUSTOM); - String item_text = String::num_int64(line_number) + ": " + text.replace("\t", " "); + String item_text = vformat("%3s: %s", line_number, text.replace("\t", " ")); item->set_text(text_index, item_text); item->set_custom_draw(text_index, this, "_draw_result_text"); @@ -712,7 +672,7 @@ void FindInFilesPanel::_on_result_found(String fpath, int line_number, int begin r.begin = begin; r.end = end; r.draw_begin = (item_text_width - raw_text_width) + font->get_string_size(text.left(r.begin)).x; - r.draw_width = font->get_string_size(text.substr(r.begin, r.end - r.begin + 1)).x; + r.draw_width = font->get_string_size(text.substr(r.begin, r.end - r.begin)).x; _result_items[item] = r; if (_with_replace) { @@ -763,7 +723,7 @@ void FindInFilesPanel::_on_finished() { _status_label->set_text(TTR("Search complete")); update_replace_buttons(); set_progress_visible(false); - _cancel_button->set_disabled(true); + _cancel_button->hide(); } void FindInFilesPanel::_on_cancel_button_clicked() { @@ -864,7 +824,7 @@ void FindInFilesPanel::apply_replaces_in_file(String fpath, const Vector<Result> // If the file is already open, I assume the editor will reload it. // If there are unsaved changes, the user will be asked on focus, - // however that means either loosing changes or loosing replaces. + // however that means either losing changes or losing replaces. FileAccess *f = FileAccess::open(fpath, FileAccess::READ); ERR_FAIL_COND(f == NULL); diff --git a/editor/find_in_files.h b/editor/find_in_files.h index 75ea1c3161..7f37123430 100644 --- a/editor/find_in_files.h +++ b/editor/find_in_files.h @@ -91,8 +91,8 @@ class CheckBox; class FileDialog; // Prompts search parameters -class FindInFilesDialog : public WindowDialog { - GDCLASS(FindInFilesDialog, WindowDialog) +class FindInFilesDialog : public AcceptDialog { + GDCLASS(FindInFilesDialog, AcceptDialog) public: static const char *SIGNAL_FIND_REQUESTED; static const char *SIGNAL_REPLACE_REQUESTED; @@ -111,11 +111,10 @@ protected: static void _bind_methods(); void _notification(int p_what); + void custom_action(const String &p_action); private: void _on_folder_button_pressed(); - void _on_find_button_pressed(); - void _on_replace_button_pressed(); void _on_folder_selected(String path); void _on_search_text_modified(String text); void _on_search_text_entered(String text); diff --git a/editor/groups_editor.cpp b/editor/groups_editor.cpp index 0efd14e932..cb9703342f 100644 --- a/editor/groups_editor.cpp +++ b/editor/groups_editor.cpp @@ -90,12 +90,7 @@ void GroupDialog::_load_nodes(Node *p_current) { node->set_metadata(0, path); node->set_tooltip(0, path); - Ref<Texture> icon; - if (p_current->has_meta("_editor_icon")) { - icon = p_current->get_meta("_editor_icon"); - } else { - icon = get_icon((has_icon(p_current->get_class(), "EditorIcons") ? p_current->get_class() : String("Object")), "EditorIcons"); - } + Ref<Texture> icon = EditorNode::get_singleton()->get_object_icon(p_current, "Node"); node->set_icon(0, icon); if (!_can_edit(p_current, selected_group)) { diff --git a/editor/groups_editor.h b/editor/groups_editor.h index 461cf0f8c2..7705b3b6fb 100644 --- a/editor/groups_editor.h +++ b/editor/groups_editor.h @@ -31,6 +31,7 @@ #ifndef GROUPS_EDITOR_H #define GROUPS_EDITOR_H +#include "core/undo_redo.h" #include "editor/scene_tree_editor.h" #include "scene/gui/button.h" #include "scene/gui/dialogs.h" @@ -39,7 +40,6 @@ #include "scene/gui/popup.h" #include "scene/gui/tool_button.h" #include "scene/gui/tree.h" -#include "undo_redo.h" /** @author Juan Linietsky <reduzio@gmail.com> diff --git a/editor/icons/README.md b/editor/icons/README.md index 3a2aba5b07..5f652e47ab 100644 --- a/editor/icons/README.md +++ b/editor/icons/README.md @@ -9,4 +9,4 @@ There you can find the optimizer script. If you add a new icon, please make a pull request to this repo: https://github.com/godotengine/godot-design/ -and store the the optimized SVG version here. +and store the optimized SVG version here. diff --git a/editor/icons/SCsub b/editor/icons/SCsub index 31bf8f116a..109e1aa83b 100644 --- a/editor/icons/SCsub +++ b/editor/icons/SCsub @@ -1,15 +1,13 @@ #!/usr/bin/env python Import('env') + from platform_methods import run_in_subprocess import editor_icons_builders - 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"))]) - -Export('env') diff --git a/editor/icons/icon_GUI_checked.svg b/editor/icons/icon_GUI_checked.svg index e5fa67ebf5..8d00eca8d3 100644 --- a/editor/icons/icon_GUI_checked.svg +++ b/editor/icons/icon_GUI_checked.svg @@ -1,3 +1 @@ -<svg width="16" height="16" version="1.1" viewBox="0 0 16 15.999999" xmlns="http://www.w3.org/2000/svg"> -<path d="m4 2c-1.1046 0-2 0.89543-2 2v8c0 1.1046 0.89543 2 2 2h8c1.1046 0 2-0.89543 2-2v-8c0-1.1046-0.89543-2-2-2h-8zm7.293 2.293l1.4141 1.4141-6.707 6.707-2.707-2.707 1.4141-1.4141 1.293 1.293 5.293-5.293z" fill="#e0e0e0" fill-opacity=".78431"/> -</svg> +<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0"><path d="m3.3333333 1c-1.2887 0-2.3333333 1.0446683-2.3333333 2.3333333v9.3333337c0 1.2887 1.0446683 2.333333 2.3333333 2.333333h9.3333337c1.2887 0 2.333333-1.044668 2.333333-2.333333v-9.3333337c0-1.2887-1.044668-2.3333333-2.333333-2.3333333z" fill-opacity=".188235" stroke-width="1.166667"/><path d="m11.500773 3.7343508-5.6117507 5.6117502-1.7045017-1.6814543-1.4992276 1.4992276 3.2037293 3.1806817 7.1109777-7.1109775z" stroke-width="1.060227"/></g></svg>
\ No newline at end of file diff --git a/editor/icons/icon_GUI_radio_checked.svg b/editor/icons/icon_GUI_radio_checked.svg index 6a65d49eeb..447b57f8ae 100644 --- a/editor/icons/icon_GUI_radio_checked.svg +++ b/editor/icons/icon_GUI_radio_checked.svg @@ -1,6 +1 @@ -<svg width="16" height="16" version="1.1" viewBox="0 0 16 15.999999" xmlns="http://www.w3.org/2000/svg"> -<g transform="translate(0 -1036.4)"> -<circle cx="8" cy="1044.4" r="5" fill="none" stroke="#e0e0e0" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".78431" stroke-width="2"/> -<circle cx="8" cy="1044.4" r="3" fill="#e0e0e0" fill-opacity=".78431"/> -</g> -</svg> +<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0"><path d="m15 8a7 7 0 0 1 -7 7 7 7 0 0 1 -7-7 7 7 0 0 1 7-7 7 7 0 0 1 7 7" fill-opacity=".188235" stroke-width="2.333333"/><path d="m12 8a4 4 0 0 1 -4 4 4 4 0 0 1 -4-4 4 4 0 0 1 4-4 4 4 0 0 1 4 4" stroke-width="1.333333"/></g></svg>
\ No newline at end of file diff --git a/editor/icons/icon_GUI_radio_unchecked.svg b/editor/icons/icon_GUI_radio_unchecked.svg index 6e52a8af77..1e8117bd10 100644 --- a/editor/icons/icon_GUI_radio_unchecked.svg +++ b/editor/icons/icon_GUI_radio_unchecked.svg @@ -1,5 +1 @@ -<svg width="16" height="16" version="1.1" viewBox="0 0 16 15.999999" xmlns="http://www.w3.org/2000/svg"> -<g transform="translate(0 -1036.4)"> -<circle cx="8" cy="1044.4" r="5" fill="none" stroke="#e0e0e0" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".78431" stroke-width="2"/> -</g> -</svg> +<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m15 8a7 7 0 0 1 -7 7 7 7 0 0 1 -7-7 7 7 0 0 1 7-7 7 7 0 0 1 7 7" fill="#e0e0e0" fill-opacity=".188235" stroke-width="2.333333"/></svg>
\ No newline at end of file diff --git a/editor/icons/icon_GUI_unchecked.svg b/editor/icons/icon_GUI_unchecked.svg index 59df40954f..9575422df3 100644 --- a/editor/icons/icon_GUI_unchecked.svg +++ b/editor/icons/icon_GUI_unchecked.svg @@ -1,3 +1 @@ -<svg width="16" height="16" version="1.1" viewBox="0 0 16 15.999999" xmlns="http://www.w3.org/2000/svg"> -<path d="m4 2a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h8a2 2 0 0 0 2 -2v-8a2 2 0 0 0 -2 -2h-8zm0.80078 2h6.3984a0.8 0.8 0 0 1 0.80078 0.80078v6.3984a0.8 0.8 0 0 1 -0.80078 0.80078h-6.3984a0.8 0.8 0 0 1 -0.80078 -0.80078v-6.3984a0.8 0.8 0 0 1 0.80078 -0.80078z" fill="#e0e0e0" fill-opacity=".78431"/> -</svg> +<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3.3333333 1c-1.2887 0-2.3333333 1.0446683-2.3333333 2.3333333v9.3333337c0 1.2887 1.0446683 2.333333 2.3333333 2.333333h9.3333337c1.2887 0 2.333333-1.044668 2.333333-2.333333v-9.3333337c0-1.2887-1.044668-2.3333333-2.333333-2.3333333z" fill="#e0e0e0" fill-opacity=".188235" stroke-width="1.166667"/></svg>
\ No newline at end of file diff --git a/editor/icons/icon_GUI_viewport_hdiagsplitter.svg b/editor/icons/icon_GUI_viewport_hdiagsplitter.svg new file mode 100644 index 0000000000..90a0f56c43 --- /dev/null +++ b/editor/icons/icon_GUI_viewport_hdiagsplitter.svg @@ -0,0 +1,5 @@ +<svg width="64" height="34" version="1.1" viewBox="0 0 64 34" xmlns="http://www.w3.org/2000/svg"> +<g transform="rotate(90,541.2,539.2)"> +<path d="m4.0307 1048.4h29.969m-30 30v-60" fill="none" stroke="#fff" stroke-linecap="round" stroke-opacity=".39216" stroke-width="2"/> +</g> +</svg> diff --git a/editor/icons/icon_GUI_viewport_vdiagsplitter.svg b/editor/icons/icon_GUI_viewport_vdiagsplitter.svg new file mode 100644 index 0000000000..481f895d46 --- /dev/null +++ b/editor/icons/icon_GUI_viewport_vdiagsplitter.svg @@ -0,0 +1,7 @@ +<svg width="34" height="64" version="1.1" viewBox="0 0 34 64" xmlns="http://www.w3.org/2000/svg"> +<g transform="rotate(90 32.004 32.004)"> +<g transform="rotate(90,526.2,554.2)"> +<path d="m4.0307 1048.4h29.969m-30 30v-60" fill="none" stroke="#fff" stroke-linecap="round" stroke-opacity=".39216" stroke-width="2"/> +</g> +</g> +</svg> diff --git a/editor/icons/icon_GUI_viewport_vhsplitter.svg b/editor/icons/icon_GUI_viewport_vhsplitter.svg new file mode 100644 index 0000000000..52d7d8f0b7 --- /dev/null +++ b/editor/icons/icon_GUI_viewport_vhsplitter.svg @@ -0,0 +1,5 @@ +<svg width="64" height="64" version="1.1" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg"> +<g transform="rotate(90,526.2,554.2)"> +<path d="m-26 1048.4h60m-30 30v-60" fill="none" stroke="#fff" stroke-linecap="round" stroke-opacity=".39216" stroke-width="2"/> +</g> +</svg> diff --git a/editor/icons/icon_c_p_u_particles_2_d.svg b/editor/icons/icon_c_p_u_particles_2_d.svg new file mode 100644 index 0000000000..926e675fee --- /dev/null +++ b/editor/icons/icon_c_p_u_particles_2_d.svg @@ -0,0 +1,60 @@ +<?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_c_p_u_particles_2_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="1741" + inkscape:window-height="753" + id="namedview8" + showgrid="false" + inkscape:zoom="14.75" + inkscape:cx="-5.6949153" + inkscape:cy="7.7288136" + inkscape:window-x="67" + inkscape:window-y="27" + inkscape:window-maximized="0" + inkscape:current-layer="svg6" /> + <path + style="fill:#a3b6f3;fill-opacity:0.99215686" + d="m 4.5587261,0.60940813 c -0.4226244,0 -0.7617187,0.3410473 -0.7617187,0.76367177 v 0.5078126 c 0,0.1028478 0.020058,0.199689 0.056641,0.2890624 H 2.6602887 c -0.4226245,0 -0.7617188,0.3390944 -0.7617188,0.7617188 v 0.921875 C 1.8581419,3.8469787 1.821771,3.8301112 1.7794293,3.8301112 H 1.2716168 c -0.42262448,0 -0.76367188,0.3410475 -0.76367188,0.7636719 v 0.3730468 c 0,0.4226245 0.3410474,0.7617188 0.76367188,0.7617188 h 0.5078125 c 0.042396,0 0.078663,-0.016851 0.1191406,-0.023437 v 4.4531248 c -0.040428,-0.0066 -0.076799,-0.02344 -0.1191406,-0.02344 H 1.2716168 c -0.42262448,0 -0.76367188,0.341047 -0.76367188,0.763672 v 0.373047 c 0,0.422625 0.3410474,0.761718 0.76367188,0.761718 h 0.5078125 c 0.042396,0 0.078663,-0.01685 0.1191406,-0.02344 v 1.125 c 0,0.422624 0.3390944,0.763672 0.7617188,0.763672 h 1.1367187 v 0.457031 c 0,0.422624 0.3390943,0.763672 0.7617187,0.763672 H 4.931773 c 0.4226244,0 0.7636719,-0.341048 0.7636719,-0.763672 v -0.457031 h 4.4062501 v 0.457031 c 0,0.422624 0.339094,0.763672 0.761719,0.763672 h 0.373047 c 0.422624,0 0.763671,-0.341048 0.763671,-0.763672 v -0.457031 h 1.269532 c 0.422625,0 0.763672,-0.341048 0.763672,-0.763672 v -1.111328 c 0.01774,0.0012 0.03272,0.0098 0.05078,0.0098 h 0.507812 c 0.422624,0 0.763672,-0.339093 0.763672,-0.761718 v -0.373047 c 0,-0.422624 -0.341048,-0.763672 -0.763672,-0.763672 h -0.507812 c -0.01803,0 -0.03307,0.0085 -0.05078,0.0098 V 5.7187831 c 0.01774,0.00122 0.03272,0.00977 0.05078,0.00977 h 0.507812 c 0.422624,0 0.763672,-0.3390943 0.763672,-0.7617188 V 4.5937831 c 0,-0.4226244 -0.341048,-0.7636719 -0.763672,-0.7636719 h -0.507812 c -0.01803,0 -0.03307,0.00855 -0.05078,0.00977 V 2.9316737 c 0,-0.4226244 -0.341047,-0.7617187 -0.763672,-0.7617188 h -1.328125 c 0.03658,-0.089375 0.05859,-0.1862118 0.05859,-0.2890624 V 1.3730799 c 0,-0.42262437 -0.341047,-0.76367177 -0.763671,-0.76367177 h -0.373047 c -0.422625,0 -0.761719,0.3410474 -0.761719,0.76367177 v 0.5078126 c 0,0.1028478 0.02006,0.1996891 0.05664,0.2890624 H 5.6368511 C 5.6734361,2.08058 5.6954449,1.9837431 5.6954449,1.8808925 V 1.3730799 c 0,-0.42262437 -0.3410475,-0.76367177 -0.7636719,-0.76367177 z M 7.7970074,2.9668299 A 3.279661,3.6440678 0 0 1 11.009898,5.9062831 2.1864407,2.1864407 0 0 1 12.89857,8.0683925 2.1864407,2.1864407 0 0 1 10.71107,10.25394 H 4.8809918 A 2.1864407,2.1864407 0 0 1 2.6954449,8.0683925 2.1864407,2.1864407 0 0 1 4.5802105,5.9043299 3.279661,3.6440678 0 0 1 7.7970074,2.9668299 Z M 4.8809918,10.982455 A 0.72881355,0.72881355 0 0 1 5.6095074,11.710971 0.72881355,0.72881355 0 0 1 4.8809918,12.44144 0.72881355,0.72881355 0 0 1 4.1524761,11.710971 0.72881355,0.72881355 0 0 1 4.8809918,10.982455 Z m 5.8300782,0 A 0.72881355,0.72881355 0 0 1 11.441539,11.710971 0.72881355,0.72881355 0 0 1 10.71107,12.44144 0.72881355,0.72881355 0 0 1 9.9825543,11.710971 0.72881355,0.72881355 0 0 1 10.71107,10.982455 Z M 7.7970074,11.710971 A 0.72881355,0.72881355 0 0 1 8.525523,12.44144 0.72881355,0.72881355 0 0 1 7.7970074,13.169955 0.72881355,0.72881355 0 0 1 7.0684918,12.44144 0.72881355,0.72881355 0 0 1 7.7970074,11.710971 Z" + id="rect822" + inkscape:connector-curvature="0" /> + <g + inkscape:groupmode="layer" + id="layer1" + inkscape:label="Layer 1" /> +</svg> diff --git a/editor/icons/icon_g_l_e_s_2.svg b/editor/icons/icon_g_l_e_s_2.svg deleted file mode 100644 index efc4f01e4f..0000000000 --- a/editor/icons/icon_g_l_e_s_2.svg +++ /dev/null @@ -1,69 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<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" - version="1.1" - id="svg2" - width="48" - height="16" - viewBox="0 0 47.999999 16" - sodipodi:docname="icon_g_l_e_s_2.svg" - inkscape:version="0.92.3 (2405546, 2018-03-11)"> - <metadata - id="metadata8"> - <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 /> - </cc:Work> - </rdf:RDF> - </metadata> - <defs - id="defs6" /> - <sodipodi:namedview - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1" - objecttolerance="10" - gridtolerance="10" - guidetolerance="10" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:window-width="1484" - inkscape:window-height="697" - id="namedview4" - showgrid="false" - inkscape:zoom="13.520979" - inkscape:cx="20.549976" - inkscape:cy="7.9399684" - inkscape:window-x="67" - inkscape:window-y="27" - inkscape:window-maximized="0" - inkscape:current-layer="svg2" /> - <path - inkscape:connector-curvature="0" - id="path835" - d="m 19.839879,15.499154 c -0.0652,-0.0268 -0.141743,-0.1098 -0.170113,-0.184417 -0.03304,-0.08688 -0.05158,-0.95731 -0.05158,-5.912028 V 3.1830459 l 0.108486,-0.1379162 c 0.150269,-0.1910365 0.41814,-0.1907342 0.568677,6.436e-4 l 0.10899,0.1385579 -0.01358,6.2990785 c -0.01194,6.8660953 -0.0921,5.3381383 -0.0921,5.9327083 -0.106573,0.104434 -0.315006,0.142158 -0.458762,0.08303 z M 5.3808767,14.575188 C 4.5309456,14.518738 3.6260357,14.196602 2.9750499,13.718734 2.5767564,13.42636 2.0035795,12.787236 1.747789,12.350269 1.2385669,11.480363 1.0170768,10.580508 1.0213778,9.399057 1.0293972,7.2009406 1.9726797,5.5285643 3.6891526,4.6693537 4.7813316,4.1226444 6.2246017,4.0371807 7.4330177,4.4476602 8.1309525,4.6847376 8.4685433,4.8972607 9.0207129,5.4471587 9.4063328,5.8311907 9.5338898,6.0004852 9.7108978,6.3631718 9.8335428,6.6144683 9.9681328,6.9987435 10.020175,7.2461971 10.145759,7.8433551 10.170431,7.8289765 9.0218356,7.828057 8.5307356,7.8276009 8.0769363,7.8134035 8.0133918,7.7963663 7.9392662,7.7764919 7.8757344,7.6970176 7.8361313,7.5746239 7.5012661,6.5397183 6.6297764,6.0267536 5.4889128,6.193037 4.244092,6.3744711 3.4980921,7.3344965 3.343357,8.9541432 3.2260083,10.182472 3.5434132,11.329338 4.1781352,11.97041 c 0.46237,0.466997 0.9869175,0.673904 1.7084683,0.673904 1.2025378,0 1.9439704,-0.533034 2.1862936,-1.57178 0.055989,-0.240028 0.059178,-0.324448 0.012859,-0.341503 -0.033838,-0.01246 -0.5090516,-0.02871 -1.0560342,-0.03612 L 6.0352096,10.681458 V 9.8178001 8.9541431 l 1.9890278,-0.014575 c 1.0939663,-0.00802 2.0422396,-0.00163 2.1072756,0.014201 l 0.118246,0.028779 -0.01356,2.6814549 -0.01356,2.681455 -0.7170922,0.01455 c -0.8295927,0.01682 -0.7753286,0.05076 -0.8815155,-0.55106 -0.036825,-0.208719 -0.077853,-0.379487 -0.091164,-0.379487 -0.013311,0 -0.16916,0.135437 -0.3463303,0.300972 -0.3894417,0.363866 -0.8188673,0.600316 -1.3418506,0.738852 -0.4725114,0.125166 -0.8081647,0.149449 -1.4638111,0.10591 z M 32.49721,14.469781 c -0.928547,-0.194854 -1.630354,-0.56605 -2.174913,-1.150343 -0.515384,-0.552992 -0.832054,-1.344249 -0.800629,-2.000518 l 0.01549,-0.323408 1.060826,-0.01418 1.060825,-0.01418 0.05146,0.135352 c 0.0283,0.07444 0.0517,0.198593 0.05197,0.275887 8.54e-4,0.230559 0.229434,0.649361 0.479979,0.879354 0.347226,0.318744 0.735307,0.44853 1.431019,0.478576 1.267096,0.05472 2.007349,-0.393206 1.947849,-1.178652 -0.0456,-0.601928 -0.471503,-0.860841 -2.12876,-1.294103 C 32.881626,10.103917 32.242852,9.9264243 32.07283,9.8691486 30.95902,9.4939337 30.283515,8.9537559 29.97948,8.195172 29.836139,7.8375288 29.784025,7.0484225 29.874852,6.6109088 30.100606,5.5234588 31.071976,4.6456053 32.416011,4.314394 33.01697,4.1662997 34.128873,4.156633 34.77144,4.293917 c 1.67335,0.3575071 2.584333,1.270761 2.774448,2.7813655 0.0543,0.4314615 0.0347,0.4394334 -1.080484,0.4394334 -0.521251,0 -0.9851,-0.023133 -1.038665,-0.051802 C 35.367672,7.4313026 35.307808,7.3078793 35.273143,7.1462409 35.195527,6.7843357 35.099156,6.6147944 34.849667,6.4012402 34.543832,6.1394568 34.14764,6.029069 33.515213,6.0294329 c -0.428465,2.111e-4 -0.570793,0.021517 -0.784491,0.1172346 -0.47592,0.2131691 -0.654939,0.4628549 -0.654939,0.9134748 0,0.5904894 0.225799,0.7059322 2.58195,1.3200619 1.350552,0.3520209 1.903346,0.598685 2.415601,1.0778741 0.591219,0.5530567 0.852295,1.2543747 0.796412,2.1393787 -0.07929,1.255762 -0.891416,2.255747 -2.192274,2.699402 -0.885807,0.302103 -2.21918,0.374602 -3.180262,0.172924 z M 11.476954,14.306572 c -0.0138,-0.03619 -0.019,-2.268126 -0.01158,-4.9598581 l 0.0135,-4.8940567 1.066335,-0.01419 c 0.742348,-0.00988 1.088249,0.00399 1.138458,0.045665 0.06009,0.049873 0.07211,0.7135739 0.07211,3.9791612 v 3.9193056 h 2.293081 c 1.756352,0 2.314103,0.01538 2.382892,0.06567 0.07993,0.05845 0.08822,0.166396 0.07543,0.981428 l -0.01437,0.915757 -3.495384,0.01345 c -2.768549,0.0107 -3.500605,-1.69e-4 -3.520473,-0.05234 z m 10.193414,0.0026 c -0.04842,-0.04842 -0.06297,-1.193838 -0.06236,-4.9074882 4.61e-4,-2.6643823 0.01959,-4.8739347 0.04256,-4.9101166 0.03301,-0.05201 0.813774,-0.062971 3.728627,-0.052342 l 3.686862,0.013441 V 5.3948518 6.337024 l -2.5648,0.026171 -2.5648,0.026172 v 0.9421438 0.9421716 l 2.313597,0.026171 c 1.548367,0.017515 2.332217,0.044804 2.36989,0.082507 0.03673,0.036745 0.05127,0.3461819 0.04183,0.889829 l -0.01446,0.8334926 -2.355428,0.02617 -2.355429,0.02617 v 1.0992 1.099199 l 2.617143,0.0274 c 1.439428,0.01507 2.623562,0.03274 2.63141,0.03926 0.0078,0.0065 0.0078,0.441727 0,0.967118 l -0.01427,0.955257 -3.718613,0.01343 c -2.848812,0.01027 -3.733388,-0.0013 -3.781773,-0.04973 z m 17.753791,-0.378679 c -0.04061,-0.105824 0.0759,-0.828141 0.198829,-1.232689 0.288088,-0.948035 0.88431,-1.590368 2.319422,-2.498804 1.100465,-0.6965999 1.86374,-1.2293374 2.17747,-1.5198007 0.515251,-0.477031 0.731074,-1.0868265 0.620161,-1.7522036 -0.126353,-0.7579473 -0.607483,-1.1395723 -1.436711,-1.1395723 -0.930964,0 -1.401324,0.4507271 -1.481617,1.4197789 l -0.03634,0.4383927 h -1.099202 -1.099196 l -0.01524,-0.3725124 c -0.03408,-0.8332648 0.288934,-1.6827799 0.855164,-2.2490093 0.399774,-0.3997734 1.09283,-0.7574546 1.70958,-0.8822975 0.580047,-0.1174131 1.71432,-0.1077309 2.332892,0.019914 1.258364,0.2596698 2.203978,1.0560413 2.520675,2.1228587 0.104477,0.3519131 0.117355,0.4871812 0.09657,1.0144101 -0.01959,0.4962935 -0.04847,0.667451 -0.157022,0.9292002 -0.313508,0.7560998 -0.900391,1.3802206 -1.888823,2.0086882 -1.507571,0.958543 -1.915442,1.243322 -2.230808,1.557578 -0.26352,0.262604 -0.32016,0.345357 -0.261709,0.382352 0.04123,0.0261 1.061246,0.04757 2.280484,0.04802 1.96272,7.11e-4 2.209393,0.0099 2.237659,0.0836 0.01749,0.04554 0.03178,0.408703 0.03178,0.807033 0,0.398331 -0.0143,0.761495 -0.03178,0.807033 -0.0286,0.07445 -0.414152,0.0828 -3.822672,0.0828 -3.236429,0 -3.795092,-0.01093 -3.819578,-0.07475 z" - style="fill:#5586a4;fill-opacity:1;stroke-width:0.05234285" - sodipodi:nodetypes="ccscccccccccsscsscccccscccsccsccccccccccssscccccccccccccccscsccsccccsssscscccccccscscscccccccscccccccscccccccscccccccccccscccccsssccccccccscscc" /> - <path - style="fill:#000000;stroke-width:1.06666672" - d="" - id="path819" - inkscape:connector-curvature="0" /> - <path - style="fill:#000000;stroke-width:1.06666672" - d="" - id="path817" - inkscape:connector-curvature="0" /> -</svg> diff --git a/editor/icons/icon_g_l_e_s_3.svg b/editor/icons/icon_g_l_e_s_3.svg deleted file mode 100644 index dfa3c26b38..0000000000 --- a/editor/icons/icon_g_l_e_s_3.svg +++ /dev/null @@ -1,67 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<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" - version="1.1" - id="svg2" - width="48" - height="16" - viewBox="0 0 47.999999 16" - sodipodi:docname="icon_g_l_e_s_3.svg" - inkscape:version="0.92.3 (2405546, 2018-03-11)"> - <metadata - id="metadata8"> - <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="defs6" /> - <sodipodi:namedview - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1" - objecttolerance="10" - gridtolerance="10" - guidetolerance="10" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:window-width="1484" - inkscape:window-height="697" - id="namedview4" - showgrid="false" - inkscape:zoom="13.520979" - inkscape:cx="20.549976" - inkscape:cy="7.9399684" - inkscape:window-x="67" - inkscape:window-y="27" - inkscape:window-maximized="0" - inkscape:current-layer="svg2" /> - <path - style="fill:#6aa455;fill-opacity:1;stroke-width:0.05234285" - d="M 20.011719 2.9023438 C 19.90715 2.9022255 19.801697 2.9494036 19.726562 3.0449219 L 19.619141 3.1835938 L 19.619141 9.4023438 C 19.619141 14.357062 19.636882 15.227573 19.669922 15.314453 C 19.698292 15.38907 19.774644 15.4732 19.839844 15.5 C 19.9836 15.559128 20.192255 15.52045 20.298828 15.416016 C 20.298828 14.821446 20.378685 16.35047 20.390625 9.484375 L 20.404297 3.1835938 L 20.294922 3.0449219 C 20.219653 2.949233 20.116287 2.902462 20.011719 2.9023438 z M 33.578125 4.1972656 C 33.144709 4.2010336 32.716495 4.240406 32.416016 4.3144531 C 31.071981 4.6456644 30.100754 5.5238781 29.875 6.6113281 C 29.784173 7.0488418 29.835175 7.8376693 29.978516 8.1953125 C 30.282551 8.9538964 30.958456 9.4939257 32.072266 9.8691406 C 32.242288 9.9264163 32.881487 10.104023 33.492188 10.263672 C 35.149445 10.696934 35.575494 10.956666 35.621094 11.558594 C 35.680594 12.34404 34.940924 12.791048 33.673828 12.736328 C 32.978116 12.706282 32.589413 12.576557 32.242188 12.257812 C 31.991643 12.02782 31.762573 11.609465 31.761719 11.378906 C 31.761449 11.301612 31.739238 11.176002 31.710938 11.101562 L 31.658203 10.966797 L 30.597656 10.980469 L 29.537109 10.996094 L 29.521484 11.318359 C 29.490059 11.974628 29.806882 12.767321 30.322266 13.320312 C 30.866825 13.904606 31.5695 14.275849 32.498047 14.470703 C 33.459129 14.672381 34.791927 14.598978 35.677734 14.296875 C 36.978592 13.85322 37.789851 12.853418 37.869141 11.597656 C 37.925024 10.712652 37.665438 10.012041 37.074219 9.4589844 C 36.561964 8.9797953 36.008755 8.7328803 34.658203 8.3808594 C 32.302052 7.7667297 32.076172 7.6510363 32.076172 7.0605469 C 32.076172 6.609927 32.254549 6.3596535 32.730469 6.1464844 C 32.944167 6.0507668 33.08716 6.029508 33.515625 6.0292969 C 34.148052 6.028933 34.543774 6.1386072 34.849609 6.4003906 C 35.099098 6.6139448 35.195822 6.7845792 35.273438 7.1464844 C 35.308103 7.3081228 35.366714 7.4312793 35.425781 7.4628906 C 35.479346 7.4915596 35.943593 7.515625 36.464844 7.515625 C 37.580028 7.515625 37.599222 7.5076334 37.544922 7.0761719 C 37.354807 5.5655674 36.444834 4.6504759 34.771484 4.2929688 C 34.450201 4.2243268 34.011541 4.1934977 33.578125 4.1972656 z M 5.5175781 4.1992188 C 4.8691862 4.2376134 4.2355426 4.3965672 3.6894531 4.6699219 C 1.9729802 5.5291325 1.0295038 7.2003211 1.0214844 9.3984375 C 1.0171834 10.579889 1.2388248 11.479703 1.7480469 12.349609 C 2.0038374 12.786576 2.5763159 13.426376 2.9746094 13.71875 C 3.6255952 14.196618 4.5309283 14.517769 5.3808594 14.574219 C 6.0365058 14.617758 6.3712386 14.593916 6.84375 14.46875 C 7.3667333 14.330214 7.7980583 14.094335 8.1875 13.730469 C 8.3646703 13.564934 8.5198921 13.429688 8.5332031 13.429688 C 8.5465141 13.429688 8.588175 13.599875 8.625 13.808594 C 8.7311869 14.410414 8.6762667 14.376195 9.5058594 14.359375 L 10.222656 14.345703 L 10.236328 11.664062 L 10.25 8.9824219 L 10.130859 8.953125 C 10.065823 8.937294 9.1174038 8.9314331 8.0234375 8.9394531 L 6.0351562 8.9550781 L 6.0351562 9.8183594 L 6.0351562 10.681641 L 7.0292969 10.695312 C 7.5762795 10.702722 8.0520995 10.718009 8.0859375 10.730469 C 8.1322565 10.747524 8.1282546 10.832238 8.0722656 11.072266 C 7.8299424 12.111012 7.0892565 12.644531 5.8867188 12.644531 C 5.1651679 12.644531 4.6401044 12.4377 4.1777344 11.970703 C 3.5430124 11.329631 3.2264013 10.183407 3.34375 8.9550781 C 3.4984851 7.3354314 4.2434605 6.3747935 5.4882812 6.1933594 C 6.6291449 6.027076 7.5010723 6.5393131 7.8359375 7.5742188 C 7.8755406 7.6966124 7.9395463 7.7770006 8.0136719 7.796875 C 8.0772164 7.8139122 8.5303844 7.8276689 9.0214844 7.828125 C 10.17008 7.8290445 10.145115 7.8432518 10.019531 7.2460938 C 9.967489 6.9986401 9.8335825 6.6145778 9.7109375 6.3632812 C 9.5339295 6.0005947 9.4071043 5.8312976 9.0214844 5.4472656 C 8.4693148 4.8973676 8.1315285 4.684343 7.4335938 4.4472656 C 6.8293858 4.2420259 6.16597 4.1608241 5.5175781 4.1992188 z M 42.03125 4.2617188 L 41.537109 4.4335938 C 40.933232 4.6433398 40.657695 4.8014669 40.300781 5.1386719 C 39.969225 5.4519119 39.761404 5.8046136 39.621094 6.2910156 C 39.502474 6.7023596 39.433137 7.3494687 39.498047 7.4492188 C 39.531044 7.4999487 39.783863 7.5127831 40.576172 7.5019531 L 41.611328 7.4863281 L 41.691406 7.0703125 C 41.808812 6.4678105 41.927622 6.2685471 42.265625 6.0957031 C 42.510424 5.9705181 42.604184 5.953125 43.019531 5.953125 C 43.426321 5.953125 43.533311 5.9721266 43.765625 6.0878906 C 44.253715 6.3311276 44.455638 6.904517 44.273438 7.53125 C 44.105442 8.109131 43.697334 8.363965 42.791016 8.453125 C 42.521874 8.479605 42.288464 8.51424 42.271484 8.53125 C 42.225224 8.577174 42.232777 9.7874244 42.279297 9.8339844 C 42.301291 9.8559744 42.598053 9.8907794 42.939453 9.9121094 C 43.836652 9.9681724 44.239534 10.166191 44.525391 10.691406 C 44.916028 11.409137 44.561069 12.318315 43.787109 12.582031 C 43.476088 12.688024 42.767292 12.688624 42.470703 12.583984 C 42.00204 12.418633 41.795632 12.174325 41.642578 11.597656 L 41.560547 11.285156 L 40.46875 11.285156 L 39.376953 11.285156 L 39.361328 11.527344 C 39.352678 11.660649 39.384791 11.918152 39.431641 12.099609 C 39.739925 13.294376 40.783209 14.156157 42.212891 14.396484 C 42.284425 14.408514 42.682741 14.422181 43.097656 14.425781 C 44.074936 14.434074 44.653306 14.320796 45.308594 13.996094 C 46.07786 13.61492 46.610204 13.058412 46.847656 12.382812 C 47.087412 11.700564 47.08166 10.999125 46.833984 10.333984 C 46.695621 9.962377 46.130198 9.3782416 45.6875 9.1503906 C 45.523031 9.0657476 45.386773 8.9810006 45.386719 8.9628906 C 45.386654 8.9447846 45.539488 8.8195027 45.724609 8.6835938 C 46.129744 8.3861558 46.390215 8.064434 46.53125 7.6875 C 46.963216 6.532963 46.370297 5.2063894 45.166016 4.6308594 C 44.482944 4.3044164 44.23589 4.2611938 43.072266 4.2617188 L 42.03125 4.2617188 z M 12.544922 4.4375 L 11.478516 4.453125 L 11.464844 9.3476562 C 11.457424 12.039388 11.462763 14.270451 11.476562 14.306641 C 11.49643 14.358812 12.229498 14.370075 14.998047 14.359375 L 18.492188 14.345703 L 18.507812 13.429688 C 18.520602 12.614656 18.511571 12.507669 18.431641 12.449219 C 18.362852 12.398929 17.80518 12.382812 16.048828 12.382812 L 13.755859 12.382812 L 13.755859 8.4628906 C 13.755859 5.1973033 13.743684 4.534248 13.683594 4.484375 C 13.633385 4.4427 13.28727 4.42762 12.544922 4.4375 z M 25.378906 4.4394531 C 22.464053 4.4288241 21.683401 4.4401775 21.650391 4.4921875 C 21.627421 4.5283694 21.607883 6.7379615 21.607422 9.4023438 C 21.606812 13.115994 21.621502 14.260174 21.669922 14.308594 C 21.718307 14.357024 22.60236 14.369645 25.451172 14.359375 L 29.169922 14.345703 L 29.185547 13.390625 C 29.193347 12.865234 29.193347 12.430328 29.185547 12.423828 C 29.177699 12.417308 27.992162 12.399836 26.552734 12.384766 L 23.935547 12.355469 L 23.935547 11.257812 L 23.935547 10.158203 L 26.291016 10.132812 L 28.646484 10.105469 L 28.662109 9.2714844 C 28.671549 8.7278373 28.655871 8.4195575 28.619141 8.3828125 C 28.581468 8.3451095 27.798367 8.3182962 26.25 8.3007812 L 23.935547 8.2734375 L 23.935547 7.3320312 L 23.935547 6.3886719 L 26.501953 6.3632812 L 29.066406 6.3378906 L 29.066406 5.3945312 L 29.066406 4.453125 L 25.378906 4.4394531 z " - id="path3424" /> - <path - style="fill:#000000;stroke-width:1.06666672" - d="" - id="path819" - inkscape:connector-curvature="0" /> - <path - style="fill:#000000;stroke-width:1.06666672" - d="" - id="path817" - inkscape:connector-curvature="0" /> -</svg> diff --git a/editor/icons/icon_noise_texture.svg b/editor/icons/icon_noise_texture.svg new file mode 100644 index 0000000000..5908c2b2d4 --- /dev/null +++ b/editor/icons/icon_noise_texture.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="m2 1c-0.55228 0-1 0.44772-1 1v12c0 0.55228 0.44772 1 1 1h12c0.55228 0 1-0.44772 1-1v-12c0-0.55228-0.44772-1-1-1zm1 2h10v8h-10zm3 1v2h2v-2zm2 2v2h2v2h2v-6h-2v2zm0 2h-2v-2h-2v4h4z" fill="#e0e0e0" fill-opacity=".99608"/> +</svg> diff --git a/editor/icons/icon_script_extend.svg b/editor/icons/icon_script_extend.svg new file mode 100644 index 0000000000..ef3d48af9f --- /dev/null +++ b/editor/icons/icon_script_extend.svg @@ -0,0 +1,8 @@ +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<g transform="translate(0 -1036.4)"> +<path transform="translate(0 1036.4)" d="m6 1v1c-0.55228 0-1 0.44772-1 1v10h-1v-2h-2v2c2.826e-4 0.35698 0.19084 0.68674 0.5 0.86523 0.15194 0.088045 0.32439 0.13452 0.5 0.13477v1h7c0.73866 0 1.3763-0.40437 1.7227-1h-3.7227v-4h4v-5h3v-2c0-1.1046-0.89543-2-2-2z" fill="#e0e0e0"/> +<path transform="translate(0 1036.4)" d="m6 1c-1.1046 0-2 0.89543-2 2v7h-2-1v1 2c0 1.1046 0.89543 2 2 2s2-0.89543 2-2v-10c0-0.55228 0.44772-1 1-1s1 0.44772 1 1v1 1 1h1 4v-1h-4v-1-1c0-1.1046-0.89543-2-2-2zm-4 10h2v2c0 0.55228-0.44772 1-1 1s-1-0.44772-1-1v-2z" fill="#b4b4b4"/> +<circle cx="3" cy="1048.4" rx="1" ry="1" fill="#e0e0e0"/> +<path d="m16 1048.4-3-3v2h-4v2h4v2z" fill="#68b6ff" fill-rule="evenodd"/> +</g> +</svg> diff --git a/editor/icons/icon_text_file.svg b/editor/icons/icon_text_file.svg new file mode 100644 index 0000000000..342a407b79 --- /dev/null +++ b/editor/icons/icon_text_file.svg @@ -0,0 +1,57 @@ +<?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_text_file.svg" + inkscape:version="0.92.2 2405546, 2018-03-11" + enable-background="new"> + <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="2560" + inkscape:window-height="1440" + id="namedview10" + showgrid="false" + inkscape:zoom="64" + inkscape:cx="-0.11275433" + inkscape:cy="5.0633688" + inkscape:window-x="1920" + inkscape:window-y="0" + inkscape:window-maximized="0" + inkscape:current-layer="svg8" + inkscape:snap-grids="true" /> + <path + style="display:inline;fill:#e0e0e0" + d="m 13.370548,12.198712 c 0.359546,-0.0075 0.719092,-0.015 1.078638,-0.0225 -0.004,-0.738576 -0.008,-1.477152 -0.01198,-2.215728 -1.429703,0.011985 -2.859406,0.02397 -4.289109,0.035955 0.004,0.759672 0.008,1.519344 0.01198,2.279016 0.40349,-0.01135 0.806981,-0.02271 1.210471,-0.03406 0,1.251681 0,2.503363 0,3.755044 0.666667,0 1.333333,0 2,0 M 6.1005477,12.247152 c 0.332722,0.21587 0.665444,0.431741 0.998166,0.647611 -0.3328629,0.218648 -0.6657258,0.437297 -0.9985887,0.655945 -1e-7,0.818044 -2e-7,1.636088 -3e-7,2.454132 0.5662705,-0.533749 1.1325409,-1.067498 1.6988114,-1.601247 0.6353035,0.532396 1.2706071,1.064791 1.9059106,1.597187 -9.5e-4,-0.757409 -0.0019,-1.514817 -0.00285,-2.272226 -0.2987204,-0.278501 -0.5974407,-0.557002 -0.8961611,-0.835503 0.2983766,-0.205775 0.5967531,-0.41155 0.8951297,-0.617325 0.00283,-0.73844 0.00565,-1.476881 0.00848,-2.215321 -0.63732,0.474447 -1.27464,0.948893 -1.91196,1.42334 C 7.2318406,10.979446 6.6661958,10.475146 6.1005511,9.9708468 M 4.6399123,12.202271 c 0.3595459,-0.0075 0.7190917,-0.015 1.0786376,-0.0225 -0.00399,-0.738576 -0.00799,-1.477152 -0.011985,-2.2157276 -1.4297028,0.011985 -2.8594057,0.02397 -4.2891085,0.035955 0.00399,0.7596716 0.00799,1.5193436 0.011985,2.2790156 0.4034903,-0.01135 0.8069806,-0.02271 1.2104709,-0.03406 0,1.251681 0,2.503363 0,3.755044 0.6666667,0 1.3333333,0 2,0 M 7,1 C 6.81185,1.7526 6.6237,2.5052 6.43555,3.2578 6.0521572,3.3957205 5.6943609,3.6619566 5.3589944,3.3047548 4.8252629,2.9844032 4.2915315,2.6640516 3.7578,2.3437 3.2864333,2.8150667 2.8150667,3.2864333 2.3437,3.7578 2.7421333,4.4225 3.1405667,5.0872 3.539,5.7519 3.3683054,6.121632 3.3058712,6.5625877 2.8157946,6.5467719 2.2105097,6.6978312 1.6052249,6.8488906 0.99994,6.99995 c 0,0.6666667 0,1.3333333 0,2 1.7571667,0 3.5143333,0 5.2715,0 C 5.5845118,7.9199003 6.2580962,6.3373839 7.5001288,6.0629153 8.7083679,5.7047153 10.045643,6.7406952 9.99996,7.99995 c 0.104409,0.4657408 -0.6052318,1.1778026 0.181951,1 1.606006,0 3.212013,0 4.818019,0 0,-0.6666667 0,-1.3333333 0,-2 C 14.24733,6.8118 13.49473,6.62365 12.74213,6.4355 12.603459,6.0528244 12.33852,5.6958457 12.695012,5.3607965 13.015418,4.8264643 13.335824,4.2921322 13.65623,3.7578 13.184863,3.2864333 12.713497,2.8150667 12.24213,2.3437 11.57743,2.7421333 10.91273,3.1405667 10.24803,3.539 9.8782981,3.3683053 9.4373423,3.3058712 9.4531581,2.8157946 9.3020988,2.2105097 9.1510394,1.6052249 8.99998,0.99994 8.3333478,0.99998002 7.6664935,0.99985998 7,1 Z" + id="path4781-7" + inkscape:connector-curvature="0" /> +</svg> diff --git a/editor/import/SCsub b/editor/import/SCsub index f1fa50148f..2b1e889fb0 100644 --- a/editor/import/SCsub +++ b/editor/import/SCsub @@ -1,5 +1,5 @@ #!/usr/bin/env python Import('env') -Export('env') + env.add_source_files(env.editor_sources, "*.cpp") diff --git a/editor/import/editor_import_collada.cpp b/editor/import/editor_import_collada.cpp index d4bd4f85e6..e8bb772a64 100644 --- a/editor/import/editor_import_collada.cpp +++ b/editor/import/editor_import_collada.cpp @@ -30,9 +30,9 @@ #include "editor_import_collada.h" +#include "core/os/os.h" #include "editor/collada/collada.h" #include "editor/editor_node.h" -#include "os/os.h" #include "scene/3d/camera.h" #include "scene/3d/light.h" #include "scene/3d/mesh_instance.h" @@ -321,7 +321,7 @@ Error ColladaImport::_create_scene(Collada::Node *p_node, Spatial *p_parent) { } else { //mesh since nothing else node = memnew(MeshInstance); - Object::cast_to<MeshInstance>(node)->set_flag(GeometryInstance::FLAG_USE_BAKED_LIGHT, true); + //Object::cast_to<MeshInstance>(node)->set_flag(GeometryInstance::FLAG_USE_BAKED_LIGHT, true); } } break; case Collada::Node::TYPE_SKELETON: { @@ -490,120 +490,6 @@ Error ColladaImport::_create_material(const String &p_target) { return OK; } -static void _generate_normals(const PoolVector<int> &p_indices, const PoolVector<Vector3> &p_vertices, PoolVector<Vector3> &r_normals) { - - r_normals.resize(p_vertices.size()); - PoolVector<Vector3>::Write narrayw = r_normals.write(); - - int iacount = p_indices.size() / 3; - PoolVector<int>::Read index_arrayr = p_indices.read(); - PoolVector<Vector3>::Read vertex_arrayr = p_vertices.read(); - - for (int idx = 0; idx < iacount; idx++) { - - Vector3 v[3] = { - vertex_arrayr[index_arrayr[idx * 3 + 0]], - vertex_arrayr[index_arrayr[idx * 3 + 1]], - vertex_arrayr[index_arrayr[idx * 3 + 2]] - }; - - Vector3 normal = Plane(v[0], v[1], v[2]).normal; - - narrayw[index_arrayr[idx * 3 + 0]] += normal; - narrayw[index_arrayr[idx * 3 + 1]] += normal; - narrayw[index_arrayr[idx * 3 + 2]] += normal; - } - - int vlen = p_vertices.size(); - - for (int idx = 0; idx < vlen; idx++) { - narrayw[idx].normalize(); - } -} - -static void _generate_tangents_and_binormals(const PoolVector<int> &p_indices, const PoolVector<Vector3> &p_vertices, const PoolVector<Vector3> &p_uvs, const PoolVector<Vector3> &p_normals, PoolVector<real_t> &r_tangents) { - - int vlen = p_vertices.size(); - - Vector<Vector3> tangents; - tangents.resize(vlen); - Vector<Vector3> binormals; - binormals.resize(vlen); - - int iacount = p_indices.size() / 3; - - PoolVector<int>::Read index_arrayr = p_indices.read(); - PoolVector<Vector3>::Read vertex_arrayr = p_vertices.read(); - PoolVector<Vector3>::Read narrayr = p_normals.read(); - PoolVector<Vector3>::Read uvarrayr = p_uvs.read(); - - for (int idx = 0; idx < iacount; idx++) { - - Vector3 v1 = vertex_arrayr[index_arrayr[idx * 3 + 0]]; - Vector3 v2 = vertex_arrayr[index_arrayr[idx * 3 + 1]]; - Vector3 v3 = vertex_arrayr[index_arrayr[idx * 3 + 2]]; - - Vector3 w1 = uvarrayr[index_arrayr[idx * 3 + 0]]; - Vector3 w2 = uvarrayr[index_arrayr[idx * 3 + 1]]; - Vector3 w3 = uvarrayr[index_arrayr[idx * 3 + 2]]; - - real_t x1 = v2.x - v1.x; - real_t x2 = v3.x - v1.x; - real_t y1 = v2.y - v1.y; - real_t y2 = v3.y - v1.y; - real_t z1 = v2.z - v1.z; - real_t z2 = v3.z - v1.z; - - real_t s1 = w2.x - w1.x; - real_t s2 = w3.x - w1.x; - real_t t1 = w2.y - w1.y; - real_t t2 = w3.y - w1.y; - - real_t r = (s1 * t2 - s2 * t1); - - Vector3 tangent; - Vector3 binormal; - - if (r == 0) { - - binormal = Vector3(); - tangent = Vector3(); - } else { - tangent = Vector3((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, - (t2 * z1 - t1 * z2) * r) - .normalized(); - binormal = Vector3((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, - (s1 * z2 - s2 * z1) * r) - .normalized(); - } - - 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; - } - - r_tangents.resize(vlen * 4); - PoolVector<real_t>::Write tarrayw = r_tangents.write(); - - for (int idx = 0; idx < vlen; idx++) { - Vector3 tangent = tangents[idx]; - Vector3 bingen = narrayr[idx].cross(tangent); - float dir; - if (bingen.dot(binormals[idx]) < 0) - dir = -1.0; - else - dir = +1.0; - - tarrayw[idx * 4 + 0] = tangent.x; - tarrayw[idx * 4 + 1] = tangent.y; - tarrayw[idx * 4 + 2] = tangent.z; - tarrayw[idx * 4 + 3] = dir; - } -} - Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_mesh, const Map<String, Collada::NodeGeometry::Material> &p_material_map, const Collada::MeshData &meshdata, const Transform &p_local_xform, const Vector<int> &bone_remap, const Collada::SkinControllerData *p_skin_controller, const Collada::MorphControllerData *p_morph_data, Vector<Ref<ArrayMesh> > p_morph_meshes, bool p_use_compression, bool p_use_mesh_material) { bool local_xform_mirror = p_local_xform.basis.determinant() < 0; diff --git a/editor/import/editor_import_plugin.h b/editor/import/editor_import_plugin.h index 92d83158ef..b850c0605b 100644 --- a/editor/import/editor_import_plugin.h +++ b/editor/import/editor_import_plugin.h @@ -31,7 +31,7 @@ #ifndef EDITOR_IMPORT_PLUGIN_H #define EDITOR_IMPORT_PLUGIN_H -#include "io/resource_import.h" +#include "core/io/resource_import.h" class EditorImportPlugin : public ResourceImporter { GDCLASS(EditorImportPlugin, Reference) diff --git a/editor/import/editor_scene_importer_gltf.cpp b/editor/import/editor_scene_importer_gltf.cpp index 4d5c292847..cb15be35f2 100644 --- a/editor/import/editor_scene_importer_gltf.cpp +++ b/editor/import/editor_scene_importer_gltf.cpp @@ -29,10 +29,10 @@ /*************************************************************************/ #include "editor_scene_importer_gltf.h" -#include "io/json.h" -#include "math_defs.h" -#include "os/file_access.h" -#include "os/os.h" +#include "core/io/json.h" +#include "core/math/math_defs.h" +#include "core/os/file_access.h" +#include "core/os/os.h" #include "scene/3d/camera.h" #include "scene/3d/mesh_instance.h" #include "scene/animation/animation_player.h" @@ -793,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.write[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]).normalized(); } } return ret; @@ -1793,17 +1793,24 @@ template <> struct EditorSceneImporterGLTFInterpolate<Quat> { Quat lerp(const Quat &a, const Quat &b, float c) const { + ERR_FAIL_COND_V(!a.is_normalized(), Quat()); + ERR_FAIL_COND_V(!b.is_normalized(), Quat()); - return a.slerp(b, c); + return a.slerp(b, c).normalized(); } Quat catmull_rom(const Quat &p0, const Quat &p1, const Quat &p2, const Quat &p3, float c) { + ERR_FAIL_COND_V(!p1.is_normalized(), Quat()); + ERR_FAIL_COND_V(!p2.is_normalized(), Quat()); - return p1.slerp(p2, c); + return p1.slerp(p2, c).normalized(); } Quat bezier(Quat start, Quat control_1, Quat control_2, Quat end, float t) { - return start.slerp(end, t); + ERR_FAIL_COND_V(!start.is_normalized(), Quat()); + ERR_FAIL_COND_V(!end.is_normalized(), Quat()); + + return start.slerp(end, t).normalized(); } }; @@ -1869,9 +1876,9 @@ T EditorSceneImporterGLTF::_interpolate_track(const Vector<float> &p_times, cons float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]); T from = p_values[idx * 3 + 1]; - T c1 = from + p_values[idx * 3 + 0]; - T to = p_values[idx * 3 + 3]; - T c2 = to + p_values[idx * 3 + 2]; + T c1 = from + p_values[idx * 3 + 2]; + T to = p_values[idx * 3 + 4]; + T c2 = to + p_values[idx * 3 + 3]; return interp.bezier(from, c1, c2, to, c); @@ -1903,15 +1910,15 @@ void EditorSceneImporterGLTF::_import_animation(GLTFState &state, AnimationPlaye NodePath node_path; GLTFNode *node = state.nodes[E->key()]; - for (int i = 0; i < node->godot_nodes.size(); i++) { + for (int n = 0; n < node->godot_nodes.size(); n++) { if (node->joints.size()) { - Skeleton *sk = (Skeleton *)node->godot_nodes[i]; + Skeleton *sk = (Skeleton *)node->godot_nodes[n]; String path = ap->get_parent()->get_path_to(sk); - String bone = sk->get_bone_name(node->joints[i].godot_bone_index); + String bone = sk->get_bone_name(node->joints[n].godot_bone_index); node_path = path + ":" + bone; } else { - node_path = ap->get_parent()->get_path_to(node->godot_nodes[i]); + node_path = ap->get_parent()->get_path_to(node->godot_nodes[n]); } for (int i = 0; i < track.rotation_track.times.size(); i++) { @@ -1945,7 +1952,7 @@ void EditorSceneImporterGLTF::_import_animation(GLTFState &state, AnimationPlaye Vector3 base_scale = Vector3(1, 1, 1); if (!track.rotation_track.values.size()) { - base_rot = state.nodes[E->key()]->rotation; + base_rot = state.nodes[E->key()]->rotation.normalized(); } if (!track.translation_track.values.size()) { @@ -1986,11 +1993,12 @@ void EditorSceneImporterGLTF::_import_animation(GLTFState &state, AnimationPlaye xform.basis.set_quat_scale(rot, scale); xform.origin = pos; - Skeleton *skeleton = skeletons[node->joints[i].skin]; - int bone = node->joints[i].godot_bone_index; + Skeleton *skeleton = skeletons[node->joints[n].skin]; + int bone = node->joints[n].godot_bone_index; xform = skeleton->get_bone_rest(bone).affine_inverse() * xform; rot = xform.basis.get_rotation_quat(); + rot.normalize(); scale = xform.basis.get_scale(); pos = xform.origin; } diff --git a/editor/import/resource_importer_bitmask.cpp b/editor/import/resource_importer_bitmask.cpp index 7b330936f6..917d6d1bcc 100644 --- a/editor/import/resource_importer_bitmask.cpp +++ b/editor/import/resource_importer_bitmask.cpp @@ -30,10 +30,10 @@ #include "resource_importer_bitmask.h" #include "core/image.h" +#include "core/io/config_file.h" +#include "core/io/image_loader.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/bit_mask.h" #include "scene/resources/texture.h" diff --git a/editor/import/resource_importer_bitmask.h b/editor/import/resource_importer_bitmask.h index f3537df819..1b97152099 100644 --- a/editor/import/resource_importer_bitmask.h +++ b/editor/import/resource_importer_bitmask.h @@ -31,8 +31,8 @@ #ifndef RESOURCE_IMPORTER_BITMASK_H #define RESOURCE_IMPORTER_BITMASK_H -#include "image.h" -#include "io/resource_import.h" +#include "core/image.h" +#include "core/io/resource_import.h" class StreamBitMap; diff --git a/editor/import/resource_importer_csv_translation.cpp b/editor/import/resource_importer_csv_translation.cpp index cf850eef03..e7f9e1afe6 100644 --- a/editor/import/resource_importer_csv_translation.cpp +++ b/editor/import/resource_importer_csv_translation.cpp @@ -30,10 +30,10 @@ #include "resource_importer_csv_translation.h" -#include "compressed_translation.h" -#include "io/resource_saver.h" -#include "os/file_access.h" -#include "translation.h" +#include "core/compressed_translation.h" +#include "core/io/resource_saver.h" +#include "core/os/file_access.h" +#include "core/translation.h" String ResourceImporterCSVTranslation::get_importer_name() const { diff --git a/editor/import/resource_importer_csv_translation.h b/editor/import/resource_importer_csv_translation.h index f5f230c6bd..370c182f65 100644 --- a/editor/import/resource_importer_csv_translation.h +++ b/editor/import/resource_importer_csv_translation.h @@ -31,7 +31,7 @@ #ifndef RESOURCEIMPORTERCSVTRANSLATION_H #define RESOURCEIMPORTERCSVTRANSLATION_H -#include "io/resource_import.h" +#include "core/io/resource_import.h" class ResourceImporterCSVTranslation : public ResourceImporter { GDCLASS(ResourceImporterCSVTranslation, ResourceImporter) diff --git a/editor/import/resource_importer_image.cpp b/editor/import/resource_importer_image.cpp index b6a67c0cd3..923a9a20ec 100644 --- a/editor/import/resource_importer_image.cpp +++ b/editor/import/resource_importer_image.cpp @@ -1,8 +1,38 @@ +/*************************************************************************/ +/* resource_importer_image.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 "resource_importer_image.h" -#include "io/image_loader.h" -#include "io/resource_saver.h" -#include "os/file_access.h" +#include "core/io/image_loader.h" +#include "core/io/resource_saver.h" +#include "core/os/file_access.h" #include "scene/resources/texture.h" String ResourceImporterImage::get_importer_name() const { diff --git a/editor/import/resource_importer_image.h b/editor/import/resource_importer_image.h index 5aadd00a35..d282ac482d 100644 --- a/editor/import/resource_importer_image.h +++ b/editor/import/resource_importer_image.h @@ -1,8 +1,38 @@ +/*************************************************************************/ +/* resource_importer_image.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 RESOURCE_IMPORTER_IMAGE_H #define RESOURCE_IMPORTER_IMAGE_H -#include "image.h" -#include "io/resource_import.h" +#include "core/image.h" +#include "core/io/resource_import.h" class ResourceImporterImage : public ResourceImporter { GDCLASS(ResourceImporterImage, ResourceImporter) diff --git a/editor/import/resource_importer_layered_texture.cpp b/editor/import/resource_importer_layered_texture.cpp index 211bb7bcc9..afda07c1c2 100644 --- a/editor/import/resource_importer_layered_texture.cpp +++ b/editor/import/resource_importer_layered_texture.cpp @@ -1,11 +1,41 @@ +/*************************************************************************/ +/* resource_importer_layered_texture.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 "resource_importer_layered_texture.h" #include "resource_importer_texture.h" +#include "core/io/config_file.h" +#include "core/io/image_loader.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 { @@ -52,6 +82,7 @@ String ResourceImporterLayeredTexture::get_preset_name(int p_idx) const { void ResourceImporterLayeredTexture::get_import_options(List<ImportOption> *r_options, int p_preset) const { r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Lossless,Video RAM,Uncompressed", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), p_preset == PRESET_3D ? 1 : 0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compress/no_bptc_if_rgb"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "flags/repeat", PROPERTY_HINT_ENUM, "Disabled,Enabled,Mirrored"), 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "flags/filter"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "flags/mipmaps"), p_preset == PRESET_COLOR_CORRECT ? 0 : 1)); @@ -273,7 +304,7 @@ Error ResourceImporterLayeredTexture::import(const String &p_source_file, const } 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."); + EditorNode::add_io_error("Warning, no suitable PC VRAM compression enabled in Project Settings. This texture will not display correctly on PC."); } } else { //import normally diff --git a/editor/import/resource_importer_layered_texture.h b/editor/import/resource_importer_layered_texture.h index ec73b2624d..a4b83bf56c 100644 --- a/editor/import/resource_importer_layered_texture.h +++ b/editor/import/resource_importer_layered_texture.h @@ -1,8 +1,38 @@ +/*************************************************************************/ +/* resource_importer_layered_texture.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 RESOURCE_IMPORTER_LAYERED_TEXTURE_H #define RESOURCE_IMPORTER_LAYERED_TEXTURE_H -#include "image.h" -#include "io/resource_import.h" +#include "core/image.h" +#include "core/io/resource_import.h" class StreamTexture; diff --git a/editor/import/resource_importer_obj.cpp b/editor/import/resource_importer_obj.cpp index 3f101cd04d..c237d2e854 100644 --- a/editor/import/resource_importer_obj.cpp +++ b/editor/import/resource_importer_obj.cpp @@ -30,8 +30,8 @@ #include "resource_importer_obj.h" -#include "io/resource_saver.h" -#include "os/file_access.h" +#include "core/io/resource_saver.h" +#include "core/os/file_access.h" #include "scene/3d/mesh_instance.h" #include "scene/3d/spatial.h" #include "scene/resources/mesh.h" @@ -126,7 +126,12 @@ static Error _parse_material_library(const String &p_path, Map<String, Ref<Spati ERR_FAIL_COND_V(current.is_null(), ERR_FILE_CORRUPT); String p = l.replace("map_Kd", "").replace("\\", "/").strip_edges(); - String path = base_path.plus_file(p); + String path; + if (p.is_abs_path()) { + path = p; + } else { + path = base_path.plus_file(p); + } Ref<Texture> texture = ResourceLoader::load(path); @@ -141,7 +146,12 @@ static Error _parse_material_library(const String &p_path, Map<String, Ref<Spati ERR_FAIL_COND_V(current.is_null(), ERR_FILE_CORRUPT); String p = l.replace("map_Ks", "").replace("\\", "/").strip_edges(); - String path = base_path.plus_file(p); + String path; + if (p.is_abs_path()) { + path = p; + } else { + path = base_path.plus_file(p); + } Ref<Texture> texture = ResourceLoader::load(path); @@ -156,7 +166,12 @@ static Error _parse_material_library(const String &p_path, Map<String, Ref<Spati ERR_FAIL_COND_V(current.is_null(), ERR_FILE_CORRUPT); String p = l.replace("map_Ns", "").replace("\\", "/").strip_edges(); - String path = base_path.plus_file(p); + String path; + if (p.is_abs_path()) { + path = p; + } else { + path = base_path.plus_file(p); + } Ref<Texture> texture = ResourceLoader::load(path); diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index f544811eb0..c5a5980fc1 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -30,8 +30,8 @@ #include "resource_importer_scene.h" +#include "core/io/resource_saver.h" #include "editor/editor_node.h" -#include "io/resource_saver.h" #include "scene/resources/packed_scene.h" #include "scene/3d/collision_shape.h" @@ -365,29 +365,37 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Array return p_node; MeshInstance *mi = Object::cast_to<MeshInstance>(p_node); if (mi) { - Node *col; + Node *col = NULL; if (_teststr(name, "colonly")) { col = mi->create_trimesh_collision_node(); - ERR_FAIL_COND_V(!col, NULL); + if (col == NULL) { + ERR_PRINTS("Error generating collision for mesh: " + name); + } else { - col->set_name(_fixstr(name, "colonly")); + col->set_name(_fixstr(name, "colonly")); + } } else { col = mi->create_convex_collision_node(); - ERR_FAIL_COND_V(!col, NULL); + if (col == NULL) { + ERR_PRINTS("Error generating collision for mesh: " + name); + } else { - col->set_name(_fixstr(name, "convcolonly")); + col->set_name(_fixstr(name, "convcolonly")); + } } - Object::cast_to<Spatial>(col)->set_transform(mi->get_transform()); - p_node->replace_by(col); - memdelete(p_node); - p_node = col; + if (col) { + Object::cast_to<Spatial>(col)->set_transform(mi->get_transform()); + p_node->replace_by(col); + memdelete(p_node); + p_node = col; - StaticBody *sb = Object::cast_to<StaticBody>(col); - CollisionShape *colshape = Object::cast_to<CollisionShape>(sb->get_child(0)); - colshape->set_name("shape"); - colshape->set_owner(p_node->get_owner()); + StaticBody *sb = Object::cast_to<StaticBody>(col); + CollisionShape *colshape = Object::cast_to<CollisionShape>(sb->get_child(0)); + colshape->set_name("shape"); + colshape->set_owner(p_node->get_owner()); + } } else if (p_node->has_meta("empty_draw_type")) { String empty_draw_type = String(p_node->get_meta("empty_draw_type")); StaticBody *sb = memnew(StaticBody); diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h index 2bde9432fc..b81a52ab70 100644 --- a/editor/import/resource_importer_scene.h +++ b/editor/import/resource_importer_scene.h @@ -31,7 +31,7 @@ #ifndef RESOURCEIMPORTERSCENE_H #define RESOURCEIMPORTERSCENE_H -#include "io/resource_import.h" +#include "core/io/resource_import.h" #include "scene/resources/animation.h" #include "scene/resources/mesh.h" #include "scene/resources/shape.h" @@ -116,7 +116,7 @@ class ResourceImporterScene : public ResourceImporter { enum LightBakeMode { LIGHT_BAKE_DISABLED, LIGHT_BAKE_ENABLE, - //LIGHT_BAKE_LIGHTMAPS + LIGHT_BAKE_LIGHTMAPS }; void _replace_owner(Node *p_node, Node *p_scene, Node *p_new_owner); diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp index d03395c070..8e91a88adb 100644 --- a/editor/import/resource_importer_texture.cpp +++ b/editor/import/resource_importer_texture.cpp @@ -30,10 +30,10 @@ #include "resource_importer_texture.h" +#include "core/io/config_file.h" +#include "core/io/image_loader.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" void ResourceImporterTexture::_texture_reimport_srgb(const Ref<StreamTexture> &p_tex) { @@ -164,11 +164,19 @@ bool ResourceImporterTexture::get_option_visibility(const String &p_option, cons if (compress_mode != COMPRESS_LOSSY && compress_mode != COMPRESS_VIDEO_RAM) { return false; } - } else if (p_option == "compress/no_bptc_if_rgb" || p_option == "compress/hdr_mode") { + } else if (p_option == "compress/hdr_mode") { int compress_mode = int(p_options["compress/mode"]); if (compress_mode != COMPRESS_VIDEO_RAM) { return false; } + } else if (p_option == "compress/bptc_ldr") { + int compress_mode = int(p_options["compress/mode"]); + if (compress_mode != COMPRESS_VIDEO_RAM) { + return false; + } + if (!ProjectSettings::get_singleton()->get("rendering/vram_compression/import_bptc")) { + return false; + } } return true; @@ -193,8 +201,8 @@ void ResourceImporterTexture::get_import_options(List<ImportOption> *r_options, r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Lossless,Lossy,Video RAM,Uncompressed", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), p_preset == PRESET_3D ? 2 : 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::REAL, "compress/lossy_quality", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.7)); - r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compress/no_bptc_if_rgb"), false)); - r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/hdr_mode", PROPERTY_HINT_ENUM, "LDR Fallback,Force RGBE,RGBE Fallback"), 0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/hdr_mode", PROPERTY_HINT_ENUM, "Enabled,Force RGBE"), 0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/bptc_ldr", PROPERTY_HINT_ENUM, "Enabled,RGBA Only"), 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/normal_map", PROPERTY_HINT_ENUM, "Detect,Enable,Disabled"), 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "flags/repeat", PROPERTY_HINT_ENUM, "Disabled,Enabled,Mirrored"), p_preset == PRESET_3D ? 1 : 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "flags/filter"), p_preset == PRESET_2D_PIXEL ? false : true)); @@ -227,7 +235,7 @@ void ResourceImporterTexture::_save_stex(const Ref<Image> &p_image, const String if (p_streamable) format |= StreamTexture::FORMAT_BIT_STREAM; - if (p_mipmaps || p_compress_mode == COMPRESS_VIDEO_RAM) //VRAM always uses mipmaps + if (p_mipmaps) format |= StreamTexture::FORMAT_BIT_HAS_MIPMAPS; //mipmaps bit if (p_detect_3d) format |= StreamTexture::FORMAT_BIT_DETECT_3D; @@ -302,7 +310,9 @@ void ResourceImporterTexture::_save_stex(const Ref<Image> &p_image, const String case COMPRESS_VIDEO_RAM: { Ref<Image> image = p_image->duplicate(); - image->generate_mipmaps(p_force_normal); + if (p_mipmaps) { + image->generate_mipmaps(p_force_normal); + } if (p_force_rgbe && image->get_format() >= Image::FORMAT_R8 && image->get_format() <= Image::FORMAT_RGBE9995) { image->convert(Image::FORMAT_RGBE9995); @@ -353,7 +363,6 @@ void ResourceImporterTexture::_save_stex(const Ref<Image> &p_image, const String Error ResourceImporterTexture::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 no_bptc_if_rgb = p_options["compress/no_bptc_if_rgb"]; float lossy = p_options["compress/lossy_quality"]; int repeat = p_options["flags/repeat"]; bool filter = p_options["flags/filter"]; @@ -365,11 +374,11 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String bool invert_color = p_options["process/invert_color"]; bool stream = p_options["stream"]; int size_limit = p_options["size_limit"]; - bool force_rgbe = int(p_options["compress/hdr_mode"]) == 1; - bool rgbe_fallback = int(p_options["compress/hdr_mode"]) == 2; bool hdr_as_srgb = p_options["process/HDR_as_SRGB"]; int normal = p_options["compress/normal_map"]; float scale = p_options["svg/scale"]; + bool force_rgbe = p_options["compress/hdr_mode"]; + int bptc_ldr = p_options["compress/bptc_ldr"]; Ref<Image> image; image.instance(); @@ -424,8 +433,8 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String int width = image->get_width(); image->lock(); - for (int i = 0; i < height; i++) { - for (int j = 0; j < width; j++) { + for (int i = 0; i < width; i++) { + for (int j = 0; j < height; j++) { image->set_pixel(i, j, image->get_pixel(i, j).inverted()); } } @@ -442,61 +451,57 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String //Android, GLES 2.x bool ok_on_pc = false; - bool encode_bptc = false; bool is_hdr = (image->get_format() >= Image::FORMAT_RF && image->get_format() <= Image::FORMAT_RGBE9995); - bool no_ldr_compression = (is_hdr && rgbe_fallback); + bool is_ldr = (image->get_format() >= Image::FORMAT_L8 && image->get_format() <= Image::FORMAT_RGBA5551); + bool can_bptc = ProjectSettings::get_singleton()->get("rendering/vram_compression/import_bptc"); + bool can_s3tc = ProjectSettings::get_singleton()->get("rendering/vram_compression/import_s3tc"); - if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_bptc")) { + if (can_bptc) { + Image::DetectChannels channels = image->get_detected_channels(); + if (is_hdr) { - encode_bptc = true; + if (channels == Image::DETECTED_LA || channels == Image::DETECTED_RGBA) { + can_bptc = false; + } + } else if (is_ldr) { - if (no_bptc_if_rgb && !is_hdr) { - Image::DetectChannels channels = image->get_detected_channels(); - if (channels != Image::DETECTED_LA && channels != Image::DETECTED_RGBA) { - encode_bptc = false; + //handle "RGBA Only" setting + if (bptc_ldr == 1 && channels != Image::DETECTED_LA && channels != Image::DETECTED_RGBA) { + can_bptc = false; } } } - if (encode_bptc) { - - _save_stex(image, p_save_path + ".bptc.stex", compress_mode, lossy, Image::COMPRESS_BPTC, mipmaps, tex_flags, stream, detect_3d, detect_srgb, force_rgbe, detect_normal, force_normal); - r_platform_variants->push_back("bptc"); - ok_on_pc = true; + if (!can_bptc && is_hdr && !force_rgbe) { + //convert to ldr if this can't be stored hdr + image->convert(Image::FORMAT_RGBA8); } - if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_s3tc") && !no_ldr_compression) { - - _save_stex(image, p_save_path + ".s3tc.stex", compress_mode, lossy, Image::COMPRESS_S3TC, mipmaps, tex_flags, stream, detect_3d, detect_srgb, force_rgbe, detect_normal, force_normal); + if (can_bptc || can_s3tc) { + _save_stex(image, p_save_path + ".s3tc.stex", compress_mode, lossy, can_bptc ? Image::COMPRESS_BPTC : Image::COMPRESS_S3TC, mipmaps, tex_flags, stream, detect_3d, detect_srgb, force_rgbe, detect_normal, force_normal); r_platform_variants->push_back("s3tc"); ok_on_pc = true; } - if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc2") && !no_ldr_compression) { + if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc2")) { _save_stex(image, p_save_path + ".etc2.stex", compress_mode, lossy, Image::COMPRESS_ETC2, mipmaps, tex_flags, stream, detect_3d, detect_srgb, force_rgbe, detect_normal, force_normal); r_platform_variants->push_back("etc2"); } - if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc") && !no_ldr_compression) { + if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc")) { _save_stex(image, p_save_path + ".etc.stex", compress_mode, lossy, Image::COMPRESS_ETC, mipmaps, tex_flags, stream, detect_3d, detect_srgb, force_rgbe, detect_normal, force_normal); r_platform_variants->push_back("etc"); } - if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_pvrtc") && !no_ldr_compression) { + if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_pvrtc")) { _save_stex(image, p_save_path + ".pvrtc.stex", compress_mode, lossy, Image::COMPRESS_PVRTC4, mipmaps, tex_flags, stream, detect_3d, detect_srgb, force_rgbe, detect_normal, force_normal); r_platform_variants->push_back("pvrtc"); } - if (is_hdr && rgbe_fallback) { - _save_stex(image, p_save_path + ".fallback.stex", compress_mode, lossy, Image::COMPRESS_S3TC /*this is ignored */, mipmaps, tex_flags, stream, detect_3d, detect_srgb, true, detect_normal, force_normal); - r_platform_variants->push_back("fallback"); - ok_on_pc = true; - } - 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."); + EditorNode::add_io_error("Warning, no suitable PC VRAM compression enabled in Project Settings. This texture will not display correctly on PC."); } } else { //import normally diff --git a/editor/import/resource_importer_texture.h b/editor/import/resource_importer_texture.h index fd6f75c3f4..b49b29874d 100644 --- a/editor/import/resource_importer_texture.h +++ b/editor/import/resource_importer_texture.h @@ -31,8 +31,8 @@ #ifndef RESOURCEIMPORTTEXTURE_H #define RESOURCEIMPORTTEXTURE_H -#include "image.h" -#include "io/resource_import.h" +#include "core/image.h" +#include "core/io/resource_import.h" class StreamTexture; diff --git a/editor/import/resource_importer_wav.cpp b/editor/import/resource_importer_wav.cpp index d04f29ea5e..55f4cc7439 100644 --- a/editor/import/resource_importer_wav.cpp +++ b/editor/import/resource_importer_wav.cpp @@ -30,9 +30,9 @@ #include "resource_importer_wav.h" -#include "io/marshalls.h" -#include "io/resource_saver.h" -#include "os/file_access.h" +#include "core/io/marshalls.h" +#include "core/io/resource_saver.h" +#include "core/os/file_access.h" #include "scene/resources/audio_stream_sample.h" String ResourceImporterWAV::get_importer_name() const { diff --git a/editor/import/resource_importer_wav.h b/editor/import/resource_importer_wav.h index f78ab09e9b..a630ff732e 100644 --- a/editor/import/resource_importer_wav.h +++ b/editor/import/resource_importer_wav.h @@ -31,7 +31,7 @@ #ifndef RESOURCEIMPORTWAV_H #define RESOURCEIMPORTWAV_H -#include "io/resource_import.h" +#include "core/io/resource_import.h" class ResourceImporterWAV : public ResourceImporter { GDCLASS(ResourceImporterWAV, ResourceImporter) diff --git a/editor/import_dock.cpp b/editor/import_dock.cpp index 31eb193461..2e68609a56 100644 --- a/editor/import_dock.cpp +++ b/editor/import_dock.cpp @@ -38,11 +38,17 @@ public: List<PropertyInfo> properties; Ref<ResourceImporter> importer; Vector<String> paths; + Set<StringName> checked; + bool checking; bool _set(const StringName &p_name, const Variant &p_value) { if (values.has(p_name)) { values[p_name] = p_value; + if (checking) { + checked.insert(p_name); + _change_notify(String(p_name).utf8().get_data()); + } return true; } @@ -63,13 +69,24 @@ public: for (const List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) { if (!importer->get_option_visibility(E->get().name, values)) continue; - p_list->push_back(E->get()); + PropertyInfo pi = E->get(); + if (checking) { + pi.usage |= PROPERTY_USAGE_CHECKABLE; + if (checked.has(E->get().name)) { + pi.usage |= PROPERTY_USAGE_CHECKED; + } + } + p_list->push_back(pi); } } void update() { _change_notify(); } + + ImportDockParameters() { + checking = false; + } }; void ImportDock::set_edit_path(const String &p_path) { @@ -125,6 +142,8 @@ void ImportDock::_update_options(const Ref<ConfigFile> &p_config) { params->properties.clear(); params->values.clear(); + params->checking = false; + params->checked.clear(); for (List<ResourceImporter::ImportOption>::Element *E = options.front(); E; E = E->next()) { @@ -205,6 +224,8 @@ void ImportDock::set_edit_multiple_paths(const Vector<String> &p_paths) { params->properties.clear(); params->values.clear(); + params->checking = true; + params->checked.clear(); for (List<ResourceImporter::ImportOption>::Element *E = options.front(); E; E = E->next()) { @@ -360,11 +381,21 @@ void ImportDock::_reimport() { Error err = config->load(params->paths[i] + ".import"); ERR_CONTINUE(err != OK); - config->set_value("remap", "importer", params->importer->get_importer_name()); - config->erase_section("params"); + if (params->checking) { + //update only what edited (checkboxes) + for (List<PropertyInfo>::Element *E = params->properties.front(); E; E = E->next()) { + if (params->checked.has(E->get().name)) { + config->set_value("params", E->get().name, params->values[E->get().name]); + } + } + } else { + //override entirely + config->set_value("remap", "importer", params->importer->get_importer_name()); + config->erase_section("params"); - for (List<PropertyInfo>::Element *E = params->properties.front(); E; E = E->next()) { - config->set_value("params", E->get().name, params->values[E->get().name]); + for (List<PropertyInfo>::Element *E = params->properties.front(); E; E = E->next()) { + config->set_value("params", E->get().name, params->values[E->get().name]); + } } config->save(params->paths[i] + ".import"); @@ -388,11 +419,20 @@ void ImportDock::_notification(int p_what) { } break; } } + +void ImportDock::_property_toggled(const StringName &p_prop, bool p_checked) { + if (p_checked) { + params->checked.insert(p_prop); + } else { + params->checked.erase(p_prop); + } +} void ImportDock::_bind_methods() { ClassDB::bind_method(D_METHOD("_reimport"), &ImportDock::_reimport); ClassDB::bind_method(D_METHOD("_preset_selected"), &ImportDock::_preset_selected); ClassDB::bind_method(D_METHOD("_importer_selected"), &ImportDock::_importer_selected); + ClassDB::bind_method(D_METHOD("_property_toggled"), &ImportDock::_property_toggled); } void ImportDock::initialize_import_options() const { @@ -423,6 +463,7 @@ ImportDock::ImportDock() { import_opts = memnew(EditorInspector); add_child(import_opts); import_opts->set_v_size_flags(SIZE_EXPAND_FILL); + import_opts->connect("property_toggled", this, "_property_toggled"); hb = memnew(HBoxContainer); add_child(hb); diff --git a/editor/import_dock.h b/editor/import_dock.h index 41c7298d9a..632fd39700 100644 --- a/editor/import_dock.h +++ b/editor/import_dock.h @@ -60,6 +60,7 @@ class ImportDock : public VBoxContainer { void _importer_selected(int i_idx); void _update_options(const Ref<ConfigFile> &p_config = Ref<ConfigFile>()); + void _property_toggled(const StringName &p_prop, bool p_checked); void _reimport(); enum { diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp index 0335053162..2a8885c0a4 100644 --- a/editor/inspector_dock.cpp +++ b/editor/inspector_dock.cpp @@ -36,6 +36,12 @@ void InspectorDock::_menu_option(int p_option) { switch (p_option) { + case EXPAND_ALL: { + _menu_expandall(); + } break; + case COLLAPSE_ALL: { + _menu_collapseall(); + } break; case RESOURCE_MAKE_BUILT_IN: { _unref_resource(); } break; @@ -98,6 +104,7 @@ void InspectorDock::_menu_option(int p_option) { res = duplicates[res]; current->set(E->get().name, res); + editor->get_inspector()->update_property(E->get().name); } } } @@ -152,7 +159,6 @@ void InspectorDock::_resource_file_selected(String p_file) { RES res = ResourceLoader::load(p_file); if (res.is_null()) { - warning_dialog->get_ok()->set_text(TTR("OK")); warning_dialog->set_text(TTR("Failed to load resource.")); return; }; @@ -225,11 +231,10 @@ void InspectorDock::_prepare_history() { already.insert(id); - Ref<Texture> icon = get_icon("Object", "EditorIcons"); - if (has_icon(obj->get_class(), "EditorIcons")) - icon = get_icon(obj->get_class(), "EditorIcons"); - else + Ref<Texture> icon = EditorNode::get_singleton()->get_object_icon(obj, ""); + if (icon.is_null()) { icon = base_icon; + } String text; if (Object::cast_to<Resource>(obj)) { @@ -254,6 +259,8 @@ void InspectorDock::_prepare_history() { } history_menu->get_popup()->add_icon_item(icon, text, i); } + + editor_path->update_path(); } void InspectorDock::_select_history(int p_idx) const { @@ -314,7 +321,6 @@ void InspectorDock::_transform_keyed(Object *sp, const String &p_sub, const Tran } void InspectorDock::_warning_pressed() { - warning_dialog->get_ok()->set_text(TTR("Ok")); warning_dialog->popup_centered_minsize(); } @@ -322,6 +328,21 @@ Container *InspectorDock::get_addon_area() { return this; } +void InspectorDock::_notification(int p_what) { + switch (p_what) { + case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { + set_theme(editor->get_gui_base()->get_theme()); + resource_new_button->set_icon(get_icon("New", "EditorIcons")); + resource_load_button->set_icon(get_icon("Load", "EditorIcons")); + backward_button->set_icon(get_icon("Back", "EditorIcons")); + forward_button->set_icon(get_icon("Forward", "EditorIcons")); + history_menu->set_icon(get_icon("History", "EditorIcons")); + object_menu->set_icon(get_icon("Tools", "EditorIcons")); + warning->set_icon(get_icon("NodeWarning", "EditorIcons")); + } break; + } +} + void InspectorDock::_bind_methods() { ClassDB::bind_method("_menu_option", &InspectorDock::_menu_option); @@ -399,8 +420,8 @@ void InspectorDock::update(Object *p_object) { PopupMenu *p = object_menu->get_popup(); p->clear(); - p->add_shortcut(ED_SHORTCUT("property_editor/expand_all", TTR("Expand all properties")), EXPAND_ALL); - p->add_shortcut(ED_SHORTCUT("property_editor/collapse_all", TTR("Collapse all properties")), COLLAPSE_ALL); + p->add_shortcut(ED_SHORTCUT("property_editor/expand_all", TTR("Expand All Properties")), EXPAND_ALL); + p->add_shortcut(ED_SHORTCUT("property_editor/collapse_all", TTR("Collapse All Properties")), COLLAPSE_ALL); p->add_separator(); if (is_resource) { p->add_item(TTR("Save"), RESOURCE_SAVE); @@ -572,6 +593,7 @@ InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) { inspector->set_undo_redo(&editor_data->get_undo_redo()); inspector->set_use_filter(true); // TODO: check me + inspector->set_auto_unfold_edited(bool(EDITOR_GET("interface/inspector/auto_unfold_edited"))); inspector->connect("resource_selected", this, "_resource_selected"); inspector->connect("property_keyed", this, "_property_keyed"); diff --git a/editor/inspector_dock.h b/editor/inspector_dock.h index 97ef6899dc..57d2a03295 100644 --- a/editor/inspector_dock.h +++ b/editor/inspector_dock.h @@ -118,6 +118,7 @@ class InspectorDock : public VBoxContainer { protected: static void _bind_methods(); + void _notification(int p_what); public: void go_back(); diff --git a/editor/multi_node_edit.cpp b/editor/multi_node_edit.cpp index 173be01586..be4e752d55 100644 --- a/editor/multi_node_edit.cpp +++ b/editor/multi_node_edit.cpp @@ -30,7 +30,7 @@ #include "multi_node_edit.h" -#include "core/helper/math_fieldwise.h" +#include "core/math/math_fieldwise.h" #include "editor_node.h" bool MultiNodeEdit::_set(const StringName &p_name, const Variant &p_value) { diff --git a/editor/output_strings.h b/editor/output_strings.h index 0729971704..4833f2067e 100644 --- a/editor/output_strings.h +++ b/editor/output_strings.h @@ -31,7 +31,7 @@ #ifndef OUTPUT_STRINGS_H #define OUTPUT_STRINGS_H -#include "map.h" +#include "core/map.h" #include "scene/gui/control.h" #include "scene/gui/scroll_bar.h" diff --git a/editor/plugin_config_dialog.cpp b/editor/plugin_config_dialog.cpp index 93bed035a5..8e626e7111 100644 --- a/editor/plugin_config_dialog.cpp +++ b/editor/plugin_config_dialog.cpp @@ -76,7 +76,10 @@ void PluginConfigDialog::_on_confirmed() { "extends EditorPlugin\n" "\n" "func _enter_tree():\n" - "\tpass"); + "\tpass\n" + "\n" + "func _exit_tree():\n" + "\tpass\n"); String script_path = path.plus_file(script_edit->get_text()); gdscript->set_path(script_path); ResourceSaver::save(script_path, gdscript); @@ -84,7 +87,7 @@ void PluginConfigDialog::_on_confirmed() { } //TODO: other languages - emit_signal("plugin_ready", script.operator->(), active_edit->is_pressed() ? name_edit->get_text() : ""); + emit_signal("plugin_ready", script.operator->(), active_edit->is_pressed() ? subfolder_edit->get_text() : ""); } else { EditorNode::get_singleton()->get_project_settings()->update_plugins(); } @@ -177,7 +180,7 @@ PluginConfigDialog::PluginConfigDialog() { grid->add_child(desc_lb); desc_edit = memnew(TextEdit); - desc_edit->set_custom_minimum_size(Size2(400.0f, 50.0f)); + desc_edit->set_custom_minimum_size(Size2(400, 80) * EDSCALE); grid->add_child(desc_edit); Label *author_lb = memnew(Label); diff --git a/editor/plugins/SCsub b/editor/plugins/SCsub index f1fa50148f..2b1e889fb0 100644 --- a/editor/plugins/SCsub +++ b/editor/plugins/SCsub @@ -1,5 +1,5 @@ #!/usr/bin/env python Import('env') -Export('env') + env.add_source_files(env.editor_sources, "*.cpp") diff --git a/editor/plugins/abstract_polygon_2d_editor.cpp b/editor/plugins/abstract_polygon_2d_editor.cpp index 2d341cdd93..71a5d73b2f 100644 --- a/editor/plugins/abstract_polygon_2d_editor.cpp +++ b/editor/plugins/abstract_polygon_2d_editor.cpp @@ -132,8 +132,8 @@ Vector2 AbstractPolygon2DEditor::_get_offset(int p_idx) const { void AbstractPolygon2DEditor::_commit_action() { - undo_redo->add_do_method(canvas_item_editor->get_viewport_control(), "update"); - undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(), "update"); + undo_redo->add_do_method(canvas_item_editor, "update_viewport"); + undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); undo_redo->commit_action(); } @@ -218,7 +218,7 @@ void AbstractPolygon2DEditor::_node_removed(Node *p_node) { edit(NULL); hide(); - canvas_item_editor->get_viewport_control()->update(); + canvas_item_editor->update_viewport(); } } @@ -275,6 +275,10 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) return (mb.is_valid() && mb->get_button_index() == 1); } + CanvasItemEditor::Tool tool = CanvasItemEditor::get_singleton()->get_current_tool(); + if (tool != CanvasItemEditor::TOOL_SELECT) + return false; + if (mb.is_valid()) { Transform2D xform = canvas_item_editor->get_canvas_transform() * _get_node()->get_global_transform(); @@ -283,10 +287,10 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) Vector2 cpoint = _get_node()->get_global_transform().affine_inverse().xform(canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(mb->get_position()))); if (mode == MODE_EDIT || (_is_line() && mode == MODE_CREATE)) { - if (mb->get_button_index() == BUTTON_LEFT) { - if (mb->is_pressed()) { + if (mb->get_control() || mb->get_shift() || mb->get_alt()) + return false; const PosVertex insert = closest_edge_point(gpoint); @@ -330,7 +334,7 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) edited_point = PosVertex(closest, xform.affine_inverse().xform(closest.pos)); selected_point = closest; edge_point = PosVertex(); - canvas_item_editor->get_viewport_control()->update(); + canvas_item_editor->update_viewport(); return true; } else { @@ -399,7 +403,7 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) wip_active = true; _wip_changed(); edited_point = PosVertex(-1, 1, cpoint); - canvas_item_editor->get_viewport_control()->update(); + canvas_item_editor->update_viewport(); hover_point = Vertex(); selected_point = Vertex(0); edge_point = PosVertex(); @@ -420,7 +424,7 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) _wip_changed(); edited_point = PosVertex(-1, wip.size(), cpoint); selected_point = Vertex(wip.size() - 1); - canvas_item_editor->get_viewport_control()->update(); + canvas_item_editor->update_viewport(); return true; } } @@ -449,7 +453,7 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) _set_polygon(edited_point.polygon, vertices); } - canvas_item_editor->get_viewport_control()->update(); + canvas_item_editor->update_viewport(); } else if (mode == MODE_EDIT || (_is_line() && mode == MODE_CREATE)) { const PosVertex onEdgeVertex = closest_edge_point(gpoint); @@ -458,20 +462,20 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) hover_point = Vertex(); edge_point = onEdgeVertex; - canvas_item_editor->get_viewport_control()->update(); + canvas_item_editor->update_viewport(); } else { if (edge_point.valid()) { edge_point = PosVertex(); - canvas_item_editor->get_viewport_control()->update(); + canvas_item_editor->update_viewport(); } const PosVertex new_hover_point = closest_point(gpoint); if (hover_point != new_hover_point) { hover_point = new_hover_point; - canvas_item_editor->get_viewport_control()->update(); + canvas_item_editor->update_viewport(); } } } @@ -490,7 +494,7 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) wip.remove(selected_point.vertex); _wip_changed(); selected_point = wip.size() - 1; - canvas_item_editor->get_viewport_control()->update(); + canvas_item_editor->update_viewport(); return true; } } else { @@ -512,12 +516,10 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) return false; } -void AbstractPolygon2DEditor::forward_draw_over_viewport(Control *p_overlay) { +void AbstractPolygon2DEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { if (!_get_node()) return; - Control *vpc = canvas_item_editor->get_viewport_control(); - Transform2D xform = canvas_item_editor->get_canvas_transform() * _get_node()->get_global_transform(); const Ref<Texture> handle = get_icon("EditorHandle", "EditorIcons"); @@ -558,7 +560,7 @@ void AbstractPolygon2DEditor::forward_draw_over_viewport(Control *p_overlay) { Vector2 point = xform.xform(p); Vector2 next_point = xform.xform(p2); - vpc->draw_line(point, next_point, col, 2 * EDSCALE); + p_overlay->draw_line(point, next_point, col, 2 * EDSCALE); } } @@ -582,7 +584,7 @@ void AbstractPolygon2DEditor::forward_draw_over_viewport(Control *p_overlay) { p2 = points[(i + 1) % n_points] + offset; const Vector2 next_point = xform.xform(p2); - vpc->draw_line(point, next_point, col, 2 * EDSCALE); + p_overlay->draw_line(point, next_point, col, 2 * EDSCALE); } } @@ -594,14 +596,14 @@ void AbstractPolygon2DEditor::forward_draw_over_viewport(Control *p_overlay) { const Vector2 point = xform.xform(p); const Color modulate = vertex == active_point ? Color(0.5, 1, 2) : Color(1, 1, 1); - vpc->draw_texture(handle, point - handle->get_size() * 0.5, modulate); + p_overlay->draw_texture(handle, point - handle->get_size() * 0.5, modulate); } } if (edge_point.valid()) { Ref<Texture> add_handle = get_icon("EditorHandleAdd", "EditorIcons"); - vpc->draw_texture(add_handle, edge_point.pos - add_handle->get_size() * 0.5); + p_overlay->draw_texture(add_handle, edge_point.pos - add_handle->get_size() * 0.5); } } @@ -625,7 +627,7 @@ void AbstractPolygon2DEditor::edit(Node *p_polygon) { hover_point = Vertex(); selected_point = Vertex(); - canvas_item_editor->get_viewport_control()->update(); + canvas_item_editor->update_viewport(); } else { diff --git a/editor/plugins/abstract_polygon_2d_editor.h b/editor/plugins/abstract_polygon_2d_editor.h index 9ba03bcdf6..00634ba5b8 100644 --- a/editor/plugins/abstract_polygon_2d_editor.h +++ b/editor/plugins/abstract_polygon_2d_editor.h @@ -137,7 +137,7 @@ protected: public: bool forward_gui_input(const Ref<InputEvent> &p_event); - void forward_draw_over_viewport(Control *p_overlay); + void forward_canvas_draw_over_viewport(Control *p_overlay); void edit(Node *p_polygon); AbstractPolygon2DEditor(EditorNode *p_editor, bool p_wip_destructive = true); @@ -153,7 +153,7 @@ class AbstractPolygon2DEditorPlugin : public EditorPlugin { public: virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return polygon_editor->forward_gui_input(p_event); } - virtual void forward_draw_over_viewport(Control *p_overlay) { polygon_editor->forward_draw_over_viewport(p_overlay); } + virtual void forward_canvas_draw_over_viewport(Control *p_overlay) { polygon_editor->forward_canvas_draw_over_viewport(p_overlay); } bool has_main_screen() const { return false; } virtual String get_name() const { return klass; } diff --git a/editor/plugins/animation_blend_space_1d_editor.cpp b/editor/plugins/animation_blend_space_1d_editor.cpp index 5373015654..4b1e710705 100644 --- a/editor/plugins/animation_blend_space_1d_editor.cpp +++ b/editor/plugins/animation_blend_space_1d_editor.cpp @@ -1,6 +1,36 @@ +/*************************************************************************/ +/* animation_blend_space_1d_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 "animation_blend_space_1d_editor.h" -#include "os/keyboard.h" +#include "core/os/keyboard.h" #include "scene/animation/animation_blend_tree.h" StringName AnimationNodeBlendSpace1DEditor::get_blend_position_path() const { diff --git a/editor/plugins/animation_blend_space_1d_editor.h b/editor/plugins/animation_blend_space_1d_editor.h index 278357b9c7..ca6135406c 100644 --- a/editor/plugins/animation_blend_space_1d_editor.h +++ b/editor/plugins/animation_blend_space_1d_editor.h @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* animation_blend_space_1d_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 ANIMATION_BLEND_SPACE_1D_EDITOR_H #define ANIMATION_BLEND_SPACE_1D_EDITOR_H diff --git a/editor/plugins/animation_blend_space_2d_editor.cpp b/editor/plugins/animation_blend_space_2d_editor.cpp index 2d240b5a5c..394b888d0e 100644 --- a/editor/plugins/animation_blend_space_2d_editor.cpp +++ b/editor/plugins/animation_blend_space_2d_editor.cpp @@ -1,10 +1,40 @@ +/*************************************************************************/ +/* animation_blend_space_2d_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 "animation_blend_space_2d_editor.h" #include "core/io/resource_loader.h" +#include "core/math/delaunay.h" +#include "core/os/input.h" +#include "core/os/keyboard.h" #include "core/project_settings.h" -#include "math/delaunay.h" -#include "os/input.h" -#include "os/keyboard.h" #include "scene/animation/animation_blend_tree.h" #include "scene/animation/animation_player.h" #include "scene/gui/menu_button.h" @@ -404,7 +434,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_draw() { if (blend_space->get_snap().x > 0) { - int prev_idx; + int prev_idx = 0; for (int i = 0; i < s.x; i++) { float v = blend_space->get_min_space().x + i * (blend_space->get_max_space().x - blend_space->get_min_space().x) / s.x; @@ -420,7 +450,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_draw() { if (blend_space->get_snap().y > 0) { - int prev_idx; + int prev_idx = 0; for (int i = 0; i < s.y; i++) { float v = blend_space->get_max_space().y - i * (blend_space->get_max_space().y - blend_space->get_min_space().y) / s.y; diff --git a/editor/plugins/animation_blend_space_2d_editor.h b/editor/plugins/animation_blend_space_2d_editor.h index 0bf1e25d7a..613289e4d8 100644 --- a/editor/plugins/animation_blend_space_2d_editor.h +++ b/editor/plugins/animation_blend_space_2d_editor.h @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* animation_blend_space_2d_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 ANIMATION_BLEND_SPACE_2D_EDITOR_H #define ANIMATION_BLEND_SPACE_2D_EDITOR_H diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp index dbb5fa578b..b83976270f 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.cpp +++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp @@ -1,10 +1,40 @@ +/*************************************************************************/ +/* animation_blend_tree_editor_plugin.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 "animation_blend_tree_editor_plugin.h" #include "core/io/resource_loader.h" +#include "core/os/input.h" +#include "core/os/keyboard.h" #include "core/project_settings.h" #include "editor/editor_inspector.h" -#include "os/input.h" -#include "os/keyboard.h" #include "scene/animation/animation_player.h" #include "scene/gui/menu_button.h" #include "scene/gui/panel.h" @@ -101,10 +131,6 @@ void AnimationNodeBlendTreeEditor::_update_graph() { Ref<AnimationNode> agnode = blend_tree->get_node(E->get()); - if (!agnode->is_connected("changed", this, "_node_changed")) { - agnode->connect("changed", this, "_node_changed", varray(agnode->get_instance_id()), CONNECT_DEFERRED); - } - node->set_offset(blend_tree->get_node_position(E->get()) * EDSCALE); node->set_title(agnode->get_caption()); @@ -211,7 +237,7 @@ void AnimationNodeBlendTreeEditor::_update_graph() { mb->get_popup()->connect("index_pressed", this, "_anim_selected", varray(options, E->get()), CONNECT_DEFERRED); } - /* should be no longer necesary, as the boolean works + /* should be no longer necessary, as the boolean works Ref<AnimationNodeOneShot> oneshot = agnode; if (oneshot.is_valid()) { @@ -512,11 +538,7 @@ bool AnimationNodeBlendTreeEditor::_update_filters(const Ref<AnimationNode> &ano if (base->has_node(accum)) { Node *node = base->get_node(accum); - if (has_icon(node->get_class(), "EditorIcons")) { - ti->set_icon(0, get_icon(node->get_class(), "EditorIcons")); - } else { - ti->set_icon(0, get_icon("Node", "EditorIcons")); - } + ti->set_icon(0, EditorNode::get_singleton()->get_object_icon(node, "Node")); } } else { @@ -529,7 +551,7 @@ bool AnimationNodeBlendTreeEditor::_update_filters(const Ref<AnimationNode> &ano node = base->get_node(accum); } if (!node) - continue; //no node, cant edit + continue; //no node, can't edit if (path.get_subname_count()) { @@ -695,14 +717,6 @@ void AnimationNodeBlendTreeEditor::_scroll_changed(const Vector2 &p_scroll) { updating = false; } -void AnimationNodeBlendTreeEditor::_node_changed(ObjectID p_node) { - - AnimationNode *an = Object::cast_to<AnimationNode>(ObjectDB::get_instance(p_node)); - //if (an && an->get_parent() == blend_tree) { - _update_graph(); - //} -} - void AnimationNodeBlendTreeEditor::_bind_methods() { ClassDB::bind_method("_update_graph", &AnimationNodeBlendTreeEditor::_update_graph); @@ -720,7 +734,6 @@ void AnimationNodeBlendTreeEditor::_bind_methods() { ClassDB::bind_method("_update_filters", &AnimationNodeBlendTreeEditor::_update_filters); ClassDB::bind_method("_filter_edited", &AnimationNodeBlendTreeEditor::_filter_edited); ClassDB::bind_method("_filter_toggled", &AnimationNodeBlendTreeEditor::_filter_toggled); - ClassDB::bind_method("_node_changed", &AnimationNodeBlendTreeEditor::_node_changed); ClassDB::bind_method("_removed_from_graph", &AnimationNodeBlendTreeEditor::_removed_from_graph); ClassDB::bind_method("_property_changed", &AnimationNodeBlendTreeEditor::_property_changed); ClassDB::bind_method("_file_opened", &AnimationNodeBlendTreeEditor::_file_opened); @@ -820,8 +833,6 @@ void AnimationNodeBlendTreeEditor::edit(const Ref<AnimationNode> &p_node) { if (p_node.is_valid()) { blend_tree = p_node; - } else { - blend_tree.unref(); } if (blend_tree.is_null()) { diff --git a/editor/plugins/animation_blend_tree_editor_plugin.h b/editor/plugins/animation_blend_tree_editor_plugin.h index 35ecc32979..e2daefdec6 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.h +++ b/editor/plugins/animation_blend_tree_editor_plugin.h @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* animation_blend_tree_editor_plugin.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 ANIMATION_BLEND_TREE_EDITOR_PLUGIN_H #define ANIMATION_BLEND_TREE_EDITOR_PLUGIN_H @@ -74,8 +104,6 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin { void _filter_toggled(); Ref<AnimationNode> _filter_edit; - void _node_changed(ObjectID p_node); - void _property_changed(const StringName &p_property, const Variant &p_value); void _removed_from_graph(); diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index 9ab5436de8..445664f8dd 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -30,12 +30,12 @@ #include "animation_player_editor_plugin.h" +#include "core/io/resource_loader.h" +#include "core/io/resource_saver.h" +#include "core/os/keyboard.h" +#include "core/project_settings.h" #include "editor/animation_track_editor.h" #include "editor/editor_settings.h" -#include "io/resource_loader.h" -#include "io/resource_saver.h" -#include "os/keyboard.h" -#include "project_settings.h" // For onion skinning #include "editor/plugins/canvas_item_editor_plugin.h" @@ -908,7 +908,7 @@ void AnimationPlayerEditor::edit(AnimationPlayer *p_player) { } } -void AnimationPlayerEditor::forward_force_draw_over_viewport(Control *p_overlay) { +void AnimationPlayerEditor::forward_canvas_force_draw_over_viewport(Control *p_overlay) { if (!onion.can_overlay) return; @@ -1515,7 +1515,7 @@ void AnimationPlayerEditor::_prepare_onion_layers_2() { void AnimationPlayerEditor::_start_onion_skinning() { - // FIXME: Using "idle_frame" makes onion layers update one frame behing the current + // FIXME: Using "idle_frame" makes onion layers update one frame behind the current if (!get_tree()->is_connected("idle_frame", this, "call_deferred")) { get_tree()->connect("idle_frame", this, "call_deferred", varray("_prepare_onion_layers_1")); } diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h index 5ac7b99903..55f082aadb 100644 --- a/editor/plugins/animation_player_editor_plugin.h +++ b/editor/plugins/animation_player_editor_plugin.h @@ -246,7 +246,7 @@ public: void set_undo_redo(UndoRedo *p_undo_redo) { undo_redo = p_undo_redo; } void edit(AnimationPlayer *p_player); - void forward_force_draw_over_viewport(Control *p_overlay); + void forward_canvas_force_draw_over_viewport(Control *p_overlay); AnimationPlayerEditor(EditorNode *p_editor, AnimationPlayerEditorPlugin *p_plugin); }; @@ -271,7 +271,7 @@ public: virtual bool handles(Object *p_object) const; virtual void make_visible(bool p_visible); - virtual void forward_force_draw_over_viewport(Control *p_overlay) { anim_editor->forward_force_draw_over_viewport(p_overlay); } + virtual void forward_canvas_force_draw_over_viewport(Control *p_overlay) { anim_editor->forward_canvas_force_draw_over_viewport(p_overlay); } AnimationPlayerEditorPlugin(EditorNode *p_node); ~AnimationPlayerEditorPlugin(); diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp index 3a65cb9b38..e83773257b 100644 --- a/editor/plugins/animation_state_machine_editor.cpp +++ b/editor/plugins/animation_state_machine_editor.cpp @@ -1,10 +1,40 @@ +/*************************************************************************/ +/* animation_state_machine_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 "animation_state_machine_editor.h" #include "core/io/resource_loader.h" +#include "core/math/delaunay.h" +#include "core/os/input.h" +#include "core/os/keyboard.h" #include "core/project_settings.h" -#include "math/delaunay.h" -#include "os/input.h" -#include "os/keyboard.h" #include "scene/animation/animation_blend_tree.h" #include "scene/animation/animation_player.h" #include "scene/gui/menu_button.h" @@ -633,7 +663,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { transition_lines.clear(); - //draw conecting line for potential new transition + //draw connecting line for potential new transition if (connecting) { Vector2 from = (state_machine->get_node_position(connecting_from) * EDSCALE) - state_machine->get_graph_offset() * EDSCALE; Vector2 to; diff --git a/editor/plugins/animation_state_machine_editor.h b/editor/plugins/animation_state_machine_editor.h index 49d08607cf..7b8a4a0e94 100644 --- a/editor/plugins/animation_state_machine_editor.h +++ b/editor/plugins/animation_state_machine_editor.h @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* animation_state_machine_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 ANIMATION_STATE_MACHINE_EDITOR_H #define ANIMATION_STATE_MACHINE_EDITOR_H diff --git a/editor/plugins/animation_tree_editor_plugin.cpp b/editor/plugins/animation_tree_editor_plugin.cpp index 19921ef54f..24787a78e9 100644 --- a/editor/plugins/animation_tree_editor_plugin.cpp +++ b/editor/plugins/animation_tree_editor_plugin.cpp @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* animation_tree_editor_plugin.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 "animation_tree_editor_plugin.h" #include "animation_blend_space_1d_editor.h" @@ -5,10 +35,10 @@ #include "animation_blend_tree_editor_plugin.h" #include "animation_state_machine_editor.h" #include "core/io/resource_loader.h" +#include "core/math/delaunay.h" +#include "core/os/input.h" +#include "core/os/keyboard.h" #include "core/project_settings.h" -#include "math/delaunay.h" -#include "os/input.h" -#include "os/keyboard.h" #include "scene/animation/animation_blend_tree.h" #include "scene/animation/animation_player.h" #include "scene/gui/menu_button.h" diff --git a/editor/plugins/animation_tree_editor_plugin.h b/editor/plugins/animation_tree_editor_plugin.h index b12054bb62..be8848d600 100644 --- a/editor/plugins/animation_tree_editor_plugin.h +++ b/editor/plugins/animation_tree_editor_plugin.h @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* animation_tree_editor_plugin.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 ANIMATION_TREE_EDITOR_PLUGIN_H #define ANIMATION_TREE_EDITOR_PLUGIN_H diff --git a/editor/plugins/animation_tree_player_editor_plugin.cpp b/editor/plugins/animation_tree_player_editor_plugin.cpp index 36d10ab99e..c79e3a436d 100644 --- a/editor/plugins/animation_tree_player_editor_plugin.cpp +++ b/editor/plugins/animation_tree_player_editor_plugin.cpp @@ -31,9 +31,9 @@ #include "animation_tree_player_editor_plugin.h" #include "core/io/resource_loader.h" +#include "core/os/input.h" +#include "core/os/keyboard.h" #include "core/project_settings.h" -#include "os/input.h" -#include "os/keyboard.h" #include "scene/gui/menu_button.h" #include "scene/gui/panel.h" #include "scene/main/viewport.h" diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index 66770d98e5..138b8a491c 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -962,6 +962,9 @@ HBoxContainer *EditorAssetLibrary::_make_pages(int p_page, int p_page_count, int HBoxContainer *hbc = memnew(HBoxContainer); + if (p_page_count < 2) + return hbc; + //do the mario int from = p_page - 5; if (from < 0) diff --git a/editor/plugins/audio_stream_editor_plugin.cpp b/editor/plugins/audio_stream_editor_plugin.cpp index 454a5d72f2..06ca5833e2 100644 --- a/editor/plugins/audio_stream_editor_plugin.cpp +++ b/editor/plugins/audio_stream_editor_plugin.cpp @@ -30,9 +30,9 @@ #include "audio_stream_editor_plugin.h" +#include "core/io/resource_loader.h" +#include "core/project_settings.h" #include "editor/editor_settings.h" -#include "io/resource_loader.h" -#include "project_settings.h" void AudioStreamEditor::_notification(int p_what) { diff --git a/editor/plugins/baked_lightmap_editor_plugin.cpp b/editor/plugins/baked_lightmap_editor_plugin.cpp index 59b79bd070..40e3bb5be2 100644 --- a/editor/plugins/baked_lightmap_editor_plugin.cpp +++ b/editor/plugins/baked_lightmap_editor_plugin.cpp @@ -50,6 +50,7 @@ void BakedLightmapEditorPlugin::_bake() { case BakedLightmap::BAKE_ERROR_CANT_CREATE_IMAGE: EditorNode::get_singleton()->show_warning(TTR("Failed creating lightmap images, make sure path is writable.")); break; + default: {} } } } @@ -107,7 +108,7 @@ void BakedLightmapEditorPlugin::_bind_methods() { BakedLightmapEditorPlugin::BakedLightmapEditorPlugin(EditorNode *p_node) { editor = p_node; - bake = memnew(Button); + bake = memnew(ToolButton); bake->set_icon(editor->get_gui_base()->get_icon("Bake", "EditorIcons")); bake->set_text(TTR("Bake Lightmaps")); bake->hide(); diff --git a/editor/plugins/baked_lightmap_editor_plugin.h b/editor/plugins/baked_lightmap_editor_plugin.h index a32b573851..8d3b5b1dd6 100644 --- a/editor/plugins/baked_lightmap_editor_plugin.h +++ b/editor/plugins/baked_lightmap_editor_plugin.h @@ -42,7 +42,7 @@ class BakedLightmapEditorPlugin : public EditorPlugin { BakedLightmap *lightmap; - Button *bake; + ToolButton *bake; EditorNode *editor; static EditorProgress *tmp_progress; diff --git a/editor/plugins/camera_editor_plugin.cpp b/editor/plugins/camera_editor_plugin.cpp index 37fbb54c30..3d8b24ccc7 100644 --- a/editor/plugins/camera_editor_plugin.cpp +++ b/editor/plugins/camera_editor_plugin.cpp @@ -32,18 +32,6 @@ #include "spatial_editor_plugin.h" -void CameraEditor::_notification(int p_what) { - - switch (p_what) { - - /* case NOTIFICATION_PROCESS: { - - if (preview->is_pressed() && node) - node->call("make_current"); - - } break;*/ - } -} void CameraEditor::_node_removed(Node *p_node) { if (p_node == node) { diff --git a/editor/plugins/camera_editor_plugin.h b/editor/plugins/camera_editor_plugin.h index 275624beeb..0340808c9a 100644 --- a/editor/plugins/camera_editor_plugin.h +++ b/editor/plugins/camera_editor_plugin.h @@ -50,7 +50,6 @@ class CameraEditor : public Control { void _pressed(); protected: - void _notification(int p_what); void _node_removed(Node *p_node); static void _bind_methods(); diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 72248b62a3..45f7f36cbd 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -30,15 +30,15 @@ #include "canvas_item_editor_plugin.h" +#include "core/os/input.h" +#include "core/os/keyboard.h" +#include "core/print_string.h" +#include "core/project_settings.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" #include "editor/plugins/animation_player_editor_plugin.h" #include "editor/plugins/script_editor_plugin.h" #include "editor/script_editor_debugger.h" -#include "os/input.h" -#include "os/keyboard.h" -#include "print_string.h" -#include "project_settings.h" #include "scene/2d/light_2d.h" #include "scene/2d/particles_2d.h" #include "scene/2d/polygon_2d.h" @@ -55,6 +55,7 @@ #define MAX_ZOOM 100 #define RULER_WIDTH 15 * EDSCALE +#define SCALE_HANDLE_DISTANCE 25 class SnapDialog : public ConfirmationDialog { @@ -178,6 +179,10 @@ void CanvasItemEditor::_snap_if_closer_float(float p_value, float p_target_snap, } } +bool CanvasItemEditor::_is_node_editable(const Node *p_node) { + return (!(p_node->has_meta("_edit_lock_") && p_node->get_meta("_edit_lock_")) && !(ClassDB::is_parent_class(p_node->get_parent()->get_class_name(), "Container") && ClassDB::is_parent_class(p_node->get_class_name(), "Control"))); +} + void CanvasItemEditor::_snap_if_closer_point(Point2 p_value, Point2 p_target_snap, Point2 &r_current_snap, bool (&r_snapped)[2], real_t rotation, float p_radius) { Transform2D rot_trans = Transform2D(rotation, Point2()); p_value = rot_trans.inverse().xform(p_value); @@ -335,10 +340,11 @@ void CanvasItemEditor::_unhandled_key_input(const Ref<InputEvent> &p_ev) { if (!is_visible_in_tree() || get_viewport()->gui_has_modal_stack()) return; - if (k->get_control()) - return; + if (k->get_scancode() == KEY_CONTROL || k->get_scancode() == KEY_ALT || k->get_scancode() == KEY_SHIFT) { + viewport->update(); + } - if (k->is_pressed() && !k->is_echo()) { + if (k->is_pressed() && !k->get_control() && !k->is_echo()) { if ((snap_grid || show_grid) && multiply_grid_step_shortcut.is_valid() && multiply_grid_step_shortcut->is_shortcut(p_ev)) { // Multiply the grid size grid_step_multiplier = MIN(grid_step_multiplier + 1, 12); @@ -409,7 +415,7 @@ void CanvasItemEditor::_expand_encompassing_rect_using_children(Rect2 &r_rect, c } } - if (canvas_item && canvas_item->is_visible_in_tree() && (include_locked_nodes || !canvas_item->has_meta("_edit_lock_"))) { + if (canvas_item && canvas_item->is_visible_in_tree() && (include_locked_nodes || !_is_node_editable(canvas_item))) { Transform2D xform = p_parent_xform * p_canvas_xform * canvas_item->get_transform(); Rect2 rect = canvas_item->_edit_get_rect(); if (r_first) { @@ -441,8 +447,12 @@ void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_no CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_node); for (int i = p_node->get_child_count() - 1; i >= 0; i--) { - if (canvas_item && !canvas_item->is_set_as_toplevel()) { - _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, p_limit, p_parent_xform * canvas_item->get_transform(), p_canvas_xform); + if (canvas_item) { + if (!canvas_item->is_set_as_toplevel()) { + _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, p_limit, p_parent_xform * canvas_item->get_transform(), p_canvas_xform); + } else { + _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, p_limit, canvas_item->get_transform(), p_canvas_xform); + } } else { CanvasLayer *cl = Object::cast_to<CanvasLayer>(p_node); _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, p_limit, Transform2D(), cl ? cl->get_transform() : p_canvas_xform); @@ -503,7 +513,7 @@ void CanvasItemEditor::_get_canvas_items_at_pos(const Point2 &p_pos, Vector<_Sel } //Remove the item if invalid - if (!canvas_item || duplicate || (canvas_item != scene && canvas_item->get_owner() != scene && !scene->is_editable_instance(canvas_item->get_owner())) || (canvas_item->has_meta("_edit_lock_") && canvas_item->get_meta("_edit_lock_"))) { + if (!canvas_item || duplicate || (canvas_item != scene && canvas_item->get_owner() != scene && !scene->is_editable_instance(canvas_item->get_owner())) || !_is_node_editable(canvas_item)) { r_items.remove(i); i--; } else { @@ -604,12 +614,16 @@ void CanvasItemEditor::_find_canvas_items_in_rect(const Rect2 &p_rect, Node *p_n bool editable = p_node == scene || p_node->get_owner() == scene || scene->is_editable_instance(p_node->get_owner()); bool lock_children = p_node->has_meta("_edit_group_") && p_node->get_meta("_edit_group_"); - bool locked = p_node->has_meta("_edit_lock_") && p_node->get_meta("_edit_lock_"); + bool locked = !_is_node_editable(p_node); 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()) { - _find_canvas_items_in_rect(p_rect, p_node->get_child(i), r_items, p_parent_xform * canvas_item->get_transform(), p_canvas_xform); + if (canvas_item) { + if (!canvas_item->is_set_as_toplevel()) { + _find_canvas_items_in_rect(p_rect, p_node->get_child(i), r_items, p_parent_xform * canvas_item->get_transform(), p_canvas_xform); + } else { + _find_canvas_items_in_rect(p_rect, p_node->get_child(i), r_items, canvas_item->get_transform(), p_canvas_xform); + } } else { CanvasLayer *canvas_layer = Object::cast_to<CanvasLayer>(p_node); _find_canvas_items_in_rect(p_rect, p_node->get_child(i), r_items, Transform2D(), canvas_layer ? canvas_layer->get_transform() : p_canvas_xform); @@ -667,7 +681,7 @@ List<CanvasItem *> CanvasItemEditor::_get_edited_canvas_items(bool retreive_lock List<CanvasItem *> selection; for (Map<Node *, Object *>::Element *E = editor_selection->get_selection().front(); E; E = E->next()) { CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->key()); - if (canvas_item && canvas_item->is_visible_in_tree() && canvas_item->get_viewport() == EditorNode::get_singleton()->get_scene_root() && (!retreive_locked || !canvas_item->has_meta("_edit_lock_"))) { + if (canvas_item && canvas_item->is_visible_in_tree() && canvas_item->get_viewport() == EditorNode::get_singleton()->get_scene_root() && (retreive_locked || _is_node_editable(canvas_item))) { CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); if (se) { selection.push_back(canvas_item); @@ -705,6 +719,46 @@ Vector2 CanvasItemEditor::_position_to_anchor(const Control *p_control, Vector2 return (p_control->get_transform().xform(position) - parent_rect.position) / parent_rect.size; } +void CanvasItemEditor::_save_canvas_item_ik_chain(const CanvasItem *p_canvas_item, List<float> *p_bones_length, List<Dictionary> *p_bones_state) { + if (p_bones_length) + *p_bones_length = List<float>(); + if (p_bones_state) + *p_bones_state = List<Dictionary>(); + + const Node2D *bone = Object::cast_to<Node2D>(p_canvas_item); + if (bone && bone->has_meta("_edit_bone_")) { + // Check if we have an IK chain + List<const Node2D *> bone_ik_list; + bool ik_found = false; + bone = Object::cast_to<Node2D>(bone->get_parent()); + while (bone) { + bone_ik_list.push_back(bone); + if (bone->has_meta("_edit_ik_")) { + ik_found = true; + break; + } else if (!bone->has_meta("_edit_bone_")) { + break; + } + bone = Object::cast_to<Node2D>(bone->get_parent()); + } + + //Save the bone state and length if we have an IK chain + if (ik_found) { + bone = Object::cast_to<Node2D>(p_canvas_item); + Transform2D bone_xform = bone->get_global_transform(); + for (List<const Node2D *>::Element *bone_E = bone_ik_list.front(); bone_E; bone_E = bone_E->next()) { + bone_xform = bone_xform * bone->get_transform().affine_inverse(); + const Node2D *parent_bone = bone_E->get(); + if (p_bones_length) + p_bones_length->push_back(parent_bone->get_global_transform().get_origin().distance_to(bone->get_global_position())); + if (p_bones_state) + p_bones_state->push_back(parent_bone->_edit_get_state()); + bone = parent_bone; + } + } + } +} + void CanvasItemEditor::_save_canvas_item_state(List<CanvasItem *> p_canvas_items, bool save_bones) { for (List<CanvasItem *>::Element *E = p_canvas_items.front(); E; E = E->next()) { CanvasItem *canvas_item = E->get(); @@ -717,54 +771,28 @@ void CanvasItemEditor::_save_canvas_item_state(List<CanvasItem *> p_canvas_items } else { se->pre_drag_rect = Rect2(); } - se->pre_drag_bones_length = List<float>(); - se->pre_drag_bones_undo_state = List<Dictionary>(); // If we have a bone, save the state of all nodes in the IK chain - Node2D *bone = Object::cast_to<Node2D>(canvas_item); - if (bone && bone->has_meta("_edit_bone_")) { - // Check if we have an IK chain - List<Node2D *> bone_ik_list; - bool ik_found = false; - bone = Object::cast_to<Node2D>(bone->get_parent()); - while (bone) { - bone_ik_list.push_back(bone); - if (bone->has_meta("_edit_ik_")) { - ik_found = true; - break; - } else if (!bone->has_meta("_edit_bone_")) { - break; - } - bone = Object::cast_to<Node2D>(bone->get_parent()); - } - - //Save the bone state and length if we have an IK chain - if (ik_found) { - bone = Object::cast_to<Node2D>(canvas_item); - Transform2D bone_xform = bone->get_global_transform(); - for (List<Node2D *>::Element *bone_E = bone_ik_list.front(); bone_E; bone_E = bone_E->next()) { - bone_xform = bone_xform * bone->get_transform().affine_inverse(); - Node2D *parent_bone = bone_E->get(); - se->pre_drag_bones_length.push_back(parent_bone->get_global_transform().get_origin().distance_to(bone->get_global_position())); - se->pre_drag_bones_undo_state.push_back(parent_bone->_edit_get_state()); - bone = parent_bone; - } - } - } + _save_canvas_item_ik_chain(canvas_item, &(se->pre_drag_bones_length), &(se->pre_drag_bones_undo_state)); } } } +void CanvasItemEditor::_restore_canvas_item_ik_chain(CanvasItem *p_canvas_item, const List<Dictionary> *p_bones_state) { + CanvasItem *canvas_item = p_canvas_item; + for (const List<Dictionary>::Element *E = p_bones_state->front(); E; E = E->next()) { + canvas_item = Object::cast_to<CanvasItem>(canvas_item->get_parent()); + canvas_item->_edit_set_state(E->get()); + } +} + void CanvasItemEditor::_restore_canvas_item_state(List<CanvasItem *> p_canvas_items, bool restore_bones) { for (List<CanvasItem *>::Element *E = drag_selection.front(); E; E = E->next()) { CanvasItem *canvas_item = E->get(); CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); canvas_item->_edit_set_state(se->undo_state); if (restore_bones) { - for (List<Dictionary>::Element *E = se->pre_drag_bones_undo_state.front(); E; E = E->next()) { - canvas_item = Object::cast_to<CanvasItem>(canvas_item->get_parent()); - canvas_item->_edit_set_state(E->get()); - } + _restore_canvas_item_ik_chain(canvas_item, &(se->pre_drag_bones_undo_state)); } } } @@ -997,8 +1025,7 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) { // Scroll or pan down if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) { view_offset.y += int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor(); - _update_scrollbars(); - viewport->update(); + update_viewport(); } else { _zoom_on_position(zoom * (1 - (0.05 * b->get_factor())), b->get_position()); } @@ -1009,8 +1036,7 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) { // Scroll or pan up if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) { view_offset.y -= int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor(); - _update_scrollbars(); - viewport->update(); + update_viewport(); } else { _zoom_on_position(zoom * ((0.95 + (0.05 * b->get_factor())) / 0.95), b->get_position()); } @@ -1021,8 +1047,7 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) { // Pan left if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) { view_offset.x -= int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor(); - _update_scrollbars(); - viewport->update(); + update_viewport(); return true; } } @@ -1031,8 +1056,7 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) { // Pan right if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) { view_offset.x += int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor(); - _update_scrollbars(); - viewport->update(); + update_viewport(); return true; } } @@ -1084,8 +1108,7 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) { } view_offset.x -= relative.x / zoom; view_offset.y -= relative.y / zoom; - _update_scrollbars(); - viewport->update(); + update_viewport(); return true; } } @@ -1103,8 +1126,7 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) { const Vector2 delta = (int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom) * pan_gesture->get_delta(); view_offset.x += delta.x; view_offset.y += delta.y; - _update_scrollbars(); - viewport->update(); + update_viewport(); return true; } @@ -1189,73 +1211,72 @@ bool CanvasItemEditor::_gui_input_pivot(const Ref<InputEvent> &p_event) { void CanvasItemEditor::_solve_IK(Node2D *leaf_node, Point2 target_position) { CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(leaf_node); - if (se && !se->pre_drag_bones_undo_state.empty()) { - - // Build the node list - Point2 leaf_pos = target_position; - - List<Node2D *> joints_list; - List<Point2> joints_pos; - Node2D *joint = leaf_node; - Transform2D joint_transform = leaf_node->get_global_transform_with_canvas(); - for (int i = 0; i < se->pre_drag_bones_undo_state.size() + 1; i++) { - joints_list.push_back(joint); - joints_pos.push_back(joint_transform.get_origin()); - joint_transform = joint_transform * joint->get_transform().affine_inverse(); - joint = Object::cast_to<Node2D>(joint->get_parent()); - } - Point2 root_pos = joints_list.back()->get()->get_global_transform_with_canvas().get_origin(); - - // Restraints the node to a maximum distance is necessary - float total_len = 0; - for (List<float>::Element *E = se->pre_drag_bones_length.front(); E; E = E->next()) { - total_len += E->get(); - } - if ((root_pos.distance_to(leaf_pos)) > total_len) { - Vector2 rel = leaf_pos - root_pos; - rel = rel.normalized() * total_len; - leaf_pos = root_pos + rel; - } - joints_pos[0] = leaf_pos; - - // Run the solver - int solver_iterations = 64; - float solver_k = 0.3; - - // Build the position list - for (int i = 0; i < solver_iterations; i++) { - // Handle the leaf joint - int node_id = 0; + if (se) { + int nb_bones = se->pre_drag_bones_undo_state.size(); + if (nb_bones > 0) { + + // Build the node list + Point2 leaf_pos = target_position; + + List<Node2D *> joints_list; + List<Point2> joints_pos; + Node2D *joint = leaf_node; + Transform2D joint_transform = leaf_node->get_global_transform_with_canvas(); + for (int i = 0; i < nb_bones + 1; i++) { + joints_list.push_back(joint); + joints_pos.push_back(joint_transform.get_origin()); + joint_transform = joint_transform * joint->get_transform().affine_inverse(); + joint = Object::cast_to<Node2D>(joint->get_parent()); + } + Point2 root_pos = joints_list.back()->get()->get_global_transform_with_canvas().get_origin(); + + // Restraints the node to a maximum distance is necessary + float total_len = 0; for (List<float>::Element *E = se->pre_drag_bones_length.front(); E; E = E->next()) { - Vector2 direction = (joints_pos[node_id + 1] - joints_pos[node_id]).normalized(); - int len = E->get(); - if (E == se->pre_drag_bones_length.front()) { - joints_pos[1] = joints_pos[1].linear_interpolate(joints_pos[0] + len * direction, solver_k); - } else if (E == se->pre_drag_bones_length.back()) { - joints_pos[node_id] = joints_pos[node_id].linear_interpolate(joints_pos[node_id + 1] - len * direction, solver_k); - } else { - Vector2 center = (joints_pos[node_id + 1] + joints_pos[node_id]) / 2.0; - joints_pos[node_id] = joints_pos[node_id].linear_interpolate(center - (direction * len) / 2.0, solver_k); - joints_pos[node_id + 1] = joints_pos[node_id + 1].linear_interpolate(center + (direction * len) / 2.0, solver_k); + total_len += E->get(); + } + if ((root_pos.distance_to(leaf_pos)) > total_len) { + Vector2 rel = leaf_pos - root_pos; + rel = rel.normalized() * total_len; + leaf_pos = root_pos + rel; + } + joints_pos[0] = leaf_pos; + + // Run the solver + int solver_iterations = 64; + float solver_k = 0.3; + + // Build the position list + for (int i = 0; i < solver_iterations; i++) { + // Handle the leaf joint + int node_id = 0; + for (List<float>::Element *E = se->pre_drag_bones_length.front(); E; E = E->next()) { + Vector2 direction = (joints_pos[node_id + 1] - joints_pos[node_id]).normalized(); + int len = E->get(); + if (E == se->pre_drag_bones_length.front()) { + joints_pos[1] = joints_pos[1].linear_interpolate(joints_pos[0] + len * direction, solver_k); + } else if (E == se->pre_drag_bones_length.back()) { + joints_pos[node_id] = joints_pos[node_id].linear_interpolate(joints_pos[node_id + 1] - len * direction, solver_k); + } else { + Vector2 center = (joints_pos[node_id + 1] + joints_pos[node_id]) / 2.0; + joints_pos[node_id] = joints_pos[node_id].linear_interpolate(center - (direction * len) / 2.0, solver_k); + joints_pos[node_id + 1] = joints_pos[node_id + 1].linear_interpolate(center + (direction * len) / 2.0, solver_k); + } + node_id++; } - node_id++; } - } - // Set the position - float total_rot = 0.0f; - for (int node_id = joints_list.size() - 1; node_id > 0; node_id--) { - Point2 current = (joints_list[node_id - 1]->get_global_position() - joints_list[node_id]->get_global_position()).normalized(); - Point2 target = (joints_pos[node_id - 1] - joints_list[node_id]->get_global_position()).normalized(); - float rot = current.angle_to(target); - if (joints_list[node_id]->get_global_transform().basis_determinant() < 0) { - rot = -rot; + // Set the position + for (int node_id = joints_list.size() - 1; node_id > 0; node_id--) { + Point2 current = (joints_list[node_id - 1]->get_global_position() - joints_list[node_id]->get_global_position()).normalized(); + Point2 target = (joints_pos[node_id - 1] - joints_list[node_id]->get_global_position()).normalized(); + float rot = current.angle_to(target); + if (joints_list[node_id]->get_global_transform().basis_determinant() < 0) { + rot = -rot; + } + joints_list[node_id]->rotate(rot); } - joints_list[node_id]->rotate(rot); - total_rot += rot; } - - joints_list[0]->rotate(-total_rot); } } @@ -1267,7 +1288,7 @@ bool CanvasItemEditor::_gui_input_rotate(const Ref<InputEvent> &p_event) { if (drag_type == DRAG_NONE) { if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && b->is_pressed()) { drag_selection = _get_edited_canvas_items(); - if (drag_selection.size() > 0 && ((b->get_control() && tool == TOOL_SELECT) || tool == TOOL_ROTATE)) { + if (drag_selection.size() > 0 && ((b->get_control() && !b->get_alt() && tool == TOOL_SELECT) || tool == TOOL_ROTATE)) { drag_type = DRAG_ROTATE; drag_from = transform.affine_inverse().xform(b->get_position()); CanvasItem *canvas_item = drag_selection[0]; @@ -1615,6 +1636,109 @@ bool CanvasItemEditor::_gui_input_resize(const Ref<InputEvent> &p_event) { return false; } +bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) { + + Ref<InputEventMouseButton> b = p_event; + Ref<InputEventMouseMotion> m = p_event; + + // Drag resize handles + if (drag_type == DRAG_NONE) { + if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && b->is_pressed() && ((b->get_alt() && b->get_control()) || tool == TOOL_SCALE)) { + List<CanvasItem *> selection = _get_edited_canvas_items(); + if (selection.size() == 1) { + CanvasItem *canvas_item = selection[0]; + + Transform2D xform = transform * canvas_item->get_global_transform_with_canvas(); + Transform2D unscaled_transform = (xform * canvas_item->get_transform().affine_inverse() * Transform2D(canvas_item->_edit_get_rotation(), canvas_item->_edit_get_position())).orthonormalized(); + Transform2D simple_xform = viewport->get_transform() * unscaled_transform; + + drag_type = DRAG_SCALE_BOTH; + + Size2 scale_factor = Size2(SCALE_HANDLE_DISTANCE, SCALE_HANDLE_DISTANCE); + Rect2 x_handle_rect = Rect2(scale_factor.x * EDSCALE, -5 * EDSCALE, 10 * EDSCALE, 10 * EDSCALE); + if (x_handle_rect.has_point(simple_xform.affine_inverse().xform(b->get_position()))) { + drag_type = DRAG_SCALE_X; + } + Rect2 y_handle_rect = Rect2(-5 * EDSCALE, -(scale_factor.y + 10) * EDSCALE, 10 * EDSCALE, 10 * EDSCALE); + if (y_handle_rect.has_point(simple_xform.affine_inverse().xform(b->get_position()))) { + drag_type = DRAG_SCALE_Y; + } + + drag_from = transform.affine_inverse().xform(b->get_position()); + drag_selection = List<CanvasItem *>(); + drag_selection.push_back(canvas_item); + _save_canvas_item_state(drag_selection); + return true; + } + } + } + + if (drag_type == DRAG_SCALE_BOTH || drag_type == DRAG_SCALE_X || drag_type == DRAG_SCALE_Y) { + // Resize the node + if (m.is_valid()) { + _restore_canvas_item_state(drag_selection); + CanvasItem *canvas_item = drag_selection[0]; + + drag_to = transform.affine_inverse().xform(m->get_position()); + + Transform2D parent_xform = canvas_item->get_global_transform_with_canvas() * canvas_item->get_transform().affine_inverse(); + Transform2D unscaled_transform = (transform * parent_xform * Transform2D(canvas_item->_edit_get_rotation(), canvas_item->_edit_get_position())).orthonormalized(); + Transform2D simple_xform = (viewport->get_transform() * unscaled_transform).affine_inverse() * transform; + + bool uniform = m->get_shift(); + + Point2 drag_from_local = simple_xform.xform(drag_from); + Point2 drag_to_local = simple_xform.xform(drag_to); + Point2 offset = drag_to_local - drag_from_local; + + Size2 scale = canvas_item->call("get_scale"); + float ratio = scale.y / scale.x; + if (drag_type == DRAG_SCALE_BOTH) { + Size2 scale_factor = drag_to_local / drag_from_local; + if (uniform) { + scale *= (scale_factor.x + scale_factor.y) / 2.0; + } else { + scale *= scale_factor; + } + } else { + Size2 scale_factor = Vector2(offset.x, -offset.y) / SCALE_HANDLE_DISTANCE; + Size2 parent_scale = parent_xform.get_scale(); + scale_factor *= Vector2(1.0 / parent_scale.x, 1.0 / parent_scale.y); + if (drag_type == DRAG_SCALE_X) { + scale.x += scale_factor.x; + if (uniform) { + scale.y = scale.x * ratio; + } + } else if (drag_type == DRAG_SCALE_Y) { + scale.y += scale_factor.y; + if (uniform) { + scale.x = scale.y / ratio; + } + } + } + canvas_item->call("set_scale", scale); + return true; + } + + // Confirm resize + if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && !b->is_pressed()) { + _commit_canvas_item_state(drag_selection, TTR("Scale CanvasItem")); + drag_type = DRAG_NONE; + viewport->update(); + return true; + } + + // Cancel a drag + if (b.is_valid() && b->get_button_index() == BUTTON_RIGHT && b->is_pressed()) { + _restore_canvas_item_state(drag_selection); + drag_type = DRAG_NONE; + viewport->update(); + return true; + } + } + return false; +} + bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> b = p_event; Ref<InputEventMouseMotion> m = p_event; @@ -1624,8 +1748,8 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) { //Start moving the nodes if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && b->is_pressed()) { List<CanvasItem *> selection = _get_edited_canvas_items(); - if ((b->get_alt() || tool == TOOL_MOVE) && selection.size() > 0) { - drag_type = DRAG_ALL; + if (((b->get_alt() && !b->get_control()) || tool == TOOL_MOVE) && selection.size() > 0) { + drag_type = DRAG_MOVE; drag_from = transform.affine_inverse().xform(b->get_position()); drag_selection = selection; _save_canvas_item_state(drag_selection); @@ -1634,9 +1758,18 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) { } } - if (drag_type == DRAG_ALL) { + if (drag_type == DRAG_MOVE) { // Move the nodes if (m.is_valid()) { + + // Save the ik chain for reapplying before IK solve + Vector<List<Dictionary> > all_bones_ik_states; + for (List<CanvasItem *>::Element *E = drag_selection.front(); E; E = E->next()) { + List<Dictionary> bones_ik_states; + _save_canvas_item_ik_chain(E->get(), NULL, &bones_ik_states); + all_bones_ik_states.push_back(bones_ik_states); + } + _restore_canvas_item_state(drag_selection, true); drag_to = transform.affine_inverse().xform(m->get_position()); @@ -1658,6 +1791,7 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) { } bool force_no_IK = m->get_alt(); + int index = 0; for (List<CanvasItem *>::Element *E = drag_selection.front(); E; E = E->next()) { CanvasItem *canvas_item = E->get(); CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); @@ -1665,16 +1799,21 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) { Node2D *node2d = Object::cast_to<Node2D>(canvas_item); if (node2d && se->pre_drag_bones_undo_state.size() > 0 && !force_no_IK) { + real_t initial_leaf_node_rotation = node2d->get_global_transform_with_canvas().get_rotation(); + _restore_canvas_item_ik_chain(node2d, &(all_bones_ik_states[index])); + real_t final_leaf_node_rotation = node2d->get_global_transform_with_canvas().get_rotation(); + node2d->rotate(initial_leaf_node_rotation - final_leaf_node_rotation); _solve_IK(node2d, new_pos); } else { canvas_item->_edit_set_position(canvas_item->_edit_get_position() + xform.xform(new_pos) - xform.xform(previous_pos)); } + index++; } return true; } // Confirm the move (only if it was moved) - if (b.is_valid() && !b->is_pressed() && b->get_button_index() == BUTTON_LEFT && (drag_type == DRAG_ALL)) { + if (b.is_valid() && !b->is_pressed() && b->get_button_index() == BUTTON_LEFT && (drag_type == DRAG_MOVE)) { if (transform.affine_inverse().xform(b->get_position()) != drag_from) { _commit_canvas_item_state(drag_selection, TTR("Move CanvasItem"), true); } @@ -1707,6 +1846,14 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) { if (drag_selection.size() > 0) { + // Save the ik chain for reapplying before IK solve + Vector<List<Dictionary> > all_bones_ik_states; + for (List<CanvasItem *>::Element *E = drag_selection.front(); E; E = E->next()) { + List<Dictionary> bones_ik_states; + _save_canvas_item_ik_chain(E->get(), NULL, &bones_ik_states); + all_bones_ik_states.push_back(bones_ik_states); + } + _restore_canvas_item_state(drag_selection, true); bool move_local_base = k->get_alt(); @@ -1752,6 +1899,7 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) { new_pos = previous_pos + (drag_to - drag_from); } + int index = 0; for (List<CanvasItem *>::Element *E = drag_selection.front(); E; E = E->next()) { CanvasItem *canvas_item = E->get(); CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); @@ -1759,10 +1907,15 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) { Node2D *node2d = Object::cast_to<Node2D>(canvas_item); if (node2d && se->pre_drag_bones_undo_state.size() > 0) { + real_t initial_leaf_node_rotation = node2d->get_global_transform_with_canvas().get_rotation(); + _restore_canvas_item_ik_chain(node2d, &(all_bones_ik_states[index])); + real_t final_leaf_node_rotation = node2d->get_global_transform_with_canvas().get_rotation(); + node2d->rotate(initial_leaf_node_rotation - final_leaf_node_rotation); _solve_IK(node2d, new_pos); } else { canvas_item->_edit_set_position(canvas_item->_edit_get_position() + xform.xform(new_pos) - xform.xform(previous_pos)); } + index++; } } return true; @@ -1820,11 +1973,7 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) { for (int i = 0; i < selection_results.size(); i++) { CanvasItem *item = selection_results[i].item; - Ref<Texture> icon; - if (item->has_meta("_editor_icon")) - icon = item->get_meta("_editor_icon"); - else - icon = get_icon(has_icon(item->get_class(), "EditorIcons") ? item->get_class() : String("Object"), "EditorIcons"); + Ref<Texture> icon = EditorNode::get_singleton()->get_object_icon(item, "Node"); String node_path = "/" + root_name + "/" + root_path.rel_path_to(item->get_path()); selection_menu->add_item(item->get_name()); @@ -1858,22 +2007,9 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) { // Retrieve the bones _get_bones_at_pos(click, selection); - for (int i = 0; i < selection.size(); i++) { - if (editor_selection->is_selected(selection[i].item)) { - // Drag the node(s) if requested - List<CanvasItem *> selection = _get_edited_canvas_items(); - - drag_type = DRAG_ALL; - drag_selection = selection; - drag_from = click; - _save_canvas_item_state(drag_selection); - - return true; - } - } - - if (!selection.empty()) + if (!selection.empty()) { canvas_item = selection[0].item; + } if (!canvas_item) { // Start a box selection @@ -1894,7 +2030,7 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) { // Drag the node(s) if requested List<CanvasItem *> selection = _get_edited_canvas_items(); - drag_type = DRAG_ALL; + drag_type = DRAG_MOVE; drag_selection = selection; drag_from = click; _save_canvas_item_state(drag_selection); @@ -1974,10 +2110,7 @@ bool CanvasItemEditor::_gui_input_hover(const Ref<InputEvent> &p_event) { _HoverResult hover_result; hover_result.position = canvas_item->get_global_transform_with_canvas().get_origin(); - if (has_icon(canvas_item->get_class(), "EditorIcons")) - hover_result.icon = get_icon(canvas_item->get_class(), "EditorIcons"); - else - hover_result.icon = get_icon("Object", "EditorIcons"); + hover_result.icon = EditorNode::get_singleton()->get_object_icon(canvas_item); hover_result.name = canvas_item->get_name(); hovering_results_tmp.push_back(hover_result); @@ -2019,6 +2152,8 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) { //printf("Open scene on double click\n"); } else if ((accepted = _gui_input_anchors(p_event))) { //printf("Anchors\n"); + } else if ((accepted = _gui_input_scale(p_event))) { + //printf("Set scale\n"); } else if ((accepted = _gui_input_pivot(p_event))) { //printf("Set pivot\n"); } else if ((accepted = _gui_input_resize(p_event))) { @@ -2073,7 +2208,7 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) { case DRAG_BOTTOM_LEFT: c = CURSOR_BDIAGSIZE; break; - case DRAG_ALL: + case DRAG_MOVE: c = CURSOR_MOVE; break; case DRAG_PAN: @@ -2235,7 +2370,7 @@ void CanvasItemEditor::_draw_rulers() { if (i % minor_subdivision == 0) { viewport->draw_line(Point2(position.x, RULER_WIDTH * 0.33), Point2(position.x, RULER_WIDTH), graduation_color); } else { - viewport->draw_line(Point2(position.x, RULER_WIDTH * 0.66), Point2(position.x, RULER_WIDTH), graduation_color); + viewport->draw_line(Point2(position.x, RULER_WIDTH * 0.75), Point2(position.x, RULER_WIDTH), graduation_color); } } } @@ -2247,12 +2382,17 @@ void CanvasItemEditor::_draw_rulers() { if (i % (major_subdivision * minor_subdivision) == 0) { viewport->draw_line(Point2(0, position.y), Point2(RULER_WIDTH, position.y), graduation_color); float val = (ruler_transform * major_subdivide * minor_subdivide).xform(Point2(0, i)).y; - viewport->draw_string(font, Point2(2, position.y + 2 + font->get_height()), vformat(((int)val == val) ? "%d" : "%.1f", val), font_color); + + Transform2D text_xform = Transform2D(-Math_PI / 2.0, Point2(font->get_height(), position.y - 2)); + viewport->draw_set_transform_matrix(viewport->get_transform() * text_xform); + viewport->draw_string(font, Point2(), vformat(((int)val == val) ? "%d" : "%.1f", val), font_color); + viewport->draw_set_transform_matrix(viewport->get_transform()); + } else { if (i % minor_subdivision == 0) { viewport->draw_line(Point2(RULER_WIDTH * 0.33, position.y), Point2(RULER_WIDTH, position.y), graduation_color); } else { - viewport->draw_line(Point2(RULER_WIDTH * 0.66, position.y), Point2(RULER_WIDTH, position.y), graduation_color); + viewport->draw_line(Point2(RULER_WIDTH * 0.75, position.y), Point2(RULER_WIDTH, position.y), graduation_color); } } } @@ -2301,6 +2441,188 @@ void CanvasItemEditor::_draw_grid() { } } +void CanvasItemEditor::_draw_control_helpers(Control *control) { + Transform2D xform = transform * control->get_global_transform_with_canvas(); + RID ci = viewport->get_canvas_item(); + if (tool == TOOL_SELECT && show_helpers && !Object::cast_to<Container>(control->get_parent())) { + // Draw the helpers + Color color_base = Color(0.8, 0.8, 0.8, 0.5); + + float anchors_values[4]; + anchors_values[0] = control->get_anchor(MARGIN_LEFT); + anchors_values[1] = control->get_anchor(MARGIN_TOP); + anchors_values[2] = control->get_anchor(MARGIN_RIGHT); + anchors_values[3] = control->get_anchor(MARGIN_BOTTOM); + + // Draw the anchors + Vector2 anchors[4]; + Vector2 anchors_pos[4]; + for (int i = 0; i < 4; i++) { + anchors[i] = Vector2((i % 2 == 0) ? anchors_values[i] : anchors_values[(i + 1) % 4], (i % 2 == 1) ? anchors_values[i] : anchors_values[(i + 1) % 4]); + anchors_pos[i] = xform.xform(_anchor_to_position(control, anchors[i])); + } + + // Get which anchor is dragged + int dragged_anchor = -1; + switch (drag_type) { + case DRAG_ANCHOR_ALL: + case DRAG_ANCHOR_TOP_LEFT: + dragged_anchor = 0; + break; + case DRAG_ANCHOR_TOP_RIGHT: + dragged_anchor = 1; + break; + case DRAG_ANCHOR_BOTTOM_RIGHT: + dragged_anchor = 2; + break; + case DRAG_ANCHOR_BOTTOM_LEFT: + dragged_anchor = 3; + break; + default: + break; + } + + if (dragged_anchor >= 0) { + // Draw the 4 lines when dragged + bool snapped; + Color color_snapped = Color(0.64, 0.93, 0.67, 0.5); + + Vector2 corners_pos[4]; + for (int i = 0; i < 4; i++) { + corners_pos[i] = xform.xform(_anchor_to_position(control, Vector2((i == 0 || i == 3) ? ANCHOR_BEGIN : ANCHOR_END, (i <= 1) ? ANCHOR_BEGIN : ANCHOR_END))); + } + + Vector2 line_starts[4]; + Vector2 line_ends[4]; + for (int i = 0; i < 4; i++) { + float anchor_val = (i >= 2) ? ANCHOR_END - anchors_values[i] : anchors_values[i]; + line_starts[i] = Vector2::linear_interpolate(corners_pos[i], corners_pos[(i + 1) % 4], anchor_val); + line_ends[i] = Vector2::linear_interpolate(corners_pos[(i + 3) % 4], corners_pos[(i + 2) % 4], anchor_val); + snapped = anchors_values[i] == 0.0 || anchors_values[i] == 0.5 || anchors_values[i] == 1.0; + viewport->draw_line(line_starts[i], line_ends[i], snapped ? color_snapped : color_base, (i == dragged_anchor || (i + 3) % 4 == dragged_anchor) ? 2 : 1); + } + + // Display the percentages next to the lines + float percent_val; + percent_val = anchors_values[(dragged_anchor + 2) % 4] - anchors_values[dragged_anchor]; + percent_val = (dragged_anchor >= 2) ? -percent_val : percent_val; + _draw_percentage_at_position(percent_val, (anchors_pos[dragged_anchor] + anchors_pos[(dragged_anchor + 1) % 4]) / 2, (Margin)((dragged_anchor + 1) % 4)); + + percent_val = anchors_values[(dragged_anchor + 3) % 4] - anchors_values[(dragged_anchor + 1) % 4]; + percent_val = ((dragged_anchor + 1) % 4 >= 2) ? -percent_val : percent_val; + _draw_percentage_at_position(percent_val, (anchors_pos[dragged_anchor] + anchors_pos[(dragged_anchor + 3) % 4]) / 2, (Margin)(dragged_anchor)); + + percent_val = anchors_values[(dragged_anchor + 1) % 4]; + percent_val = ((dragged_anchor + 1) % 4 >= 2) ? ANCHOR_END - percent_val : percent_val; + _draw_percentage_at_position(percent_val, (line_starts[dragged_anchor] + anchors_pos[dragged_anchor]) / 2, (Margin)(dragged_anchor)); + + percent_val = anchors_values[dragged_anchor]; + percent_val = (dragged_anchor >= 2) ? ANCHOR_END - percent_val : percent_val; + _draw_percentage_at_position(percent_val, (line_ends[(dragged_anchor + 1) % 4] + anchors_pos[dragged_anchor]) / 2, (Margin)((dragged_anchor + 1) % 4)); + } + + Rect2 anchor_rects[4]; + anchor_rects[0] = Rect2(anchors_pos[0] - anchor_handle->get_size(), anchor_handle->get_size()); + anchor_rects[1] = Rect2(anchors_pos[1] - Vector2(0.0, anchor_handle->get_size().y), Point2(-anchor_handle->get_size().x, anchor_handle->get_size().y)); + anchor_rects[2] = Rect2(anchors_pos[2], -anchor_handle->get_size()); + anchor_rects[3] = Rect2(anchors_pos[3] - Vector2(anchor_handle->get_size().x, 0.0), Point2(anchor_handle->get_size().x, -anchor_handle->get_size().y)); + + for (int i = 0; i < 4; i++) { + anchor_handle->draw_rect(ci, anchor_rects[i]); + } + + // Draw the margin values and the node width/height when dragging control side + float ratio = 0.33; + Transform2D parent_transform = xform * control->get_transform().affine_inverse(); + float node_pos_in_parent[4]; + + Rect2 parent_rect = control->get_parent_anchorable_rect(); + + node_pos_in_parent[0] = control->get_anchor(MARGIN_LEFT) * parent_rect.size.width + control->get_margin(MARGIN_LEFT) + parent_rect.position.x; + node_pos_in_parent[1] = control->get_anchor(MARGIN_TOP) * parent_rect.size.height + control->get_margin(MARGIN_TOP) + parent_rect.position.y; + node_pos_in_parent[2] = control->get_anchor(MARGIN_RIGHT) * parent_rect.size.width + control->get_margin(MARGIN_RIGHT) + parent_rect.position.x; + node_pos_in_parent[3] = control->get_anchor(MARGIN_BOTTOM) * parent_rect.size.height + control->get_margin(MARGIN_BOTTOM) + parent_rect.position.y; + + Point2 start, end; + switch (drag_type) { + case DRAG_LEFT: + case DRAG_TOP_LEFT: + case DRAG_BOTTOM_LEFT: + _draw_margin_at_position(control->get_size().width, parent_transform.xform(Vector2((node_pos_in_parent[0] + node_pos_in_parent[2]) / 2, node_pos_in_parent[3])) + Vector2(0, 5), MARGIN_BOTTOM); + case DRAG_MOVE: + start = Vector2(node_pos_in_parent[0], Math::lerp(node_pos_in_parent[1], node_pos_in_parent[3], ratio)); + end = start - Vector2(control->get_margin(MARGIN_LEFT), 0); + _draw_margin_at_position(control->get_margin(MARGIN_LEFT), parent_transform.xform((start + end) / 2), MARGIN_TOP); + viewport->draw_line(parent_transform.xform(start), parent_transform.xform(end), color_base, 1); + break; + default: + break; + } + switch (drag_type) { + case DRAG_RIGHT: + case DRAG_TOP_RIGHT: + case DRAG_BOTTOM_RIGHT: + _draw_margin_at_position(control->get_size().width, parent_transform.xform(Vector2((node_pos_in_parent[0] + node_pos_in_parent[2]) / 2, node_pos_in_parent[3])) + Vector2(0, 5), MARGIN_BOTTOM); + case DRAG_MOVE: + start = Vector2(node_pos_in_parent[2], Math::lerp(node_pos_in_parent[3], node_pos_in_parent[1], ratio)); + end = start - Vector2(control->get_margin(MARGIN_RIGHT), 0); + _draw_margin_at_position(control->get_margin(MARGIN_RIGHT), parent_transform.xform((start + end) / 2), MARGIN_BOTTOM); + viewport->draw_line(parent_transform.xform(start), parent_transform.xform(end), color_base, 1); + break; + default: + break; + } + switch (drag_type) { + case DRAG_TOP: + case DRAG_TOP_LEFT: + case DRAG_TOP_RIGHT: + _draw_margin_at_position(control->get_size().height, parent_transform.xform(Vector2(node_pos_in_parent[2], (node_pos_in_parent[1] + node_pos_in_parent[3]) / 2)) + Vector2(5, 0), MARGIN_RIGHT); + case DRAG_MOVE: + start = Vector2(Math::lerp(node_pos_in_parent[0], node_pos_in_parent[2], ratio), node_pos_in_parent[1]); + end = start - Vector2(0, control->get_margin(MARGIN_TOP)); + _draw_margin_at_position(control->get_margin(MARGIN_TOP), parent_transform.xform((start + end) / 2), MARGIN_LEFT); + viewport->draw_line(parent_transform.xform(start), parent_transform.xform(end), color_base, 1); + break; + default: + break; + } + switch (drag_type) { + case DRAG_BOTTOM: + case DRAG_BOTTOM_LEFT: + case DRAG_BOTTOM_RIGHT: + _draw_margin_at_position(control->get_size().height, parent_transform.xform(Vector2(node_pos_in_parent[2], (node_pos_in_parent[1] + node_pos_in_parent[3]) / 2) + Vector2(5, 0)), MARGIN_RIGHT); + case DRAG_MOVE: + start = Vector2(Math::lerp(node_pos_in_parent[2], node_pos_in_parent[0], ratio), node_pos_in_parent[3]); + end = start - Vector2(0, control->get_margin(MARGIN_BOTTOM)); + _draw_margin_at_position(control->get_margin(MARGIN_BOTTOM), parent_transform.xform((start + end) / 2), MARGIN_RIGHT); + viewport->draw_line(parent_transform.xform(start), parent_transform.xform(end), color_base, 1); + break; + default: + break; + } + + switch (drag_type) { + //Draw the ghost rect if the node if rotated/scaled + case DRAG_LEFT: + case DRAG_TOP_LEFT: + case DRAG_TOP: + case DRAG_TOP_RIGHT: + case DRAG_RIGHT: + case DRAG_BOTTOM_RIGHT: + case DRAG_BOTTOM: + case DRAG_BOTTOM_LEFT: + case DRAG_MOVE: + if (control->get_rotation() != 0.0 || control->get_scale() != Vector2(1, 1)) { + Rect2 rect = Rect2(Vector2(node_pos_in_parent[0], node_pos_in_parent[1]), control->get_size()); + viewport->draw_rect(parent_transform.xform(rect), color_base, false); + } + break; + default: + break; + } + } +} + void CanvasItemEditor::_draw_selection() { Ref<Texture> pivot_icon = get_icon("EditorPivot", "EditorIcons"); Ref<Texture> position_icon = get_icon("EditorPosition", "EditorIcons"); @@ -2317,7 +2639,7 @@ void CanvasItemEditor::_draw_selection() { // Draw the previous position if we are dragging the node if (show_helpers && - (drag_type == DRAG_ALL || drag_type == DRAG_ROTATE || + (drag_type == DRAG_MOVE || drag_type == DRAG_ROTATE || drag_type == DRAG_LEFT || drag_type == DRAG_RIGHT || drag_type == DRAG_TOP || drag_type == DRAG_BOTTOM || drag_type == DRAG_TOP_LEFT || drag_type == DRAG_TOP_RIGHT || drag_type == DRAG_BOTTOM_LEFT || drag_type == DRAG_BOTTOM_RIGHT)) { const Transform2D pre_drag_xform = transform * se->pre_drag_xform; @@ -2359,199 +2681,26 @@ void CanvasItemEditor::_draw_selection() { } } else { - Transform2D transform = Transform2D(xform.get_rotation(), xform.get_origin()); - viewport->draw_set_transform_matrix(transform); + Transform2D unscaled_transform = (xform * canvas_item->get_transform().affine_inverse() * Transform2D(canvas_item->_edit_get_rotation(), canvas_item->_edit_get_position())).orthonormalized(); + Transform2D simple_xform = viewport->get_transform() * unscaled_transform; + viewport->draw_set_transform_matrix(simple_xform); viewport->draw_texture(position_icon, -(position_icon->get_size() / 2)); - viewport->draw_set_transform_matrix(Transform2D()); + viewport->draw_set_transform_matrix(viewport->get_transform()); } - if (single && (tool == TOOL_SELECT || tool == TOOL_MOVE || tool == TOOL_ROTATE || tool == TOOL_EDIT_PIVOT)) { //kind of sucks + if (single && (tool == TOOL_SELECT || tool == TOOL_MOVE || tool == TOOL_SCALE || tool == TOOL_ROTATE || tool == TOOL_EDIT_PIVOT)) { //kind of sucks // Draw the pivot if (canvas_item->_edit_get_pivot() != Vector2() || drag_type == DRAG_PIVOT || tool == TOOL_EDIT_PIVOT) { // This is not really clean :/ viewport->draw_texture(pivot_icon, (xform.xform(canvas_item->_edit_get_pivot()) - (pivot_icon->get_size() / 2)).floor()); } + // Draw control-related helpers Control *control = Object::cast_to<Control>(canvas_item); if (control) { - if (tool == TOOL_SELECT && show_helpers && !Object::cast_to<Container>(control->get_parent())) { - // Draw the helpers - Color color_base = Color(0.8, 0.8, 0.8, 0.5); - - float anchors_values[4]; - anchors_values[0] = control->get_anchor(MARGIN_LEFT); - anchors_values[1] = control->get_anchor(MARGIN_TOP); - anchors_values[2] = control->get_anchor(MARGIN_RIGHT); - anchors_values[3] = control->get_anchor(MARGIN_BOTTOM); - - // Draw the anchors - Vector2 anchors[4]; - Vector2 anchors_pos[4]; - for (int i = 0; i < 4; i++) { - anchors[i] = Vector2((i % 2 == 0) ? anchors_values[i] : anchors_values[(i + 1) % 4], (i % 2 == 1) ? anchors_values[i] : anchors_values[(i + 1) % 4]); - anchors_pos[i] = xform.xform(_anchor_to_position(control, anchors[i])); - } - - // Get which anchor is dragged - int dragged_anchor = -1; - switch (drag_type) { - case DRAG_ANCHOR_ALL: - case DRAG_ANCHOR_TOP_LEFT: - dragged_anchor = 0; - break; - case DRAG_ANCHOR_TOP_RIGHT: - dragged_anchor = 1; - break; - case DRAG_ANCHOR_BOTTOM_RIGHT: - dragged_anchor = 2; - break; - case DRAG_ANCHOR_BOTTOM_LEFT: - dragged_anchor = 3; - break; - default: - break; - } - - if (dragged_anchor >= 0) { - // Draw the 4 lines when dragged - bool snapped; - Color color_snapped = Color(0.64, 0.93, 0.67, 0.5); - - Vector2 corners_pos[4]; - for (int i = 0; i < 4; i++) { - corners_pos[i] = xform.xform(_anchor_to_position(control, Vector2((i == 0 || i == 3) ? ANCHOR_BEGIN : ANCHOR_END, (i <= 1) ? ANCHOR_BEGIN : ANCHOR_END))); - } - - Vector2 line_starts[4]; - Vector2 line_ends[4]; - for (int i = 0; i < 4; i++) { - float anchor_val = (i >= 2) ? ANCHOR_END - anchors_values[i] : anchors_values[i]; - line_starts[i] = Vector2::linear_interpolate(corners_pos[i], corners_pos[(i + 1) % 4], anchor_val); - line_ends[i] = Vector2::linear_interpolate(corners_pos[(i + 3) % 4], corners_pos[(i + 2) % 4], anchor_val); - snapped = anchors_values[i] == 0.0 || anchors_values[i] == 0.5 || anchors_values[i] == 1.0; - viewport->draw_line(line_starts[i], line_ends[i], snapped ? color_snapped : color_base, (i == dragged_anchor || (i + 3) % 4 == dragged_anchor) ? 2 : 1); - } - - // Display the percentages next to the lines - float percent_val; - percent_val = anchors_values[(dragged_anchor + 2) % 4] - anchors_values[dragged_anchor]; - percent_val = (dragged_anchor >= 2) ? -percent_val : percent_val; - _draw_percentage_at_position(percent_val, (anchors_pos[dragged_anchor] + anchors_pos[(dragged_anchor + 1) % 4]) / 2, (Margin)((dragged_anchor + 1) % 4)); - - percent_val = anchors_values[(dragged_anchor + 3) % 4] - anchors_values[(dragged_anchor + 1) % 4]; - percent_val = ((dragged_anchor + 1) % 4 >= 2) ? -percent_val : percent_val; - _draw_percentage_at_position(percent_val, (anchors_pos[dragged_anchor] + anchors_pos[(dragged_anchor + 3) % 4]) / 2, (Margin)(dragged_anchor)); - - percent_val = anchors_values[(dragged_anchor + 1) % 4]; - percent_val = ((dragged_anchor + 1) % 4 >= 2) ? ANCHOR_END - percent_val : percent_val; - _draw_percentage_at_position(percent_val, (line_starts[dragged_anchor] + anchors_pos[dragged_anchor]) / 2, (Margin)(dragged_anchor)); - - percent_val = anchors_values[dragged_anchor]; - percent_val = (dragged_anchor >= 2) ? ANCHOR_END - percent_val : percent_val; - _draw_percentage_at_position(percent_val, (line_ends[(dragged_anchor + 1) % 4] + anchors_pos[dragged_anchor]) / 2, (Margin)((dragged_anchor + 1) % 4)); - } - - Rect2 anchor_rects[4]; - anchor_rects[0] = Rect2(anchors_pos[0] - anchor_handle->get_size(), anchor_handle->get_size()); - anchor_rects[1] = Rect2(anchors_pos[1] - Vector2(0.0, anchor_handle->get_size().y), Point2(-anchor_handle->get_size().x, anchor_handle->get_size().y)); - anchor_rects[2] = Rect2(anchors_pos[2], -anchor_handle->get_size()); - anchor_rects[3] = Rect2(anchors_pos[3] - Vector2(anchor_handle->get_size().x, 0.0), Point2(anchor_handle->get_size().x, -anchor_handle->get_size().y)); - - for (int i = 0; i < 4; i++) { - anchor_handle->draw_rect(ci, anchor_rects[i]); - } - - // Draw the margin values and the node width/height when dragging control side - float ratio = 0.33; - Transform2D parent_transform = xform * control->get_transform().affine_inverse(); - float node_pos_in_parent[4]; - - Rect2 parent_rect = control->get_parent_anchorable_rect(); - - node_pos_in_parent[0] = control->get_anchor(MARGIN_LEFT) * parent_rect.size.width + control->get_margin(MARGIN_LEFT) + parent_rect.position.x; - node_pos_in_parent[1] = control->get_anchor(MARGIN_TOP) * parent_rect.size.height + control->get_margin(MARGIN_TOP) + parent_rect.position.y; - node_pos_in_parent[2] = control->get_anchor(MARGIN_RIGHT) * parent_rect.size.width + control->get_margin(MARGIN_RIGHT) + parent_rect.position.x; - node_pos_in_parent[3] = control->get_anchor(MARGIN_BOTTOM) * parent_rect.size.height + control->get_margin(MARGIN_BOTTOM) + parent_rect.position.y; - - Point2 start, end; - switch (drag_type) { - case DRAG_LEFT: - case DRAG_TOP_LEFT: - case DRAG_BOTTOM_LEFT: - _draw_margin_at_position(control->get_size().width, parent_transform.xform(Vector2((node_pos_in_parent[0] + node_pos_in_parent[2]) / 2, node_pos_in_parent[3])) + Vector2(0, 5), MARGIN_BOTTOM); - case DRAG_ALL: - start = Vector2(node_pos_in_parent[0], Math::lerp(node_pos_in_parent[1], node_pos_in_parent[3], ratio)); - end = start - Vector2(control->get_margin(MARGIN_LEFT), 0); - _draw_margin_at_position(control->get_margin(MARGIN_LEFT), parent_transform.xform((start + end) / 2), MARGIN_TOP); - viewport->draw_line(parent_transform.xform(start), parent_transform.xform(end), color_base, 1); - break; - default: - break; - } - switch (drag_type) { - case DRAG_RIGHT: - case DRAG_TOP_RIGHT: - case DRAG_BOTTOM_RIGHT: - _draw_margin_at_position(control->get_size().width, parent_transform.xform(Vector2((node_pos_in_parent[0] + node_pos_in_parent[2]) / 2, node_pos_in_parent[3])) + Vector2(0, 5), MARGIN_BOTTOM); - case DRAG_ALL: - start = Vector2(node_pos_in_parent[2], Math::lerp(node_pos_in_parent[3], node_pos_in_parent[1], ratio)); - end = start - Vector2(control->get_margin(MARGIN_RIGHT), 0); - _draw_margin_at_position(control->get_margin(MARGIN_RIGHT), parent_transform.xform((start + end) / 2), MARGIN_BOTTOM); - viewport->draw_line(parent_transform.xform(start), parent_transform.xform(end), color_base, 1); - break; - default: - break; - } - switch (drag_type) { - case DRAG_TOP: - case DRAG_TOP_LEFT: - case DRAG_TOP_RIGHT: - _draw_margin_at_position(control->get_size().height, parent_transform.xform(Vector2(node_pos_in_parent[2], (node_pos_in_parent[1] + node_pos_in_parent[3]) / 2)) + Vector2(5, 0), MARGIN_RIGHT); - case DRAG_ALL: - start = Vector2(Math::lerp(node_pos_in_parent[0], node_pos_in_parent[2], ratio), node_pos_in_parent[1]); - end = start - Vector2(0, control->get_margin(MARGIN_TOP)); - _draw_margin_at_position(control->get_margin(MARGIN_TOP), parent_transform.xform((start + end) / 2), MARGIN_LEFT); - viewport->draw_line(parent_transform.xform(start), parent_transform.xform(end), color_base, 1); - break; - default: - break; - } - switch (drag_type) { - case DRAG_BOTTOM: - case DRAG_BOTTOM_LEFT: - case DRAG_BOTTOM_RIGHT: - _draw_margin_at_position(control->get_size().height, parent_transform.xform(Vector2(node_pos_in_parent[2], (node_pos_in_parent[1] + node_pos_in_parent[3]) / 2) + Vector2(5, 0)), MARGIN_RIGHT); - case DRAG_ALL: - start = Vector2(Math::lerp(node_pos_in_parent[2], node_pos_in_parent[0], ratio), node_pos_in_parent[3]); - end = start - Vector2(0, control->get_margin(MARGIN_BOTTOM)); - _draw_margin_at_position(control->get_margin(MARGIN_BOTTOM), parent_transform.xform((start + end) / 2), MARGIN_RIGHT); - viewport->draw_line(parent_transform.xform(start), parent_transform.xform(end), color_base, 1); - break; - default: - break; - } - - switch (drag_type) { - //Draw the ghost rect if the node if rotated/scaled - case DRAG_LEFT: - case DRAG_TOP_LEFT: - case DRAG_TOP: - case DRAG_TOP_RIGHT: - case DRAG_RIGHT: - case DRAG_BOTTOM_RIGHT: - case DRAG_BOTTOM: - case DRAG_BOTTOM_LEFT: - case DRAG_ALL: - if (control->get_rotation() != 0.0 || control->get_scale() != Vector2(1, 1)) { - Rect2 rect = Rect2(Vector2(node_pos_in_parent[0], node_pos_in_parent[1]), control->get_size()); - viewport->draw_rect(parent_transform.xform(rect), color_base, false); - } - break; - default: - break; - } - } + _draw_control_helpers(control); } + // Draw the resize handles if (tool == TOOL_SELECT && canvas_item->_edit_use_rect()) { Rect2 rect = canvas_item->_edit_get_rect(); Vector2 endpoints[4] = { @@ -2561,7 +2710,6 @@ void CanvasItemEditor::_draw_selection() { xform.xform(rect.position + Vector2(0, rect.size.y)) }; for (int i = 0; i < 4; i++) { - // Draw the resize handles int prev = (i + 3) % 4; int next = (i + 1) % 4; @@ -2576,6 +2724,46 @@ void CanvasItemEditor::_draw_selection() { select_handle->draw(ci, (ofs - (select_handle->get_size() / 2)).floor()); } } + + // Draw the rescale handles + bool is_ctrl = Input::get_singleton()->is_key_pressed(KEY_CONTROL); + bool is_alt = Input::get_singleton()->is_key_pressed(KEY_ALT); + if ((is_alt && is_ctrl) || tool == TOOL_SCALE || drag_type == DRAG_SCALE_X || drag_type == DRAG_SCALE_Y) { + + Transform2D unscaled_transform = (xform * canvas_item->get_transform().affine_inverse() * Transform2D(canvas_item->_edit_get_rotation(), canvas_item->_edit_get_position())).orthonormalized(); + Transform2D simple_xform = viewport->get_transform() * unscaled_transform; + + Size2 scale_factor = Size2(SCALE_HANDLE_DISTANCE, SCALE_HANDLE_DISTANCE); + bool uniform = Input::get_singleton()->is_key_pressed(KEY_SHIFT); + Point2 offset = (simple_xform.affine_inverse().xform(drag_to) - simple_xform.affine_inverse().xform(drag_from)) * zoom; + + if (drag_type == DRAG_SCALE_X) { + scale_factor.x += offset.x; + if (uniform) { + scale_factor.y += offset.x; + } + } else if (drag_type == DRAG_SCALE_Y) { + scale_factor.y -= offset.y; + if (uniform) { + scale_factor.x -= offset.y; + } + } + + //scale_factor *= zoom; + + viewport->draw_set_transform_matrix(simple_xform); + Rect2 x_handle_rect = Rect2(scale_factor.x * EDSCALE, -5 * EDSCALE, 10 * EDSCALE, 10 * EDSCALE); + Color x_axis_color(1.0, 0.4, 0.4, 0.6); + viewport->draw_rect(x_handle_rect, x_axis_color); + viewport->draw_line(Point2(), Point2(scale_factor.x * EDSCALE, 0), x_axis_color); + + Rect2 y_handle_rect = Rect2(-5 * EDSCALE, -(scale_factor.y + 10) * EDSCALE, 10 * EDSCALE, 10 * EDSCALE); + Color y_axis_color(0.4, 1.0, 0.4, 0.6); + viewport->draw_rect(y_handle_rect, y_axis_color); + viewport->draw_line(Point2(), Point2(0, -scale_factor.y * EDSCALE), y_axis_color); + + viewport->draw_set_transform_matrix(viewport->get_transform()); + } } } @@ -2650,7 +2838,7 @@ void CanvasItemEditor::_draw_axis() { RID ci = viewport->get_canvas_item(); - Color area_axis_color(0.4, 0.4, 1.0, 0.4); + Color area_axis_color = EditorSettings::get_singleton()->get("editors/2d/viewport_border_color"); Size2 screen_size = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height")); @@ -2750,15 +2938,16 @@ void CanvasItemEditor::_draw_invisible_nodes_positions(Node *p_node, const Trans _draw_invisible_nodes_positions(p_node->get_child(i), parent_xform, canvas_xform); } - if (canvas_item && !canvas_item->_edit_use_rect() && !editor_selection->is_selected(canvas_item)) { + if (canvas_item && !canvas_item->_edit_use_rect() && (!editor_selection->is_selected(canvas_item) || !_is_node_editable(canvas_item))) { Transform2D xform = transform * canvas_xform * parent_xform; // Draw the node's position Ref<Texture> position_icon = get_icon("EditorPositionUnselected", "EditorIcons"); - Transform2D transform = Transform2D(xform.get_rotation(), xform.get_origin()); - viewport->draw_set_transform_matrix(transform); + Transform2D unscaled_transform = (xform * canvas_item->get_transform().affine_inverse() * Transform2D(canvas_item->_edit_get_rotation(), canvas_item->_edit_get_position())).orthonormalized(); + Transform2D simple_xform = viewport->get_transform() * unscaled_transform; + viewport->draw_set_transform_matrix(simple_xform); viewport->draw_texture(position_icon, -position_icon->get_size() / 2, Color(1.0, 1.0, 1.0, 0.5)); - viewport->draw_set_transform_matrix(Transform2D()); + viewport->draw_set_transform_matrix(viewport->get_transform()); } } @@ -2775,7 +2964,7 @@ void CanvasItemEditor::_draw_hover() { Size2 item_size = Size2(node_icon->get_size().x + 4 + node_name_size.x, MAX(node_icon->get_size().y, node_name_size.y - 3)); Point2 pos = transform.xform(hovering_results[i].position) - Point2(0, item_size.y) + (Point2(node_icon->get_size().x, -node_icon->get_size().y) / 4); - // Rectify the position to avoid overlaping items + // Rectify the position to avoid overlapping items for (List<Rect2>::Element *E = previous_rects.front(); E; E = E->next()) { if (E->get().intersects(Rect2(pos, item_size))) { pos.y = E->get().get_position().y - item_size.y; @@ -2822,13 +3011,13 @@ void CanvasItemEditor::_draw_locks_and_groups(Node *p_node, const Transform2D &p float offset = 0; Ref<Texture> lock = get_icon("LockViewport", "EditorIcons"); - if (p_node->has_meta("_edit_lock_")) { + if (p_node->has_meta("_edit_lock_") && show_edit_locks) { lock->draw(viewport_canvas_item, (transform * canvas_xform * parent_xform).xform(Point2(0, 0)) + Point2(offset, 0)); offset += lock->get_size().x; } Ref<Texture> group = get_icon("GroupViewport", "EditorIcons"); - if (canvas_item->has_meta("_edit_group_")) { + if (canvas_item->has_meta("_edit_group_") && show_edit_locks) { group->draw(viewport_canvas_item, (transform * canvas_xform * parent_xform).xform(Point2(0, 0)) + Point2(offset, 0)); //offset += group->get_size().x; } @@ -2902,6 +3091,7 @@ bool CanvasItemEditor::_build_bones_list(Node *p_node) { } void CanvasItemEditor::_draw_viewport() { + // Update the transform transform = Transform2D(); transform.scale_basis(Size2(zoom, zoom)); @@ -2950,11 +3140,11 @@ void CanvasItemEditor::_draw_viewport() { EditorPluginList *over_plugin_list = editor->get_editor_plugins_over(); if (!over_plugin_list->empty()) { - over_plugin_list->forward_draw_over_viewport(viewport); + over_plugin_list->forward_canvas_draw_over_viewport(viewport); } EditorPluginList *force_over_plugin_list = editor->get_editor_plugins_force_over(); if (!force_over_plugin_list->empty()) { - force_over_plugin_list->forward_force_draw_over_viewport(viewport); + force_over_plugin_list->forward_canvas_force_draw_over_viewport(viewport); } _draw_bones(); @@ -2966,6 +3156,11 @@ void CanvasItemEditor::_draw_viewport() { _draw_hover(); } +void CanvasItemEditor::update_viewport() { + _update_scrollbars(); + viewport->update(); +} + void CanvasItemEditor::_notification(int p_what) { if (p_what == NOTIFICATION_PHYSICS_PROCESS) { @@ -3086,6 +3281,7 @@ void CanvasItemEditor::_notification(int p_what) { select_button->set_icon(get_icon("ToolSelect", "EditorIcons")); list_select_button->set_icon(get_icon("ListSelect", "EditorIcons")); move_button->set_icon(get_icon("ToolMove", "EditorIcons")); + scale_button->set_icon(get_icon("ToolScale", "EditorIcons")); rotate_button->set_icon(get_icon("ToolRotate", "EditorIcons")); snap_button->set_icon(get_icon("Snap", "EditorIcons")); snap_config_menu->set_icon(get_icon("GuiMiniTabMenu", "EditorIcons")); @@ -3371,8 +3567,7 @@ void CanvasItemEditor::_zoom_on_position(float p_zoom, Point2 p_position) { view_offset.x = Math::round(view_offset.x + ofs.x); view_offset.y = Math::round(view_offset.y + ofs.y); - _update_scrollbars(); - viewport->update(); + update_viewport(); } void CanvasItemEditor::_button_zoom_minus() { @@ -3394,7 +3589,7 @@ void CanvasItemEditor::_button_toggle_snap(bool p_status) { void CanvasItemEditor::_button_tool_select(int p_index) { - ToolButton *tb[TOOL_MAX] = { select_button, list_select_button, move_button, rotate_button, pivot_button, pan_button }; + ToolButton *tb[TOOL_MAX] = { select_button, list_select_button, move_button, scale_button, rotate_button, pivot_button, pan_button }; for (int i = 0; i < TOOL_MAX; i++) { tb[i]->set_pressed(i == p_index); } @@ -3426,6 +3621,12 @@ void CanvasItemEditor::_popup_callback(int p_op) { view_menu->get_popup()->set_item_checked(idx, show_viewport); viewport->update(); } break; + case SHOW_EDIT_LOCKS: { + show_edit_locks = !show_edit_locks; + int idx = view_menu->get_popup()->get_item_index(SHOW_EDIT_LOCKS); + view_menu->get_popup()->set_item_checked(idx, show_edit_locks); + viewport->update(); + } break; case SNAP_USE_NODE_PARENT: { snap_node_parent = !snap_node_parent; int idx = smartsnap_config_popup->get_item_index(SNAP_USE_NODE_PARENT); @@ -3963,8 +4164,7 @@ void CanvasItemEditor::_focus_selection(int p_op) { Vector2 offset = viewport->get_size() / 2 - editor->get_scene_root()->get_global_canvas_transform().xform(center); view_offset.x -= offset.x / zoom; view_offset.y -= offset.y / zoom; - _update_scrollbars(); - viewport->update(); + update_viewport(); } else { // VIEW_FRAME_TO_SELECTION @@ -4001,6 +4201,7 @@ void CanvasItemEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_selection_result_pressed"), &CanvasItemEditor::_selection_result_pressed); ClassDB::bind_method(D_METHOD("_selection_menu_hide"), &CanvasItemEditor::_selection_menu_hide); ClassDB::bind_method(D_METHOD("set_state"), &CanvasItemEditor::set_state); + ClassDB::bind_method(D_METHOD("update_viewport"), &CanvasItemEditor::update_viewport); ADD_SIGNAL(MethodInfo("item_lock_status_changed")); ADD_SIGNAL(MethodInfo("item_group_status_changed")); @@ -4029,6 +4230,7 @@ Dictionary CanvasItemEditor::get_state() const { state["show_rulers"] = show_rulers; state["show_guides"] = show_guides; state["show_helpers"] = show_helpers; + state["show_edit_locks"] = show_edit_locks; state["snap_rotation"] = snap_rotation; state["snap_relative"] = snap_relative; state["snap_pixel"] = snap_pixel; @@ -4148,6 +4350,12 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) { view_menu->get_popup()->set_item_checked(idx, show_helpers); } + if (state.has("show_edit_locks")) { + show_edit_locks = state["show_edit_locks"]; + int idx = view_menu->get_popup()->get_item_index(SHOW_EDIT_LOCKS); + view_menu->get_popup()->set_item_checked(idx, show_edit_locks); + } + if (state.has("snap_rotation")) { snap_rotation = state["snap_rotation"]; int idx = snap_config_menu->get_popup()->get_item_index(SNAP_USE_ROTATION); @@ -4273,7 +4481,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { zoom_plus = memnew(ToolButton); zoom_hb->add_child(zoom_plus); zoom_plus->connect("pressed", this, "_button_zoom_plus"); - zoom_plus->set_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_plus", TTR("Zoom in"), KEY_MASK_CMD | KEY_PLUS)); + zoom_plus->set_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_plus", TTR("Zoom in"), KEY_MASK_CMD | KEY_EQUAL)); // Usually direct access key for PLUS zoom_plus->set_focus_mode(FOCUS_NONE); updating_scroll = false; @@ -4286,6 +4494,8 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { select_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/select_mode", TTR("Select Mode"), KEY_Q)); select_button->set_tooltip(keycode_get_string(KEY_MASK_CMD) + TTR("Drag: Rotate") + "\n" + TTR("Alt+Drag: Move") + "\n" + TTR("Press 'v' to Change Pivot, 'Shift+v' to Drag Pivot (while moving).") + "\n" + TTR("Alt+RMB: Depth list selection")); + hb->add_child(memnew(VSeparator)); + move_button = memnew(ToolButton); hb->add_child(move_button); move_button->set_toggle_mode(true); @@ -4300,6 +4510,13 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { rotate_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/rotate_mode", TTR("Rotate Mode"), KEY_E)); rotate_button->set_tooltip(TTR("Rotate Mode")); + scale_button = memnew(ToolButton); + hb->add_child(scale_button); + scale_button->set_toggle_mode(true); + scale_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_SCALE)); + scale_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/scale_mode", TTR("Scale Mode"), KEY_S)); + scale_button->set_tooltip(TTR("Scale Mode")); + hb->add_child(memnew(VSeparator)); list_select_button = memnew(ToolButton); @@ -4383,6 +4600,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { skeleton_menu = memnew(MenuButton); hb->add_child(skeleton_menu); + skeleton_menu->set_tooltip(TTR("Skeleton Options")); p = skeleton_menu->get_popup(); p->set_hide_on_checkable_item_selection(false); @@ -4409,6 +4627,8 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_guides", TTR("Show Guides"), KEY_Y), SHOW_GUIDES); p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_origin", TTR("Show Origin")), SHOW_ORIGIN); p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_viewport", TTR("Show Viewport")), SHOW_VIEWPORT); + p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_edit_locks", TTR("Show Group And Lock Icons")), SHOW_EDIT_LOCKS); + p->add_separator(); p->add_shortcut(ED_SHORTCUT("canvas_item_editor/center_selection", TTR("Center Selection"), KEY_F), VIEW_CENTER_TO_SELECTION); p->add_shortcut(ED_SHORTCUT("canvas_item_editor/frame_selection", TTR("Frame Selection"), KEY_MASK_SHIFT | KEY_F), VIEW_FRAME_TO_SELECTION); @@ -4499,6 +4719,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { show_helpers = false; show_rulers = true; show_guides = true; + show_edit_locks = true; zoom = 1; view_offset = Point2(-150 - RULER_WIDTH, -95 - RULER_WIDTH); previous_update_view_offset = view_offset; // Moves the view a little bit to the left so that (0,0) is visible. The values a relative to a 16/10 screen @@ -4791,7 +5012,6 @@ void CanvasItemEditorViewport::_perform_drop_data() { // Without root dropping multiple files is not allowed if (!target_node && selected_files.size() > 1) { - accept->get_ok()->set_text(TTR("Ok")); accept->set_text(TTR("Cannot instantiate multiple nodes without root.")); accept->popup_centered_minsize(); return; @@ -4853,7 +5073,6 @@ 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("OK")); accept->set_text(vformat(TTR("Error instancing scene from %s"), files_str.c_str())); accept->popup_centered_minsize(); } @@ -4887,7 +5106,7 @@ bool CanvasItemEditorViewport::can_drop_data(const Point2 &p_point, const Varian type == "AtlasTexture" || type == "LargeTexture") { Ref<Texture> texture = Ref<Texture>(Object::cast_to<Texture>(*res)); - if (texture.is_valid() == false) { + if (!texture.is_valid()) { continue; } } else { diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index 2c943385ad..dc7b74112f 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -71,18 +71,21 @@ class CanvasItemEditor : public VBoxContainer { GDCLASS(CanvasItemEditor, VBoxContainer); - EditorNode *editor; - +public: enum Tool { TOOL_SELECT, TOOL_LIST_SELECT, TOOL_MOVE, + TOOL_SCALE, TOOL_ROTATE, TOOL_EDIT_PIVOT, TOOL_PAN, TOOL_MAX }; +private: + EditorNode *editor; + enum MenuOption { SNAP_USE, SNAP_USE_NODE_PARENT, @@ -102,6 +105,7 @@ class CanvasItemEditor : public VBoxContainer { SHOW_GUIDES, SHOW_ORIGIN, SHOW_VIEWPORT, + SHOW_EDIT_LOCKS, LOCK_SELECTED, UNLOCK_SELECTED, GROUP_SELECTED, @@ -188,7 +192,10 @@ class CanvasItemEditor : public VBoxContainer { DRAG_ANCHOR_BOTTOM_RIGHT, DRAG_ANCHOR_BOTTOM_LEFT, DRAG_ANCHOR_ALL, - DRAG_ALL, + DRAG_MOVE, + DRAG_SCALE_X, + DRAG_SCALE_Y, + DRAG_SCALE_BOTH, DRAG_ROTATE, DRAG_PIVOT, DRAG_V_GUIDE, @@ -220,6 +227,7 @@ class CanvasItemEditor : public VBoxContainer { bool show_origin; bool show_viewport; bool show_helpers; + bool show_edit_locks; float zoom; Point2 view_offset; Point2 previous_update_view_offset; @@ -272,6 +280,10 @@ class CanvasItemEditor : public VBoxContainer { Transform2D xform; float length; uint64_t last_pass; + + BoneList() : + length(0.f), + last_pass(0) {} }; uint64_t bone_last_frame; @@ -298,17 +310,19 @@ class CanvasItemEditor : public VBoxContainer { List<PoseClipboard> pose_clipboard; ToolButton *select_button; - ToolButton *list_select_button; + ToolButton *move_button; + ToolButton *scale_button; ToolButton *rotate_button; + ToolButton *list_select_button; + ToolButton *pivot_button; + ToolButton *pan_button; + ToolButton *snap_button; MenuButton *snap_config_menu; PopupMenu *smartsnap_config_popup; - ToolButton *pivot_button; - ToolButton *pan_button; - ToolButton *lock_button; ToolButton *unlock_button; @@ -355,6 +369,7 @@ class CanvasItemEditor : public VBoxContainer { Ref<ShortCut> multiply_grid_step_shortcut; Ref<ShortCut> divide_grid_step_shortcut; + bool _is_node_editable(const Node *p_node); void _find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, Vector<_SelectResult> &r_items, int p_limit = 0, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D()); void _get_canvas_items_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items, int p_limit = 0); void _get_bones_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items); @@ -368,7 +383,9 @@ class CanvasItemEditor : public VBoxContainer { void _add_canvas_item(CanvasItem *p_canvas_item); + void _save_canvas_item_ik_chain(const CanvasItem *p_canvas_item, List<float> *p_bones_length, List<Dictionary> *p_bones_state); void _save_canvas_item_state(List<CanvasItem *> p_canvas_items, bool save_bones = false); + void _restore_canvas_item_ik_chain(CanvasItem *p_canvas_item, const List<Dictionary> *p_bones_state); void _restore_canvas_item_state(List<CanvasItem *> p_canvas_items, bool restore_bones = false); void _commit_canvas_item_state(List<CanvasItem *> p_canvas_items, String action_name, bool commit_bones = false); @@ -408,6 +425,7 @@ class CanvasItemEditor : public VBoxContainer { void _draw_guides(); void _draw_focus(); void _draw_grid(); + void _draw_control_helpers(Control *control); void _draw_selection(); void _draw_axis(); void _draw_bones(); @@ -420,6 +438,7 @@ class CanvasItemEditor : public VBoxContainer { bool _gui_input_anchors(const Ref<InputEvent> &p_event); bool _gui_input_move(const Ref<InputEvent> &p_event); bool _gui_input_open_scene_on_double_click(const Ref<InputEvent> &p_event); + bool _gui_input_scale(const Ref<InputEvent> &p_event); bool _gui_input_pivot(const Ref<InputEvent> &p_event); bool _gui_input_resize(const Ref<InputEvent> &p_event); bool _gui_input_rotate(const Ref<InputEvent> &p_event); @@ -528,6 +547,10 @@ public: Control *get_viewport_control() { return viewport; } + void update_viewport(); + + Tool get_current_tool() { return tool; } + void set_undo_redo(UndoRedo *p_undo_redo) { undo_redo = p_undo_redo; } void edit(CanvasItem *p_canvas_item); diff --git a/editor/plugins/collision_polygon_editor_plugin.cpp b/editor/plugins/collision_polygon_editor_plugin.cpp index 5109379add..805a7d3835 100644 --- a/editor/plugins/collision_polygon_editor_plugin.cpp +++ b/editor/plugins/collision_polygon_editor_plugin.cpp @@ -31,10 +31,10 @@ #include "collision_polygon_editor_plugin.h" #include "canvas_item_editor_plugin.h" +#include "core/os/file_access.h" +#include "core/os/input.h" +#include "core/os/keyboard.h" #include "editor/editor_settings.h" -#include "os/file_access.h" -#include "os/input.h" -#include "os/keyboard.h" #include "scene/3d/camera.h" #include "spatial_editor_plugin.h" diff --git a/editor/plugins/collision_shape_2d_editor_plugin.cpp b/editor/plugins/collision_shape_2d_editor_plugin.cpp index 9e052bb027..313ba1ee6b 100644 --- a/editor/plugins/collision_shape_2d_editor_plugin.cpp +++ b/editor/plugins/collision_shape_2d_editor_plugin.cpp @@ -129,7 +129,7 @@ void CollisionShape2DEditor::set_handle(int idx, Point2 &p_point) { capsule->set_height(parameter * 2 - capsule->get_radius() * 2); } - canvas_item_editor->get_viewport_control()->update(); + canvas_item_editor->update_viewport(); } } break; @@ -138,7 +138,7 @@ void CollisionShape2DEditor::set_handle(int idx, Point2 &p_point) { Ref<CircleShape2D> circle = node->get_shape(); circle->set_radius(p_point.length()); - canvas_item_editor->get_viewport_control()->update(); + canvas_item_editor->update_viewport(); } break; @@ -160,7 +160,7 @@ void CollisionShape2DEditor::set_handle(int idx, Point2 &p_point) { line->set_normal(p_point.normalized()); } - canvas_item_editor->get_viewport_control()->update(); + canvas_item_editor->update_viewport(); } } break; @@ -170,7 +170,7 @@ void CollisionShape2DEditor::set_handle(int idx, Point2 &p_point) { ray->set_length(Math::abs(p_point.y)); - canvas_item_editor->get_viewport_control()->update(); + canvas_item_editor->update_viewport(); } break; @@ -183,7 +183,7 @@ void CollisionShape2DEditor::set_handle(int idx, Point2 &p_point) { rect->set_extents(extents.abs()); - canvas_item_editor->get_viewport_control()->update(); + canvas_item_editor->update_viewport(); } } break; @@ -198,16 +198,16 @@ void CollisionShape2DEditor::set_handle(int idx, Point2 &p_point) { seg->set_b(p_point); } - canvas_item_editor->get_viewport_control()->update(); + canvas_item_editor->update_viewport(); } } break; } + node->get_shape()->_change_notify(); } void CollisionShape2DEditor::commit_handle(int idx, Variant &p_org) { - Control *c = canvas_item_editor->get_viewport_control(); undo_redo->create_action(TTR("Set Handle")); switch (shape_type) { @@ -216,14 +216,14 @@ void CollisionShape2DEditor::commit_handle(int idx, Variant &p_org) { if (idx == 0) { undo_redo->add_do_method(capsule.ptr(), "set_radius", capsule->get_radius()); - undo_redo->add_do_method(c, "update"); + undo_redo->add_do_method(canvas_item_editor, "update_viewport"); undo_redo->add_undo_method(capsule.ptr(), "set_radius", p_org); - undo_redo->add_do_method(c, "update"); + undo_redo->add_do_method(canvas_item_editor, "update_viewport"); } else if (idx == 1) { undo_redo->add_do_method(capsule.ptr(), "set_height", capsule->get_height()); - undo_redo->add_do_method(c, "update"); + undo_redo->add_do_method(canvas_item_editor, "update_viewport"); undo_redo->add_undo_method(capsule.ptr(), "set_height", p_org); - undo_redo->add_undo_method(c, "update"); + undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); } } break; @@ -232,9 +232,9 @@ void CollisionShape2DEditor::commit_handle(int idx, Variant &p_org) { Ref<CircleShape2D> circle = node->get_shape(); undo_redo->add_do_method(circle.ptr(), "set_radius", circle->get_radius()); - undo_redo->add_do_method(c, "update"); + undo_redo->add_do_method(canvas_item_editor, "update_viewport"); undo_redo->add_undo_method(circle.ptr(), "set_radius", p_org); - undo_redo->add_undo_method(c, "update"); + undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); } break; @@ -251,14 +251,14 @@ void CollisionShape2DEditor::commit_handle(int idx, Variant &p_org) { if (idx == 0) { undo_redo->add_do_method(line.ptr(), "set_d", line->get_d()); - undo_redo->add_do_method(c, "update"); + undo_redo->add_do_method(canvas_item_editor, "update_viewport"); undo_redo->add_undo_method(line.ptr(), "set_d", p_org); - undo_redo->add_undo_method(c, "update"); + undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); } else { undo_redo->add_do_method(line.ptr(), "set_normal", line->get_normal()); - undo_redo->add_do_method(c, "update"); + undo_redo->add_do_method(canvas_item_editor, "update_viewport"); undo_redo->add_undo_method(line.ptr(), "set_normal", p_org); - undo_redo->add_undo_method(c, "update"); + undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); } } break; @@ -267,9 +267,9 @@ void CollisionShape2DEditor::commit_handle(int idx, Variant &p_org) { Ref<RayShape2D> ray = node->get_shape(); undo_redo->add_do_method(ray.ptr(), "set_length", ray->get_length()); - undo_redo->add_do_method(c, "update"); + undo_redo->add_do_method(canvas_item_editor, "update_viewport"); undo_redo->add_undo_method(ray.ptr(), "set_length", p_org); - undo_redo->add_undo_method(c, "update"); + undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); } break; @@ -277,9 +277,9 @@ void CollisionShape2DEditor::commit_handle(int idx, Variant &p_org) { Ref<RectangleShape2D> rect = node->get_shape(); undo_redo->add_do_method(rect.ptr(), "set_extents", rect->get_extents()); - undo_redo->add_do_method(c, "update"); + undo_redo->add_do_method(canvas_item_editor, "update_viewport"); undo_redo->add_undo_method(rect.ptr(), "set_extents", p_org); - undo_redo->add_undo_method(c, "update"); + undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); } break; @@ -287,14 +287,14 @@ void CollisionShape2DEditor::commit_handle(int idx, Variant &p_org) { Ref<SegmentShape2D> seg = node->get_shape(); if (idx == 0) { undo_redo->add_do_method(seg.ptr(), "set_a", seg->get_a()); - undo_redo->add_do_method(c, "update"); + undo_redo->add_do_method(canvas_item_editor, "update_viewport"); undo_redo->add_undo_method(seg.ptr(), "set_a", p_org); - undo_redo->add_undo_method(c, "update"); + undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); } else if (idx == 1) { undo_redo->add_do_method(seg.ptr(), "set_b", seg->get_b()); - undo_redo->add_do_method(c, "update"); + undo_redo->add_do_method(canvas_item_editor, "update_viewport"); undo_redo->add_undo_method(seg.ptr(), "set_b", p_org); - undo_redo->add_undo_method(c, "update"); + undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); } } break; @@ -323,7 +323,6 @@ bool CollisionShape2DEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_e if (mb.is_valid()) { Vector2 gpoint = mb->get_position(); - Vector2 cpoint = node->get_global_transform().affine_inverse().xform(canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(mb->get_position()))); if (mb->get_button_index() == BUTTON_LEFT) { if (mb->is_pressed()) { @@ -412,10 +411,10 @@ void CollisionShape2DEditor::_get_current_shape_type() { shape_type = -1; } - canvas_item_editor->get_viewport_control()->update(); + canvas_item_editor->update_viewport(); } -void CollisionShape2DEditor::forward_draw_over_viewport(Control *p_overlay) { +void CollisionShape2DEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { if (!node) { return; @@ -539,7 +538,7 @@ void CollisionShape2DEditor::edit(Node *p_node) { node = NULL; } - canvas_item_editor->get_viewport_control()->update(); + canvas_item_editor->update_viewport(); } void CollisionShape2DEditor::_bind_methods() { diff --git a/editor/plugins/collision_shape_2d_editor_plugin.h b/editor/plugins/collision_shape_2d_editor_plugin.h index 10784f1129..fb7b2acb0f 100644 --- a/editor/plugins/collision_shape_2d_editor_plugin.h +++ b/editor/plugins/collision_shape_2d_editor_plugin.h @@ -75,7 +75,7 @@ protected: public: bool forward_canvas_gui_input(const Ref<InputEvent> &p_event); - void forward_draw_over_viewport(Control *p_overlay); + void forward_canvas_draw_over_viewport(Control *p_overlay); void edit(Node *p_node); CollisionShape2DEditor(EditorNode *p_editor); @@ -89,7 +89,7 @@ class CollisionShape2DEditorPlugin : public EditorPlugin { public: virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return collision_shape_2d_editor->forward_canvas_gui_input(p_event); } - virtual void forward_draw_over_viewport(Control *p_overlay) { return collision_shape_2d_editor->forward_draw_over_viewport(p_overlay); } + virtual void forward_canvas_draw_over_viewport(Control *p_overlay) { return collision_shape_2d_editor->forward_canvas_draw_over_viewport(p_overlay); } virtual String get_name() const { return "CollisionShape2D"; } bool has_main_screen() const { return false; } diff --git a/editor/plugins/cpu_particles_editor_plugin.cpp b/editor/plugins/cpu_particles_editor_plugin.cpp index b32f927249..8d3ebc5052 100644 --- a/editor/plugins/cpu_particles_editor_plugin.cpp +++ b/editor/plugins/cpu_particles_editor_plugin.cpp @@ -1,4 +1,35 @@ +/*************************************************************************/ +/* cpu_particles_editor_plugin.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 "cpu_particles_editor_plugin.h" + #include "editor/plugins/spatial_editor_plugin.h" void CPUParticlesEditor::_node_removed(Node *p_node) { diff --git a/editor/plugins/cpu_particles_editor_plugin.h b/editor/plugins/cpu_particles_editor_plugin.h index f47d17104d..16fb0bab0c 100644 --- a/editor/plugins/cpu_particles_editor_plugin.h +++ b/editor/plugins/cpu_particles_editor_plugin.h @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* cpu_particles_editor_plugin.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 CPU_PARTICLES_EDITOR_PLUGIN_H #define CPU_PARTICLES_EDITOR_PLUGIN_H diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp index f5bdf77973..ace3012c10 100644 --- a/editor/plugins/curve_editor_plugin.cpp +++ b/editor/plugins/curve_editor_plugin.cpp @@ -31,9 +31,9 @@ #include "curve_editor_plugin.h" #include "canvas_item_editor_plugin.h" -#include "core_string_names.h" -#include "os/input.h" -#include "os/keyboard.h" +#include "core/core_string_names.h" +#include "core/os/input.h" +#include "core/os/keyboard.h" CurveEditor::CurveEditor() { _selected_point = -1; @@ -205,13 +205,13 @@ void CurveEditor::on_gui_input(const Ref<InputEvent> &p_event) { curve.set_point_left_tangent(_selected_point, tangent); // Note: if a tangent is set to linear, it shouldn't be linked to the other - if (link && _selected_point != curve.get_point_count() - 1 && !curve.get_point_right_mode(_selected_point) != Curve::TANGENT_FREE) + if (link && _selected_point != (curve.get_point_count() - 1) && curve.get_point_right_mode(_selected_point) != Curve::TANGENT_LINEAR) curve.set_point_right_tangent(_selected_point, tangent); } else { curve.set_point_right_tangent(_selected_point, tangent); - if (link && _selected_point != 0 && !curve.get_point_left_mode(_selected_point) != Curve::TANGENT_FREE) + if (link && _selected_point != 0 && curve.get_point_left_mode(_selected_point) != Curve::TANGENT_LINEAR) curve.set_point_left_tangent(_selected_point, tangent); } } @@ -782,12 +782,13 @@ bool CurvePreviewGenerator::handles(const String &p_type) const { return p_type == "Curve"; } -Ref<Texture> CurvePreviewGenerator::generate(const Ref<Resource> &p_from) { +Ref<Texture> CurvePreviewGenerator::generate(const Ref<Resource> &p_from, const Size2 p_size) const { Ref<Curve> curve_ref = p_from; ERR_FAIL_COND_V(curve_ref.is_null(), Ref<Texture>()); Curve &curve = **curve_ref; + // FIXME: Should be ported to use p_size as done in b2633a97 int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size"); thumbnail_size *= EDSCALE; Ref<Image> img_ref; diff --git a/editor/plugins/curve_editor_plugin.h b/editor/plugins/curve_editor_plugin.h index 255f359ed2..fa0b92e353 100644 --- a/editor/plugins/curve_editor_plugin.h +++ b/editor/plugins/curve_editor_plugin.h @@ -131,14 +131,14 @@ class CurveEditorPlugin : public EditorPlugin { public: CurveEditorPlugin(EditorNode *p_node); - String get_name() const { return "Curve"; } + virtual String get_name() const { return "Curve"; } }; class CurvePreviewGenerator : public EditorResourcePreviewGenerator { GDCLASS(CurvePreviewGenerator, EditorResourcePreviewGenerator) public: - bool handles(const String &p_type) const; - Ref<Texture> generate(const Ref<Resource> &p_from); + virtual bool handles(const String &p_type) const; + virtual Ref<Texture> generate(const Ref<Resource> &p_from, const Size2 p_size) const; }; #endif // CURVE_EDITOR_PLUGIN_H diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index 9acbceec92..7f83865777 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -30,12 +30,12 @@ #include "editor_preview_plugins.h" +#include "core/io/file_access_memory.h" +#include "core/io/resource_loader.h" +#include "core/os/os.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" -#include "io/file_access_memory.h" -#include "io/resource_loader.h" -#include "os/os.h" #include "scene/resources/bit_mask.h" #include "scene/resources/dynamic_font.h" #include "scene/resources/material.h" @@ -78,7 +78,11 @@ bool EditorTexturePreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "Texture"); } -Ref<Texture> EditorTexturePreviewPlugin::generate(const RES &p_from) const { +bool EditorTexturePreviewPlugin::should_generate_small_preview() const { + return true; +} + +Ref<Texture> EditorTexturePreviewPlugin::generate(const RES &p_from, const Size2 p_size) const { Ref<Image> img; Ref<AtlasTexture> atex = p_from; @@ -100,8 +104,6 @@ Ref<Texture> EditorTexturePreviewPlugin::generate(const RES &p_from) const { 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<Texture>(); @@ -109,22 +111,15 @@ Ref<Texture> EditorTexturePreviewPlugin::generate(const RES &p_from) const { 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(); + Vector2 new_size = img->get_size(); + if (new_size.x > p_size.x) { + new_size = Vector2(p_size.x, new_size.y * p_size.x / new_size.x); } + if (new_size.y > p_size.y) { + new_size = Vector2(new_size.x * p_size.y / new_size.y, p_size.y); + } + img->resize(new_size.x, new_size.y, Image::INTERPOLATE_CUBIC); - img->resize(width, height); post_process_preview(img); Ref<ImageTexture> ptex = Ref<ImageTexture>(memnew(ImageTexture)); @@ -143,7 +138,7 @@ bool EditorImagePreviewPlugin::handles(const String &p_type) const { return p_type == "Image"; } -Ref<Texture> EditorImagePreviewPlugin::generate(const RES &p_from) const { +Ref<Texture> EditorImagePreviewPlugin::generate(const RES &p_from, const Size2 p_size) const { Ref<Image> img = p_from; @@ -153,8 +148,6 @@ Ref<Texture> EditorImagePreviewPlugin::generate(const RES &p_from) const { 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>(); @@ -162,22 +155,15 @@ Ref<Texture> EditorImagePreviewPlugin::generate(const RES &p_from) const { 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(); + Vector2 new_size = img->get_size(); + if (new_size.x > p_size.x) { + new_size = Vector2(p_size.x, new_size.y * p_size.x / new_size.x); + } + if (new_size.y > p_size.y) { + new_size = Vector2(new_size.x * p_size.y / new_size.y, p_size.y); } + img->resize(new_size.x, new_size.y, Image::INTERPOLATE_CUBIC); - img->resize(width, height); post_process_preview(img); Ref<ImageTexture> ptex; @@ -190,6 +176,9 @@ Ref<Texture> EditorImagePreviewPlugin::generate(const RES &p_from) const { EditorImagePreviewPlugin::EditorImagePreviewPlugin() { } +bool EditorImagePreviewPlugin::should_generate_small_preview() const { + return true; +} //////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////// bool EditorBitmapPreviewPlugin::handles(const String &p_type) const { @@ -197,7 +186,7 @@ bool EditorBitmapPreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "BitMap"); } -Ref<Texture> EditorBitmapPreviewPlugin::generate(const RES &p_from) const { +Ref<Texture> EditorBitmapPreviewPlugin::generate(const RES &p_from, const Size2 p_size) const { Ref<BitMap> bm = p_from; @@ -227,8 +216,6 @@ Ref<Texture> EditorBitmapPreviewPlugin::generate(const RES &p_from) const { img.instance(); img->create(bm->get_size().width, bm->get_size().height, 0, Image::FORMAT_L8, data); - 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<Texture>(); @@ -236,22 +223,15 @@ Ref<Texture> EditorBitmapPreviewPlugin::generate(const RES &p_from) const { 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(); + Vector2 new_size = img->get_size(); + if (new_size.x > p_size.x) { + new_size = Vector2(p_size.x, new_size.y * p_size.x / new_size.x); + } + if (new_size.y > p_size.y) { + new_size = Vector2(new_size.x * p_size.y / new_size.y, p_size.y); } + img->resize(new_size.x, new_size.y, Image::INTERPOLATE_CUBIC); - img->resize(width, height); post_process_preview(img); Ref<ImageTexture> ptex = Ref<ImageTexture>(memnew(ImageTexture)); @@ -260,6 +240,10 @@ Ref<Texture> EditorBitmapPreviewPlugin::generate(const RES &p_from) const { return ptex; } +bool EditorBitmapPreviewPlugin::should_generate_small_preview() const { + return true; +} + EditorBitmapPreviewPlugin::EditorBitmapPreviewPlugin() { } @@ -269,12 +253,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) const { +Ref<Texture> EditorPackedScenePreviewPlugin::generate(const RES &p_from, const Size2 p_size) const { - return generate_from_path(p_from->get_path()); + return generate_from_path(p_from->get_path(), p_size); } -Ref<Texture> EditorPackedScenePreviewPlugin::generate_from_path(const String &p_path) const { +Ref<Texture> EditorPackedScenePreviewPlugin::generate_from_path(const String &p_path, const Size2 p_size) const { String temp_path = EditorSettings::get_singleton()->get_cache_dir(); String cache_base = ProjectSettings::get_singleton()->globalize_path(p_path).md5_text(); @@ -323,7 +307,11 @@ 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) const { +bool EditorMaterialPreviewPlugin::should_generate_small_preview() const { + return true; +} + +Ref<Texture> EditorMaterialPreviewPlugin::generate(const RES &p_from, const Size2 p_size) const { Ref<Material> material = p_from; ERR_FAIL_COND_V(material.is_null(), Ref<Texture>()); @@ -346,10 +334,9 @@ Ref<Texture> EditorMaterialPreviewPlugin::generate(const RES &p_from) const { ERR_FAIL_COND_V(!img.is_valid(), Ref<ImageTexture>()); - int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size"); - thumbnail_size *= EDSCALE; img->convert(Image::FORMAT_RGBA8); - img->resize(thumbnail_size, thumbnail_size); + int thumbnail_size = MAX(p_size.x, p_size.y); + img->resize(thumbnail_size, thumbnail_size, Image::INTERPOLATE_CUBIC); post_process_preview(img); Ref<ImageTexture> ptex = Ref<ImageTexture>(memnew(ImageTexture)); ptex->create_from_image(img, 0); @@ -490,7 +477,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) const { +Ref<Texture> EditorScriptPreviewPlugin::generate(const RES &p_from, const Size2 p_size) const { Ref<Script> scr = p_from; if (scr.is_null()) @@ -512,10 +499,9 @@ Ref<Texture> EditorScriptPreviewPlugin::generate(const RES &p_from) const { int line = 0; int col = 0; - int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size"); - thumbnail_size *= EDSCALE; Ref<Image> img; img.instance(); + int thumbnail_size = MAX(p_size.x, p_size.y); img->create(thumbnail_size, thumbnail_size, 0, Image::FORMAT_RGBA8); Color bg_color = EditorSettings::get_singleton()->get("text_editor/highlighting/background_color"); @@ -613,16 +599,15 @@ bool EditorAudioStreamPreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "AudioStream"); } -Ref<Texture> EditorAudioStreamPreviewPlugin::generate(const RES &p_from) const { +Ref<Texture> EditorAudioStreamPreviewPlugin::generate(const RES &p_from, const Size2 p_size) const { Ref<AudioStream> stream = p_from; ERR_FAIL_COND_V(stream.is_null(), Ref<Texture>()); - int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size"); - thumbnail_size *= EDSCALE; PoolVector<uint8_t> img; - int w = thumbnail_size; - int h = thumbnail_size; + + int w = p_size.x; + int h = p_size.y; img.resize(w * h * 3); PoolVector<uint8_t>::Write imgdata = img.write(); @@ -711,7 +696,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) const { +Ref<Texture> EditorMeshPreviewPlugin::generate(const RES &p_from, const Size2 p_size) const { Ref<Mesh> mesh = p_from; ERR_FAIL_COND_V(mesh.is_null(), Ref<Texture>()); @@ -749,10 +734,17 @@ Ref<Texture> EditorMeshPreviewPlugin::generate(const RES &p_from) const { VS::get_singleton()->instance_set_base(mesh_instance, RID()); - int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size"); - thumbnail_size *= EDSCALE; img->convert(Image::FORMAT_RGBA8); - img->resize(thumbnail_size, thumbnail_size); + + Vector2 new_size = img->get_size(); + if (new_size.x > p_size.x) { + new_size = Vector2(p_size.x, new_size.y * p_size.x / new_size.x); + } + if (new_size.y > p_size.y) { + new_size = Vector2(new_size.x * p_size.y / new_size.y, p_size.y); + } + img->resize(new_size.x, new_size.y, Image::INTERPOLATE_CUBIC); + post_process_preview(img); Ref<ImageTexture> ptex = Ref<ImageTexture>(memnew(ImageTexture)); @@ -825,15 +817,12 @@ 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) const { +Ref<Texture> EditorFontPreviewPlugin::generate_from_path(const String &p_path, const Size2 p_size) const { Ref<DynamicFontData> SampledFont; SampledFont.instance(); SampledFont->set_font_path(p_path); - int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size"); - thumbnail_size *= EDSCALE; - Ref<DynamicFont> sampled_font; sampled_font.instance(); sampled_font->set_size(50); @@ -864,7 +853,15 @@ Ref<Texture> EditorFontPreviewPlugin::generate_from_path(const String &p_path) c ERR_FAIL_COND_V(img.is_null(), Ref<ImageTexture>()); img->convert(Image::FORMAT_RGBA8); - img->resize(thumbnail_size, thumbnail_size); + + Vector2 new_size = img->get_size(); + if (new_size.x > p_size.x) { + new_size = Vector2(p_size.x, new_size.y * p_size.x / new_size.x); + } + if (new_size.y > p_size.y) { + new_size = Vector2(new_size.x * p_size.y / new_size.y, p_size.y); + } + img->resize(new_size.x, new_size.y, Image::INTERPOLATE_CUBIC); post_process_preview(img); @@ -874,9 +871,9 @@ Ref<Texture> EditorFontPreviewPlugin::generate_from_path(const String &p_path) c return ptex; } -Ref<Texture> EditorFontPreviewPlugin::generate(const RES &p_from) const { +Ref<Texture> EditorFontPreviewPlugin::generate(const RES &p_from, const Size2 p_size) const { - return generate_from_path(p_from->get_path()); + return generate_from_path(p_from->get_path(), p_size); } EditorFontPreviewPlugin::EditorFontPreviewPlugin() { diff --git a/editor/plugins/editor_preview_plugins.h b/editor/plugins/editor_preview_plugins.h index 8bd7943383..ed5d2a3ecd 100644 --- a/editor/plugins/editor_preview_plugins.h +++ b/editor/plugins/editor_preview_plugins.h @@ -39,7 +39,8 @@ class EditorTexturePreviewPlugin : public EditorResourcePreviewGenerator { GDCLASS(EditorTexturePreviewPlugin, EditorResourcePreviewGenerator) public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from) const; + virtual bool should_generate_small_preview() const; + virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const; EditorTexturePreviewPlugin(); }; @@ -48,7 +49,8 @@ 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; + virtual bool should_generate_small_preview() const; + virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const; EditorImagePreviewPlugin(); }; @@ -57,7 +59,8 @@ class EditorBitmapPreviewPlugin : public EditorResourcePreviewGenerator { GDCLASS(EditorBitmapPreviewPlugin, EditorResourcePreviewGenerator) public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from) const; + virtual bool should_generate_small_preview() const; + virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const; EditorBitmapPreviewPlugin(); }; @@ -66,8 +69,8 @@ class EditorPackedScenePreviewPlugin : public EditorResourcePreviewGenerator { public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from) const; - virtual Ref<Texture> generate_from_path(const String &p_path) const; + virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const; + virtual Ref<Texture> generate_from_path(const String &p_path, const Size2 p_size) const; EditorPackedScenePreviewPlugin(); }; @@ -95,7 +98,8 @@ protected: public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from) const; + virtual bool should_generate_small_preview() const; + virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const; EditorMaterialPreviewPlugin(); ~EditorMaterialPreviewPlugin(); @@ -104,7 +108,7 @@ public: class EditorScriptPreviewPlugin : public EditorResourcePreviewGenerator { public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from) const; + virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const; EditorScriptPreviewPlugin(); }; @@ -112,7 +116,7 @@ public: class EditorAudioStreamPreviewPlugin : public EditorResourcePreviewGenerator { public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from) const; + virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const; EditorAudioStreamPreviewPlugin(); }; @@ -139,7 +143,7 @@ protected: public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from) const; + virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const; EditorMeshPreviewPlugin(); ~EditorMeshPreviewPlugin(); @@ -162,8 +166,8 @@ protected: public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from) const; - virtual Ref<Texture> generate_from_path(const String &p_path) const; + virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const; + virtual Ref<Texture> generate_from_path(const String &p_path, const Size2 p_size) const; EditorFontPreviewPlugin(); ~EditorFontPreviewPlugin(); diff --git a/editor/plugins/gi_probe_editor_plugin.cpp b/editor/plugins/gi_probe_editor_plugin.cpp index 06da64b181..5fc5cad1ef 100644 --- a/editor/plugins/gi_probe_editor_plugin.cpp +++ b/editor/plugins/gi_probe_editor_plugin.cpp @@ -90,7 +90,7 @@ void GIProbeEditorPlugin::_bind_methods() { GIProbeEditorPlugin::GIProbeEditorPlugin(EditorNode *p_node) { editor = p_node; - bake = memnew(Button); + bake = memnew(ToolButton); bake->set_icon(editor->get_gui_base()->get_icon("Bake", "EditorIcons")); bake->set_text(TTR("Bake GI Probe")); bake->hide(); diff --git a/editor/plugins/gi_probe_editor_plugin.h b/editor/plugins/gi_probe_editor_plugin.h index 017e9bd743..1b3b63f227 100644 --- a/editor/plugins/gi_probe_editor_plugin.h +++ b/editor/plugins/gi_probe_editor_plugin.h @@ -42,7 +42,7 @@ class GIProbeEditorPlugin : public EditorPlugin { GIProbe *gi_probe; - Button *bake; + ToolButton *bake; EditorNode *editor; static EditorProgress *tmp_progress; diff --git a/editor/plugins/item_list_editor_plugin.cpp b/editor/plugins/item_list_editor_plugin.cpp index f75fb0d109..8df40232b0 100644 --- a/editor/plugins/item_list_editor_plugin.cpp +++ b/editor/plugins/item_list_editor_plugin.cpp @@ -30,7 +30,7 @@ #include "item_list_editor_plugin.h" -#include "io/resource_loader.h" +#include "core/io/resource_loader.h" bool ItemListPlugin::_set(const StringName &p_name, const Variant &p_value) { @@ -278,25 +278,27 @@ void ItemListEditor::_add_pressed() { void ItemListEditor::_delete_pressed() { - TreeItem *ti = tree->get_selected(); - - if (!ti) + if (selected_idx == -1) return; - if (ti->get_parent() != tree->get_root()) + String current_selected = (String)property_editor->get_selected_path(); + + if (current_selected == "") return; - int idx = ti->get_text(0).to_int(); + // FIXME: Currently relying on selecting a *property* to derive what item to delete + // e.g. you select "1/enabled" to delete item 1. + // This should be fixed so that you can delete by selecting the item section header, + // or a delete button on that header. - if (selected_idx == -1) - return; + int idx = current_selected.get_slice("/", 0).to_int(); item_plugins[selected_idx]->erase(idx); } void ItemListEditor::_edit_items() { - dialog->popup_centered(Vector2(300, 400)); + dialog->popup_centered(Vector2(300, 400) * EDSCALE); } void ItemListEditor::edit(Node *p_item_list) { @@ -315,10 +317,7 @@ void ItemListEditor::edit(Node *p_item_list) { item_plugins[i]->set_object(p_item_list); property_editor->edit(item_plugins[i]); - if (has_icon(item_list->get_class(), "EditorIcons")) - toolbar_button->set_icon(get_icon(item_list->get_class(), "EditorIcons")); - else - toolbar_button->set_icon(Ref<Texture>()); + toolbar_button->set_icon(EditorNode::get_singleton()->get_object_icon(item_list, "")); selected_idx = i; return; @@ -351,8 +350,6 @@ ItemListEditor::ItemListEditor() { selected_idx = -1; - add_child(memnew(VSeparator)); - toolbar_button = memnew(ToolButton); toolbar_button->set_text(TTR("Items")); add_child(toolbar_button); @@ -382,13 +379,9 @@ ItemListEditor::ItemListEditor() { hbc->add_child(del_button); del_button->connect("pressed", this, "_delete_button"); - property_editor = memnew(PropertyEditor); - property_editor->hide_top_label(); - property_editor->set_subsection_selectable(true); + property_editor = memnew(EditorInspector); vbc->add_child(property_editor); property_editor->set_v_size_flags(SIZE_EXPAND_FILL); - - tree = property_editor->get_property_tree(); } ItemListEditor::~ItemListEditor() { diff --git a/editor/plugins/item_list_editor_plugin.h b/editor/plugins/item_list_editor_plugin.h index d6a071b9b9..3dc3775f83 100644 --- a/editor/plugins/item_list_editor_plugin.h +++ b/editor/plugins/item_list_editor_plugin.h @@ -32,9 +32,9 @@ #define ITEM_LIST_EDITOR_PLUGIN_H #include "canvas_item_editor_plugin.h" +#include "editor/editor_inspector.h" #include "editor/editor_node.h" #include "editor/editor_plugin.h" - #include "scene/gui/menu_button.h" #include "scene/gui/option_button.h" #include "scene/gui/popup_menu.h" @@ -210,7 +210,7 @@ class ItemListEditor : public HBoxContainer { ToolButton *toolbar_button; AcceptDialog *dialog; - PropertyEditor *property_editor; + EditorInspector *property_editor; Tree *tree; Button *add_button; Button *del_button; diff --git a/editor/plugins/light_occluder_2d_editor_plugin.cpp b/editor/plugins/light_occluder_2d_editor_plugin.cpp index 3351e5918f..6a16cf0989 100644 --- a/editor/plugins/light_occluder_2d_editor_plugin.cpp +++ b/editor/plugins/light_occluder_2d_editor_plugin.cpp @@ -31,8 +31,8 @@ #include "light_occluder_2d_editor_plugin.h" #include "canvas_item_editor_plugin.h" +#include "core/os/file_access.h" #include "editor/editor_settings.h" -#include "os/file_access.h" void LightOccluder2DEditor::_notification(int p_what) { @@ -57,7 +57,7 @@ void LightOccluder2DEditor::_node_removed(Node *p_node) { if (p_node == node) { node = NULL; hide(); - canvas_item_editor->get_viewport_control()->update(); + canvas_item_editor->update_viewport(); } } @@ -88,8 +88,8 @@ void LightOccluder2DEditor::_wip_close(bool p_closed) { undo_redo->add_undo_method(node->get_occluder_polygon().ptr(), "set_closed", node->get_occluder_polygon()->is_closed()); undo_redo->add_do_method(node->get_occluder_polygon().ptr(), "set_closed", p_closed); - undo_redo->add_do_method(canvas_item_editor->get_viewport_control(), "update"); - undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(), "update"); + undo_redo->add_do_method(canvas_item_editor, "update_viewport"); + undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); undo_redo->commit_action(); wip.clear(); wip_active = false; @@ -139,7 +139,7 @@ bool LightOccluder2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { wip.push_back(cpoint); wip_active = true; edited_point_pos = cpoint; - canvas_item_editor->get_viewport_control()->update(); + canvas_item_editor->update_viewport(); edited_point = 1; return true; } else { @@ -158,7 +158,7 @@ bool LightOccluder2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { wip.push_back(cpoint); edited_point = wip.size(); - canvas_item_editor->get_viewport_control()->update(); + canvas_item_editor->update_viewport(); return true; //add wip point @@ -183,8 +183,8 @@ bool LightOccluder2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { undo_redo->add_undo_method(node->get_occluder_polygon().ptr(), "set_polygon", poly); poly.push_back(cpoint); undo_redo->add_do_method(node->get_occluder_polygon().ptr(), "set_polygon", poly); - undo_redo->add_do_method(canvas_item_editor->get_viewport_control(), "update"); - undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(), "update"); + undo_redo->add_do_method(canvas_item_editor, "update_viewport"); + undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); undo_redo->commit_action(); return true; } @@ -217,7 +217,7 @@ bool LightOccluder2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { edited_point = closest_idx + 1; edited_point_pos = xform.affine_inverse().xform(closest_pos); node->get_occluder_polygon()->set_polygon(Variant(poly)); - canvas_item_editor->get_viewport_control()->update(); + canvas_item_editor->update_viewport(); return true; } } else { @@ -244,7 +244,7 @@ bool LightOccluder2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { pre_move_edit = poly; edited_point = closest_idx; edited_point_pos = xform.affine_inverse().xform(closest_pos); - canvas_item_editor->get_viewport_control()->update(); + canvas_item_editor->update_viewport(); return true; } } @@ -259,8 +259,8 @@ bool LightOccluder2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { 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); - undo_redo->add_do_method(canvas_item_editor->get_viewport_control(), "update"); - undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(), "update"); + undo_redo->add_do_method(canvas_item_editor, "update_viewport"); + undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); undo_redo->commit_action(); edited_point = -1; @@ -290,8 +290,8 @@ bool LightOccluder2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { undo_redo->add_undo_method(node->get_occluder_polygon().ptr(), "set_polygon", poly); poly.remove(closest_idx); undo_redo->add_do_method(node->get_occluder_polygon().ptr(), "set_polygon", poly); - undo_redo->add_do_method(canvas_item_editor->get_viewport_control(), "update"); - undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(), "update"); + undo_redo->add_do_method(canvas_item_editor, "update_viewport"); + undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); undo_redo->commit_action(); return true; } @@ -312,20 +312,18 @@ bool LightOccluder2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { cpoint = canvas_item_editor->snap_point(cpoint); edited_point_pos = node->get_global_transform().affine_inverse().xform(cpoint); - canvas_item_editor->get_viewport_control()->update(); + canvas_item_editor->update_viewport(); } } return false; } -void LightOccluder2DEditor::forward_draw_over_viewport(Control *p_overlay) { +void LightOccluder2DEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { if (!node || !node->get_occluder_polygon().is_valid()) return; - Control *vpc = canvas_item_editor->get_viewport_control(); - Vector<Vector2> poly; if (wip_active) @@ -353,9 +351,9 @@ void LightOccluder2DEditor::forward_draw_over_viewport(Control *p_overlay) { if (i == poly.size() - 1 && (!node->get_occluder_polygon()->is_closed() || wip_active)) { } else { - vpc->draw_line(point, next_point, col, 2); + p_overlay->draw_line(point, next_point, col, 2); } - vpc->draw_texture(handle, point - handle->get_size() * 0.5); + p_overlay->draw_texture(handle, point - handle->get_size() * 0.5); } } @@ -371,7 +369,7 @@ void LightOccluder2DEditor::edit(Node *p_collision_polygon) { wip.clear(); wip_active = false; edited_point = -1; - canvas_item_editor->get_viewport_control()->update(); + canvas_item_editor->update_viewport(); } else { node = NULL; } diff --git a/editor/plugins/light_occluder_2d_editor_plugin.h b/editor/plugins/light_occluder_2d_editor_plugin.h index 39de8b1020..a1962892ee 100644 --- a/editor/plugins/light_occluder_2d_editor_plugin.h +++ b/editor/plugins/light_occluder_2d_editor_plugin.h @@ -83,7 +83,7 @@ protected: public: Vector2 snap_point(const Vector2 &p_point) const; - void forward_draw_over_viewport(Control *p_overlay); + void forward_canvas_draw_over_viewport(Control *p_overlay); bool forward_gui_input(const Ref<InputEvent> &p_event); void edit(Node *p_collision_polygon); LightOccluder2DEditor(EditorNode *p_editor); @@ -98,7 +98,7 @@ class LightOccluder2DEditorPlugin : public EditorPlugin { public: virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return light_occluder_editor->forward_gui_input(p_event); } - virtual void forward_draw_over_viewport(Control *p_overlay) { return light_occluder_editor->forward_draw_over_viewport(p_overlay); } + virtual void forward_canvas_draw_over_viewport(Control *p_overlay) { return light_occluder_editor->forward_canvas_draw_over_viewport(p_overlay); } virtual String get_name() const { return "LightOccluder2D"; } bool has_main_screen() const { return false; } diff --git a/editor/plugins/material_editor_plugin.cpp b/editor/plugins/material_editor_plugin.cpp index 27a16fd3dd..5e59a73061 100644 --- a/editor/plugins/material_editor_plugin.cpp +++ b/editor/plugins/material_editor_plugin.cpp @@ -28,397 +28,9 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -// FIXME: Disabled as (according to reduz) users were complaining that it gets in the way -// Waiting for PropertyEditor rewrite (planned for 3.1) to be refactored. - #include "material_editor_plugin.h" -#include "scene/3d/particles.h" - -#if 0 - -#include "scene/main/viewport.h" - -void MaterialEditor::_gui_input(InputEvent p_event) { - - -} - -void MaterialEditor::_notification(int p_what) { - - if (p_what==NOTIFICATION_PHYSICS_PROCESS) { - - } - - - if (p_what==NOTIFICATION_READY) { - - //get_scene()->connect("node_removed",this,"_node_removed"); - - if (first_enter) { - //it's in propertyeditor so.. could be moved around - - light_1_switch->set_normal_texture(get_icon("MaterialPreviewLight1","EditorIcons")); - light_1_switch->set_pressed_texture(get_icon("MaterialPreviewLight1Off","EditorIcons")); - light_2_switch->set_normal_texture(get_icon("MaterialPreviewLight2","EditorIcons")); - light_2_switch->set_pressed_texture(get_icon("MaterialPreviewLight2Off","EditorIcons")); - - sphere_switch->set_normal_texture(get_icon("MaterialPreviewSphereOff","EditorIcons")); - sphere_switch->set_pressed_texture(get_icon("MaterialPreviewSphere","EditorIcons")); - box_switch->set_normal_texture(get_icon("MaterialPreviewCubeOff","EditorIcons")); - box_switch->set_pressed_texture(get_icon("MaterialPreviewCube","EditorIcons")); - - first_enter=false; - } - - } - - if (p_what==NOTIFICATION_DRAW) { - - - Ref<Texture> checkerboard = get_icon("Checkerboard","EditorIcons"); - Size2 size = get_size(); - - draw_texture_rect(checkerboard,Rect2(Point2(),size),true); - - } -} - - - -void MaterialEditor::edit(Ref<Material> p_material) { - - material=p_material; - - if (!material.is_null()) { - sphere_mesh->surface_set_material(0,material); - box_mesh->surface_set_material(0,material); - } else { - - hide(); - } - -} - - -void MaterialEditor::_button_pressed(Node* p_button) { - - if (p_button==light_1_switch) { - light1->set_enabled(!light_1_switch->is_pressed()); - } - - if (p_button==light_2_switch) { - light2->set_enabled(!light_2_switch->is_pressed()); - } - - if (p_button==box_switch) { - box_instance->show(); - sphere_instance->hide(); - box_switch->set_pressed(true); - sphere_switch->set_pressed(false); - } - - if (p_button==sphere_switch) { - box_instance->hide(); - sphere_instance->show(); - box_switch->set_pressed(false); - sphere_switch->set_pressed(true); - } - -} - -void MaterialEditor::_bind_methods() { - - ClassDB::bind_method(D_METHOD("_gui_input"),&MaterialEditor::_gui_input); - ClassDB::bind_method(D_METHOD("_button_pressed"),&MaterialEditor::_button_pressed); - -} - -MaterialEditor::MaterialEditor() { - - viewport = memnew( Viewport ); - Ref<World> world; - world.instance(); - viewport->set_world(world); //use own world - add_child(viewport); - viewport->set_disable_input(true); - - camera = memnew( Camera ); - camera->set_transform(Transform(Matrix3(),Vector3(0,0,3))); - camera->set_perspective(45,0.1,10); - viewport->add_child(camera); - - light1 = memnew( DirectionalLight ); - light1->set_transform(Transform().looking_at(Vector3(-1,-1,-1),Vector3(0,1,0))); - viewport->add_child(light1); - - light2 = memnew( DirectionalLight ); - light2->set_transform(Transform().looking_at(Vector3(0,1,0),Vector3(0,0,1))); - light2->set_color(Light::COLOR_DIFFUSE,Color(0.7,0.7,0.7)); - light2->set_color(Light::COLOR_SPECULAR,Color(0.7,0.7,0.7)); - viewport->add_child(light2); - - sphere_instance = memnew( MeshInstance ); - viewport->add_child(sphere_instance); - - box_instance = memnew( MeshInstance ); - viewport->add_child(box_instance); - - Transform box_xform; - box_xform.basis.rotate(Vector3(1,0,0),Math::deg2rad(25)); - box_xform.basis = box_xform.basis * Matrix3().rotated(Vector3(0,1,0),Math::deg2rad(25)); - box_xform.basis.scale(Vector3(0.8,0.8,0.8)); - box_instance->set_transform(box_xform); - - { - - sphere_mesh.instance(); - - - int lats=32; - int lons=32; - float radius=1.0; - - PoolVector<Vector3> vertices; - PoolVector<Vector3> normals; - PoolVector<Vector2> uvs; - PoolVector<float> tangents; - Matrix3 tt = Matrix3(Vector3(0,1,0),Math_PI*0.5); - - for(int i = 1; i <= lats; i++) { - double lat0 = Math_PI * (-0.5 + (double) (i - 1) / lats); - double z0 = Math::sin(lat0); - double zr0 = Math::cos(lat0); - - double lat1 = Math_PI * (-0.5 + (double) i / lats); - double z1 = Math::sin(lat1); - double zr1 = Math::cos(lat1); - - for(int j = lons; j >= 1; j--) { - - double lng0 = 2 * Math_PI * (double) (j - 1) / lons; - double x0 = Math::cos(lng0); - double y0 = Math::sin(lng0); - - double lng1 = 2 * Math_PI * (double) (j) / lons; - double x1 = Math::cos(lng1); - double y1 = Math::sin(lng1); - - - Vector3 v[4]={ - Vector3(x1 * zr0, z0, y1 *zr0), - Vector3(x1 * zr1, z1, y1 *zr1), - Vector3(x0 * zr1, z1, y0 *zr1), - Vector3(x0 * zr0, z0, y0 *zr0) - }; - -#define ADD_POINT(m_idx) \ - normals.push_back(v[m_idx]); \ - vertices.push_back(v[m_idx] * radius); \ - { \ - Vector2 uv(Math::atan2(v[m_idx].x, v[m_idx].z), Math::atan2(-v[m_idx].y, v[m_idx].z)); \ - uv /= Math_PI; \ - uv *= 4.0; \ - uv = uv * 0.5 + Vector2(0.5, 0.5); \ - uvs.push_back(uv); \ - } \ - { \ - Vector3 t = tt.xform(v[m_idx]); \ - tangents.push_back(t.x); \ - tangents.push_back(t.y); \ - tangents.push_back(t.z); \ - tangents.push_back(1.0); \ - } - - - - ADD_POINT(0); - ADD_POINT(1); - ADD_POINT(2); - - ADD_POINT(2); - ADD_POINT(3); - ADD_POINT(0); - } - } - - Array arr; - arr.resize(VS::ARRAY_MAX); - arr[VS::ARRAY_VERTEX]=vertices; - arr[VS::ARRAY_NORMAL]=normals; - arr[VS::ARRAY_TANGENT]=tangents; - arr[VS::ARRAY_TEX_UV]=uvs; - - sphere_mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES,arr); - - sphere_instance->set_mesh(sphere_mesh); - - } - { - - - box_mesh.instance(); - - PoolVector<Vector3> vertices; - PoolVector<Vector3> normals; - 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++;\ - - for (int i=0;i<6;i++) { - - - Vector3 face_points[4]; - Vector3 normal_points[4]; - float uv_points[8]={0,0,0,1,1,1,1,0}; - - 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]*(i>=3?-1:1); - else - face_points[3-j][(i+k)%3]=v[k]*(i>=3?-1:1); - } - normal_points[j]=Vector3(); - normal_points[j][i%3]=(i>=3?-1:1); - } - - //tri 1 - ADD_VTX(0); - ADD_VTX(1); - ADD_VTX(2); - //tri 2 - ADD_VTX(2); - ADD_VTX(3); - ADD_VTX(0); - - } - - - - Array d; - d.resize(VS::ARRAY_MAX); - d[VisualServer::ARRAY_NORMAL]= normals ; - d[VisualServer::ARRAY_TANGENT]= tangents ; - d[VisualServer::ARRAY_TEX_UV]= uvs ; - d[VisualServer::ARRAY_VERTEX]= vertices ; - - PoolVector<int> indices; - indices.resize(vertices.size()); - for(int i=0;i<vertices.size();i++) - indices.set(i,i); - d[VisualServer::ARRAY_INDEX]=indices; - - box_mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES,d); - box_instance->set_mesh(box_mesh); - box_instance->hide(); - - - - } - - set_custom_minimum_size(Size2(1,150)*EDSCALE); - - HBoxContainer *hb = memnew( HBoxContainer ); - add_child(hb); - hb->set_anchors_and_margins_preset(Control::PRESET_WIDE, Control::PRESET_MODE_MINSIZE, 2); - - VBoxContainer *vb_shape = memnew( VBoxContainer ); - hb->add_child(vb_shape); - - sphere_switch = memnew( TextureButton ); - sphere_switch->set_toggle_mode(true); - sphere_switch->set_pressed(true); - vb_shape->add_child(sphere_switch); - sphere_switch->connect("pressed",this,"_button_pressed",varray(sphere_switch)); - - box_switch = memnew( TextureButton ); - box_switch->set_toggle_mode(true); - box_switch->set_pressed(false); - vb_shape->add_child(box_switch); - box_switch->connect("pressed",this,"_button_pressed",varray(box_switch)); - - hb->add_spacer(); - - VBoxContainer *vb_light = memnew( VBoxContainer ); - hb->add_child(vb_light); - - light_1_switch = memnew( TextureButton ); - light_1_switch->set_toggle_mode(true); - vb_light->add_child(light_1_switch); - light_1_switch->connect("pressed",this,"_button_pressed",varray(light_1_switch)); - - light_2_switch = memnew( TextureButton ); - light_2_switch->set_toggle_mode(true); - vb_light->add_child(light_2_switch); - light_2_switch->connect("pressed",this,"_button_pressed",varray(light_2_switch)); - - first_enter=true; - -} - - -void MaterialEditorPlugin::edit(Object *p_object) { - - Material * s = Object::cast_to<Material>(p_object); - if (!s) - return; - - material_editor->edit(Ref<Material>(s)); -} - -bool MaterialEditorPlugin::handles(Object *p_object) const { - - return p_object->is_type("Material"); -} - -void MaterialEditorPlugin::make_visible(bool p_visible) { - - if (p_visible) { - material_editor->show(); - //material_editor->set_process(true); - } else { - - material_editor->hide(); - //material_editor->set_process(false); - } - -} - -MaterialEditorPlugin::MaterialEditorPlugin(EditorNode *p_node) { - - editor=p_node; - material_editor = memnew( MaterialEditor ); - add_control_to_container(CONTAINER_PROPERTY_EDITOR_BOTTOM,material_editor); - material_editor->hide(); - - - -} - - -MaterialEditorPlugin::~MaterialEditorPlugin() -{ -} -#endif +#include "scene/resources/particles_material.h" String SpatialMaterialConversionPlugin::converts_to() const { @@ -429,7 +41,7 @@ bool SpatialMaterialConversionPlugin::handles(const Ref<Resource> &p_resource) c Ref<SpatialMaterial> mat = p_resource; return mat.is_valid(); } -Ref<Resource> SpatialMaterialConversionPlugin::convert(const Ref<Resource> &p_resource) { +Ref<Resource> SpatialMaterialConversionPlugin::convert(const Ref<Resource> &p_resource) const { Ref<SpatialMaterial> mat = p_resource; ERR_FAIL_COND_V(!mat.is_valid(), Ref<Resource>()); @@ -475,7 +87,7 @@ bool ParticlesMaterialConversionPlugin::handles(const Ref<Resource> &p_resource) Ref<ParticlesMaterial> mat = p_resource; return mat.is_valid(); } -Ref<Resource> ParticlesMaterialConversionPlugin::convert(const Ref<Resource> &p_resource) { +Ref<Resource> ParticlesMaterialConversionPlugin::convert(const Ref<Resource> &p_resource) const { Ref<ParticlesMaterial> mat = p_resource; ERR_FAIL_COND_V(!mat.is_valid(), Ref<Resource>()); @@ -513,7 +125,7 @@ bool CanvasItemMaterialConversionPlugin::handles(const Ref<Resource> &p_resource Ref<CanvasItemMaterial> mat = p_resource; return mat.is_valid(); } -Ref<Resource> CanvasItemMaterialConversionPlugin::convert(const Ref<Resource> &p_resource) { +Ref<Resource> CanvasItemMaterialConversionPlugin::convert(const Ref<Resource> &p_resource) const { Ref<CanvasItemMaterial> mat = p_resource; ERR_FAIL_COND_V(!mat.is_valid(), Ref<Resource>()); diff --git a/editor/plugins/material_editor_plugin.h b/editor/plugins/material_editor_plugin.h index 80a5c535b8..c06d95e700 100644 --- a/editor/plugins/material_editor_plugin.h +++ b/editor/plugins/material_editor_plugin.h @@ -32,84 +32,13 @@ #define MATERIAL_EDITOR_PLUGIN_H #include "editor/property_editor.h" -// FIXME: Disabled as (according to reduz) users were complaining that it gets in the way -// Waiting for PropertyEditor rewrite (planned for 3.1) to be refactored. -#if 0 - -#include "editor/editor_node.h" -#include "editor/editor_plugin.h" -#include "scene/3d/camera.h" -#include "scene/3d/light.h" -#include "scene/3d/mesh_instance.h" -#include "scene/resources/material.h" - -class MaterialEditor : public Control { - - GDCLASS(MaterialEditor, Control); - - - Viewport *viewport; - MeshInstance *sphere_instance; - MeshInstance *box_instance; - DirectionalLight *light1; - DirectionalLight *light2; - Camera *camera; - - Ref<Mesh> sphere_mesh; - Ref<Mesh> box_mesh; - - TextureButton *sphere_switch; - TextureButton *box_switch; - - TextureButton *light_1_switch; - TextureButton *light_2_switch; - - - Ref<Material> material; - - - void _button_pressed(Node* p_button); - bool first_enter; - -protected: - void _notification(int p_what); - void _gui_input(InputEvent p_event); - static void _bind_methods(); -public: - - void edit(Ref<Material> p_material); - MaterialEditor(); -}; - - -class MaterialEditorPlugin : public EditorPlugin { - - GDCLASS( MaterialEditorPlugin, EditorPlugin ); - - MaterialEditor *material_editor; - EditorNode *editor; - -public: - - virtual String get_name() const { return "Material"; } - bool has_main_screen() const { return false; } - virtual void edit(Object *p_node); - virtual bool handles(Object *p_node) const; - virtual void make_visible(bool p_visible); - - MaterialEditorPlugin(EditorNode *p_node); - ~MaterialEditorPlugin(); - -}; - -#endif class SpatialMaterialConversionPlugin : public EditorResourceConversionPlugin { GDCLASS(SpatialMaterialConversionPlugin, EditorResourceConversionPlugin) 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 ParticlesMaterialConversionPlugin : public EditorResourceConversionPlugin { @@ -117,7 +46,7 @@ class ParticlesMaterialConversionPlugin : public EditorResourceConversionPlugin 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 CanvasItemMaterialConversionPlugin : public EditorResourceConversionPlugin { @@ -125,7 +54,7 @@ class CanvasItemMaterialConversionPlugin : public EditorResourceConversionPlugin 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; }; #endif // MATERIAL_EDITOR_PLUGIN_H diff --git a/editor/plugins/mesh_editor_plugin.cpp b/editor/plugins/mesh_editor_plugin.cpp index 7b7e23531a..73a216e96f 100644 --- a/editor/plugins/mesh_editor_plugin.cpp +++ b/editor/plugins/mesh_editor_plugin.cpp @@ -65,14 +65,6 @@ void MeshEditor::_notification(int p_what) { first_enter = false; } } - - if (p_what == NOTIFICATION_DRAW) { - - Ref<Texture> checkerboard = get_icon("Checkerboard", "EditorIcons"); - Size2 size = get_size(); - - //draw_texture_rect(checkerboard, Rect2(Point2(), size), true); - } } void MeshEditor::_update_rotation() { diff --git a/editor/plugins/particles_2d_editor_plugin.cpp b/editor/plugins/particles_2d_editor_plugin.cpp index ae5d510502..ab94258c44 100644 --- a/editor/plugins/particles_2d_editor_plugin.cpp +++ b/editor/plugins/particles_2d_editor_plugin.cpp @@ -31,9 +31,10 @@ #include "particles_2d_editor_plugin.h" #include "canvas_item_editor_plugin.h" -#include "io/image_loader.h" -#include "scene/3d/particles.h" +#include "core/io/image_loader.h" +#include "scene/2d/cpu_particles_2d.h" #include "scene/gui/separator.h" +#include "scene/resources/particles_material.h" void Particles2DEditorPlugin::edit(Object *p_object) { @@ -82,6 +83,25 @@ void Particles2DEditorPlugin::_menu_callback(int p_idx) { emission_mask->popup_centered_minsize(); } break; + case MENU_OPTION_CONVERT_TO_CPU_PARTICLES: { + + UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + + CPUParticles2D *cpu_particles = memnew(CPUParticles2D); + cpu_particles->convert_from_particles(particles); + cpu_particles->set_name(particles->get_name()); + cpu_particles->set_transform(particles->get_transform()); + cpu_particles->set_visible(particles->is_visible()); + cpu_particles->set_pause_mode(particles->get_pause_mode()); + + undo_redo->create_action("Replace Particles by CPUParticles"); + undo_redo->add_do_method(particles, "replace_by", cpu_particles); + undo_redo->add_undo_method(cpu_particles, "replace_by", particles); + undo_redo->add_do_reference(cpu_particles); + undo_redo->add_undo_reference(particles); + undo_redo->commit_action(); + + } break; } } @@ -355,6 +375,8 @@ Particles2DEditorPlugin::Particles2DEditorPlugin(EditorNode *p_node) { menu->get_popup()->add_separator(); menu->get_popup()->add_item(TTR("Load Emission Mask"), MENU_LOAD_EMISSION_MASK); // menu->get_popup()->add_item(TTR("Clear Emission Mask"), MENU_CLEAR_EMISSION_MASK); + menu->get_popup()->add_separator(); + menu->get_popup()->add_item(TTR("Convert to CPUParticles"), MENU_OPTION_CONVERT_TO_CPU_PARTICLES); menu->set_text(TTR("Particles")); toolbar->add_child(menu); diff --git a/editor/plugins/particles_2d_editor_plugin.h b/editor/plugins/particles_2d_editor_plugin.h index 2b6123141b..eaa96d84e9 100644 --- a/editor/plugins/particles_2d_editor_plugin.h +++ b/editor/plugins/particles_2d_editor_plugin.h @@ -34,7 +34,6 @@ #include "editor/editor_node.h" #include "editor/editor_plugin.h" #include "scene/2d/collision_polygon_2d.h" - #include "scene/2d/particles_2d.h" #include "scene/gui/box_container.h" #include "scene/gui/file_dialog.h" @@ -47,7 +46,8 @@ class Particles2DEditorPlugin : public EditorPlugin { MENU_GENERATE_VISIBILITY_RECT, MENU_LOAD_EMISSION_MASK, - MENU_CLEAR_EMISSION_MASK + MENU_CLEAR_EMISSION_MASK, + MENU_OPTION_CONVERT_TO_CPU_PARTICLES }; enum EmissionMode { diff --git a/editor/plugins/particles_editor_plugin.cpp b/editor/plugins/particles_editor_plugin.cpp index 3c381158a4..6b41946918 100644 --- a/editor/plugins/particles_editor_plugin.cpp +++ b/editor/plugins/particles_editor_plugin.cpp @@ -29,9 +29,12 @@ /*************************************************************************/ #include "particles_editor_plugin.h" + +#include "core/io/resource_loader.h" #include "editor/plugins/spatial_editor_plugin.h" -#include "io/resource_loader.h" #include "scene/3d/cpu_particles.h" +#include "scene/resources/particles_material.h" + bool ParticlesEditorBase::_generate(PoolVector<Vector3> &points, PoolVector<Vector3> &normals) { bool use_normals = emission_fill->get_selected() == 1; diff --git a/editor/plugins/particles_editor_plugin.h b/editor/plugins/particles_editor_plugin.h index 622ce6e8a9..830d30d98f 100644 --- a/editor/plugins/particles_editor_plugin.h +++ b/editor/plugins/particles_editor_plugin.h @@ -43,6 +43,7 @@ class ParticlesEditorBase : public Control { GDCLASS(ParticlesEditorBase, Control) + protected: Spatial *base_node; Panel *panel; diff --git a/editor/plugins/path_2d_editor_plugin.cpp b/editor/plugins/path_2d_editor_plugin.cpp index 33e182faef..c67c96798a 100644 --- a/editor/plugins/path_2d_editor_plugin.cpp +++ b/editor/plugins/path_2d_editor_plugin.cpp @@ -31,9 +31,9 @@ #include "path_2d_editor_plugin.h" #include "canvas_item_editor_plugin.h" +#include "core/os/file_access.h" +#include "core/os/keyboard.h" #include "editor/editor_settings.h" -#include "os/file_access.h" -#include "os/keyboard.h" void Path2DEditor::_notification(int p_what) { @@ -130,8 +130,8 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { undo_redo->create_action(TTR("Remove Point from Curve")); undo_redo->add_do_method(curve.ptr(), "remove_point", i); undo_redo->add_undo_method(curve.ptr(), "add_point", curve->get_point_position(i), curve->get_point_in(i), curve->get_point_out(i), i); - undo_redo->add_do_method(canvas_item_editor->get_viewport_control(), "update"); - undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(), "update"); + undo_redo->add_do_method(canvas_item_editor, "update_viewport"); + undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); undo_redo->commit_action(); return true; } else if (dist_to_p_out < grab_threshold) { @@ -139,8 +139,8 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { undo_redo->create_action(TTR("Remove Out-Control from Curve")); undo_redo->add_do_method(curve.ptr(), "set_point_out", i, Vector2()); undo_redo->add_undo_method(curve.ptr(), "set_point_out", i, curve->get_point_out(i)); - undo_redo->add_do_method(canvas_item_editor->get_viewport_control(), "update"); - undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(), "update"); + undo_redo->add_do_method(canvas_item_editor, "update_viewport"); + undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); undo_redo->commit_action(); return true; } else if (dist_to_p_in < grab_threshold) { @@ -148,8 +148,8 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { undo_redo->create_action(TTR("Remove In-Control from Curve")); undo_redo->add_do_method(curve.ptr(), "set_point_in", i, Vector2()); undo_redo->add_undo_method(curve.ptr(), "set_point_in", i, curve->get_point_in(i)); - undo_redo->add_do_method(canvas_item_editor->get_viewport_control(), "update"); - undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(), "update"); + undo_redo->add_do_method(canvas_item_editor, "update_viewport"); + undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); undo_redo->commit_action(); return true; } @@ -165,8 +165,8 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { undo_redo->create_action(TTR("Add Point to Curve")); undo_redo->add_do_method(curve.ptr(), "add_point", cpoint); undo_redo->add_undo_method(curve.ptr(), "remove_point", curve->get_point_count()); - undo_redo->add_do_method(canvas_item_editor->get_viewport_control(), "update"); - undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(), "update"); + undo_redo->add_do_method(canvas_item_editor, "update_viewport"); + undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); undo_redo->commit_action(); action = ACTION_MOVING_POINT; @@ -174,7 +174,7 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { moving_from = curve->get_point_position(action_point); moving_screen_from = gpoint; - canvas_item_editor->get_viewport_control()->update(); + canvas_item_editor->update_viewport(); return true; } @@ -196,8 +196,8 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { undo_redo->create_action(TTR("Move Point in Curve")); undo_redo->add_do_method(curve.ptr(), "set_point_position", action_point, cpoint); undo_redo->add_undo_method(curve.ptr(), "set_point_position", action_point, moving_from); - undo_redo->add_do_method(canvas_item_editor->get_viewport_control(), "update"); - undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(), "update"); + undo_redo->add_do_method(canvas_item_editor, "update_viewport"); + undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); undo_redo->commit_action(); } break; @@ -212,8 +212,8 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { undo_redo->add_do_method(curve.ptr(), "set_point_out", action_point, mirror_handle_length ? -new_pos : (-new_pos.normalized() * orig_out_length)); undo_redo->add_undo_method(curve.ptr(), "set_point_out", action_point, mirror_handle_length ? -moving_from : (-moving_from.normalized() * orig_out_length)); } - undo_redo->add_do_method(canvas_item_editor->get_viewport_control(), "update"); - undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(), "update"); + undo_redo->add_do_method(canvas_item_editor, "update_viewport"); + undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); undo_redo->commit_action(); } break; @@ -228,8 +228,8 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { undo_redo->add_do_method(curve.ptr(), "set_point_in", action_point, mirror_handle_length ? -new_pos : (-new_pos.normalized() * orig_in_length)); undo_redo->add_undo_method(curve.ptr(), "set_point_in", action_point, mirror_handle_length ? -moving_from : (-moving_from.normalized() * orig_in_length)); } - undo_redo->add_do_method(canvas_item_editor->get_viewport_control(), "update"); - undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(), "update"); + undo_redo->add_do_method(canvas_item_editor, "update_viewport"); + undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); undo_redo->commit_action(); } break; @@ -280,7 +280,7 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { } break; } - canvas_item_editor->get_viewport_control()->update(); + canvas_item_editor->update_viewport(); return true; } } @@ -288,7 +288,7 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { return false; } -void Path2DEditor::forward_draw_over_viewport(Control *p_overlay) { +void Path2DEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { if (!node) return; @@ -331,7 +331,7 @@ void Path2DEditor::_node_visibility_changed() { if (!node) return; - canvas_item_editor->get_viewport_control()->update(); + canvas_item_editor->update_viewport(); } void Path2DEditor::edit(Node *p_path2d) { @@ -406,8 +406,8 @@ void Path2DEditor::_mode_selected(int p_mode) { undo_redo->create_action(TTR("Remove Point from Curve")); undo_redo->add_do_method(node->get_curve().ptr(), "add_point", begin); undo_redo->add_undo_method(node->get_curve().ptr(), "remove_point", node->get_curve()->get_point_count()); - undo_redo->add_do_method(canvas_item_editor->get_viewport_control(), "update"); - undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(), "update"); + undo_redo->add_do_method(canvas_item_editor, "update_viewport"); + undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); undo_redo->commit_action(); return; } diff --git a/editor/plugins/path_2d_editor_plugin.h b/editor/plugins/path_2d_editor_plugin.h index 1e3955f84f..3a78657746 100644 --- a/editor/plugins/path_2d_editor_plugin.h +++ b/editor/plugins/path_2d_editor_plugin.h @@ -107,7 +107,7 @@ protected: public: bool forward_gui_input(const Ref<InputEvent> &p_event); - void forward_draw_over_viewport(Control *p_overlay); + void forward_canvas_draw_over_viewport(Control *p_overlay); void edit(Node *p_path2d); Path2DEditor(EditorNode *p_editor); }; @@ -121,7 +121,7 @@ class Path2DEditorPlugin : public EditorPlugin { public: virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return path2d_editor->forward_gui_input(p_event); } - virtual void forward_draw_over_viewport(Control *p_overlay) { return path2d_editor->forward_draw_over_viewport(p_overlay); } + virtual void forward_canvas_draw_over_viewport(Control *p_overlay) { return path2d_editor->forward_canvas_draw_over_viewport(p_overlay); } virtual String get_name() const { return "Path2D"; } bool has_main_screen() const { return false; } diff --git a/editor/plugins/path_editor_plugin.cpp b/editor/plugins/path_editor_plugin.cpp index 618c70d1a1..df6c40ed02 100644 --- a/editor/plugins/path_editor_plugin.cpp +++ b/editor/plugins/path_editor_plugin.cpp @@ -30,7 +30,7 @@ #include "path_editor_plugin.h" -#include "os/keyboard.h" +#include "core/os/keyboard.h" #include "scene/resources/curve.h" #include "spatial_editor_plugin.h" @@ -57,7 +57,7 @@ String PathSpatialGizmo::get_handle_name(int p_idx) const { return n; } -Variant PathSpatialGizmo::get_handle_value(int p_idx) const { +Variant PathSpatialGizmo::get_handle_value(int p_idx) { Ref<Curve3D> c = path->get_curve(); if (c.is_null()) @@ -215,8 +215,8 @@ 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> path_material = gizmo_plugin->get_material("path_material", this); + Ref<SpatialMaterial> path_thin_material = gizmo_plugin->get_material("path_thin_material", this); Ref<SpatialMaterial> handles_material = gizmo_plugin->get_material("handles"); Ref<Curve3D> c = path->get_curve(); @@ -641,24 +641,8 @@ String PathSpatialGizmoPlugin::get_name() const { 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)); + create_material("path_material", path_color); 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_material("path_thin_material", path_color); create_handle_material("handles"); } diff --git a/editor/plugins/path_editor_plugin.h b/editor/plugins/path_editor_plugin.h index 61f309e794..c77b2a41cc 100644 --- a/editor/plugins/path_editor_plugin.h +++ b/editor/plugins/path_editor_plugin.h @@ -45,7 +45,7 @@ class PathSpatialGizmo : public EditorSpatialGizmo { public: 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); diff --git a/editor/plugins/physical_bone_plugin.cpp b/editor/plugins/physical_bone_plugin.cpp index 42f1adcadf..6341d7f2ef 100644 --- a/editor/plugins/physical_bone_plugin.cpp +++ b/editor/plugins/physical_bone_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -69,15 +69,7 @@ PhysicalBoneEditor::PhysicalBoneEditor(EditorNode *p_editor) : hide(); } -PhysicalBoneEditor::~PhysicalBoneEditor() { - // TODO the spatial_editor_hb should be removed from SpatialEditor, but in this moment it's not possible - for (int i = spatial_editor_hb->get_child_count() - 1; 0 <= i; --i) { - Node *n = spatial_editor_hb->get_child(i); - spatial_editor_hb->remove_child(n); - memdelete(n); - } - memdelete(spatial_editor_hb); -} +PhysicalBoneEditor::~PhysicalBoneEditor() {} void PhysicalBoneEditor::set_selected(PhysicalBone *p_pb) { @@ -98,19 +90,17 @@ void PhysicalBoneEditor::show() { PhysicalBonePlugin::PhysicalBonePlugin(EditorNode *p_editor) : editor(p_editor), - selected(NULL) { - - physical_bone_editor = memnew(PhysicalBoneEditor(editor)); -} + selected(NULL), + physical_bone_editor(editor) {} void PhysicalBonePlugin::make_visible(bool p_visible) { if (p_visible) { - physical_bone_editor->show(); + physical_bone_editor.show(); } else { - physical_bone_editor->hide(); - physical_bone_editor->set_selected(NULL); + physical_bone_editor.hide(); + physical_bone_editor.set_selected(NULL); selected = NULL; } } @@ -119,5 +109,5 @@ void PhysicalBonePlugin::edit(Object *p_node) { selected = static_cast<PhysicalBone *>(p_node); // Trust it ERR_FAIL_COND(!selected); - physical_bone_editor->set_selected(selected); + physical_bone_editor.set_selected(selected); } diff --git a/editor/plugins/physical_bone_plugin.h b/editor/plugins/physical_bone_plugin.h index 9e7a50307a..e1f8c9ec47 100644 --- a/editor/plugins/physical_bone_plugin.h +++ b/editor/plugins/physical_bone_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -64,7 +64,7 @@ class PhysicalBonePlugin : public EditorPlugin { EditorNode *editor; PhysicalBone *selected; - PhysicalBoneEditor *physical_bone_editor; + PhysicalBoneEditor physical_bone_editor; public: virtual String get_name() const { return "PhysicalBone"; } diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp index a437cd5362..f937744d45 100644 --- a/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/editor/plugins/polygon_2d_editor_plugin.cpp @@ -31,10 +31,10 @@ #include "polygon_2d_editor_plugin.h" #include "canvas_item_editor_plugin.h" +#include "core/os/file_access.h" +#include "core/os/input.h" +#include "core/os/keyboard.h" #include "editor/editor_settings.h" -#include "os/file_access.h" -#include "os/input.h" -#include "os/keyboard.h" #include "scene/2d/skeleton_2d.h" Node2D *Polygon2DEditor::_get_node() const { @@ -767,6 +767,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { node->set_polygon(uv_new); } } break; + default: {} } if (bone_painting) { diff --git a/editor/plugins/resource_preloader_editor_plugin.cpp b/editor/plugins/resource_preloader_editor_plugin.cpp index c6e8ec1a2b..bd4a35c9d8 100644 --- a/editor/plugins/resource_preloader_editor_plugin.cpp +++ b/editor/plugins/resource_preloader_editor_plugin.cpp @@ -30,9 +30,9 @@ #include "resource_preloader_editor_plugin.h" +#include "core/io/resource_loader.h" +#include "core/project_settings.h" #include "editor/editor_settings.h" -#include "io/resource_loader.h" -#include "project_settings.h" void ResourcePreloaderEditor::_gui_input(Ref<InputEvent> p_event) { } @@ -44,7 +44,6 @@ void ResourcePreloaderEditor::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { load->set_icon(get_icon("Folder", "EditorIcons")); - _delete->set_icon(get_icon("Remove", "EditorIcons")); } if (p_what == NOTIFICATION_READY) { @@ -138,15 +137,11 @@ void ResourcePreloaderEditor::_item_edited() { } } -void ResourcePreloaderEditor::_delete_confirm_pressed() { +void ResourcePreloaderEditor::_remove_resource(const String &p_to_remove) { - if (!tree->get_selected()) - return; - - String to_remove = tree->get_selected()->get_text(0); undo_redo->create_action(TTR("Delete Resource")); - undo_redo->add_do_method(preloader, "remove_resource", to_remove); - undo_redo->add_undo_method(preloader, "add_resource", to_remove, preloader->get_resource(to_remove)); + undo_redo->add_do_method(preloader, "remove_resource", p_to_remove); + undo_redo->add_undo_method(preloader, "add_resource", p_to_remove, preloader->get_resource(p_to_remove)); undo_redo->add_do_method(this, "_update_library"); undo_redo->add_undo_method(this, "_update_library"); undo_redo->commit_action(); @@ -184,21 +179,6 @@ void ResourcePreloaderEditor::_paste_pressed() { undo_redo->commit_action(); } -void ResourcePreloaderEditor::_delete_pressed() { - - if (!tree->get_selected()) - return; - - _delete_confirm_pressed(); //it has undo.. why bother with a dialog.. - /* - dialog->set_title("Confirm..."); - dialog->set_text("Remove Resource '"+tree->get_selected()->get_text(0)+"' ?"); - //dialog->get_cancel()->set_text("Cancel"); - //dialog->get_ok()->show(); - dialog->get_ok()->set_text("Remove"); - dialog->popup_centered(Size2(300,60));*/ -} - void ResourcePreloaderEditor::_update_library() { tree->clear(); @@ -228,18 +208,20 @@ void ResourcePreloaderEditor::_update_library() { ERR_CONTINUE(r.is_null()); - ti->set_tooltip(0, r->get_path()); + String type = r->get_class(); + ti->set_icon(0, EditorNode::get_singleton()->get_class_icon(type, "Object")); + ti->set_tooltip(0, TTR("Instance:") + " " + r->get_path() + "\n" + TTR("Type:") + " " + type); + ti->set_text(1, r->get_path()); - ti->add_button(1, get_icon("InstanceOptions", "EditorIcons"), BUTTON_SUBSCENE, false, TTR("Open in Editor")); - ti->set_tooltip(1, TTR("Instance:") + " " + r->get_path() + "\n" + TTR("Type:") + " " + r->get_class()); ti->set_editable(1, false); ti->set_selectable(1, false); - String type = r->get_class(); - ti->set_text(2, type); - ti->set_selectable(2, false); - if (has_icon(type, "EditorIcons")) - ti->set_icon(2, get_icon(type, "EditorIcons")); + if (type == "PackedScene") { + ti->add_button(1, get_icon("InstanceOptions", "EditorIcons"), BUTTON_OPEN_SCENE, false, TTR("Open in Editor")); + } else { + ti->add_button(1, get_icon("Load", "EditorIcons"), BUTTON_EDIT_RESOURCE, false, TTR("Open in Editor")); + } + ti->add_button(1, get_icon("Remove", "EditorIcons"), BUTTON_REMOVE, false, TTR("Remove")); } //player->add_resource("default",resource); @@ -250,10 +232,16 @@ void ResourcePreloaderEditor::_cell_button_pressed(Object *p_item, int p_column, TreeItem *item = Object::cast_to<TreeItem>(p_item); ERR_FAIL_COND(!item); - String rpath = item->get_text(p_column); - - if (p_id == BUTTON_SUBSCENE) { + if (p_id == BUTTON_OPEN_SCENE) { + String rpath = item->get_text(p_column); EditorInterface::get_singleton()->open_scene_from_path(rpath); + + } else if (p_id == BUTTON_EDIT_RESOURCE) { + RES r = preloader->get_resource(item->get_text(0)); + EditorInterface::get_singleton()->edit_resource(r); + + } else if (p_id == BUTTON_REMOVE) { + _remove_resource(item->get_text(0)); } } @@ -366,12 +354,11 @@ void ResourcePreloaderEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_gui_input"), &ResourcePreloaderEditor::_gui_input); ClassDB::bind_method(D_METHOD("_load_pressed"), &ResourcePreloaderEditor::_load_pressed); ClassDB::bind_method(D_METHOD("_item_edited"), &ResourcePreloaderEditor::_item_edited); - ClassDB::bind_method(D_METHOD("_delete_pressed"), &ResourcePreloaderEditor::_delete_pressed); ClassDB::bind_method(D_METHOD("_paste_pressed"), &ResourcePreloaderEditor::_paste_pressed); - ClassDB::bind_method(D_METHOD("_delete_confirm_pressed"), &ResourcePreloaderEditor::_delete_confirm_pressed); ClassDB::bind_method(D_METHOD("_files_load_request"), &ResourcePreloaderEditor::_files_load_request); ClassDB::bind_method(D_METHOD("_update_library"), &ResourcePreloaderEditor::_update_library); ClassDB::bind_method(D_METHOD("_cell_button_pressed"), &ResourcePreloaderEditor::_cell_button_pressed); + ClassDB::bind_method(D_METHOD("_remove_resource", "to_remove"), &ResourcePreloaderEditor::_remove_resource); ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &ResourcePreloaderEditor::get_drag_data_fw); ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &ResourcePreloaderEditor::can_drop_data_fw); @@ -392,9 +379,6 @@ ResourcePreloaderEditor::ResourcePreloaderEditor() { load->set_tooltip(TTR("Load Resource")); hbc->add_child(load); - _delete = memnew(Button); - hbc->add_child(_delete); - paste = memnew(Button); paste->set_text(TTR("Paste")); hbc->add_child(paste); @@ -404,13 +388,11 @@ ResourcePreloaderEditor::ResourcePreloaderEditor() { tree = memnew(Tree); tree->connect("button_pressed", this, "_cell_button_pressed"); - tree->set_columns(3); - tree->set_column_min_width(0, 3); - tree->set_column_min_width(1, 1); - tree->set_column_min_width(2, 1); + tree->set_columns(2); + tree->set_column_min_width(0, 2); + tree->set_column_min_width(1, 3); tree->set_column_expand(0, true); tree->set_column_expand(1, true); - tree->set_column_expand(2, true); tree->set_v_size_flags(SIZE_EXPAND_FILL); tree->set_drag_forwarding(this); @@ -420,10 +402,8 @@ ResourcePreloaderEditor::ResourcePreloaderEditor() { add_child(dialog); load->connect("pressed", this, "_load_pressed"); - _delete->connect("pressed", this, "_delete_pressed"); paste->connect("pressed", this, "_paste_pressed"); file->connect("files_selected", this, "_files_load_request"); - //dialog->connect("confirmed", this,"_delete_confirm_pressed"); tree->connect("item_edited", this, "_item_edited"); loading_scene = false; } diff --git a/editor/plugins/resource_preloader_editor_plugin.h b/editor/plugins/resource_preloader_editor_plugin.h index e737157785..0a8238ce18 100644 --- a/editor/plugins/resource_preloader_editor_plugin.h +++ b/editor/plugins/resource_preloader_editor_plugin.h @@ -43,11 +43,12 @@ class ResourcePreloaderEditor : public PanelContainer { GDCLASS(ResourcePreloaderEditor, PanelContainer); enum { - BUTTON_SUBSCENE = 0, + BUTTON_OPEN_SCENE, + BUTTON_EDIT_RESOURCE, + BUTTON_REMOVE }; Button *load; - Button *_delete; Button *paste; Tree *tree; bool loading_scene; @@ -62,8 +63,7 @@ class ResourcePreloaderEditor : public PanelContainer { void _load_scene_pressed(); void _files_load_request(const Vector<String> &p_paths); void _paste_pressed(); - void _delete_pressed(); - void _delete_confirm_pressed(); + void _remove_resource(const String &p_to_remove); void _update_library(); void _cell_button_pressed(Object *p_item, int p_column, int p_id); void _item_edited(); diff --git a/editor/plugins/root_motion_editor_plugin.cpp b/editor/plugins/root_motion_editor_plugin.cpp index af3c09afc5..b3adf19a64 100644 --- a/editor/plugins/root_motion_editor_plugin.cpp +++ b/editor/plugins/root_motion_editor_plugin.cpp @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* root_motion_editor_plugin.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 "root_motion_editor_plugin.h" #include "editor/editor_node.h" #include "scene/main/viewport.h" @@ -79,11 +109,7 @@ void EditorPropertyRootMotion::_node_assign() { if (base->has_node(accum)) { Node *node = base->get_node(accum); - if (has_icon(node->get_class(), "EditorIcons")) { - ti->set_icon(0, get_icon(node->get_class(), "EditorIcons")); - } else { - ti->set_icon(0, get_icon("Node", "EditorIcons")); - } + ti->set_icon(0, EditorNode::get_singleton()->get_object_icon(node, "Node")); } } else { @@ -96,7 +122,7 @@ void EditorPropertyRootMotion::_node_assign() { node = base->get_node(accum); } if (!node) - continue; //no node, cant edit + continue; //no node, can't edit if (path.get_subname_count()) { @@ -205,14 +231,7 @@ void EditorPropertyRootMotion::update_property() { ERR_FAIL_COND(!target_node); assign->set_text(target_node->get_name()); - - Ref<Texture> icon; - if (has_icon(target_node->get_class(), "EditorIcons")) - icon = get_icon(target_node->get_class(), "EditorIcons"); - else - icon = get_icon("Node", "EditorIcons"); - - assign->set_icon(icon); + assign->set_icon(EditorNode::get_singleton()->get_object_icon(target_node, "Node")); } void EditorPropertyRootMotion::setup(const NodePath &p_base_hint) { @@ -284,7 +303,7 @@ bool EditorInspectorRootMotionPlugin::parse_property(Object *p_object, Variant:: return true; } - return false; //can be overriden, although it will most likely be last anyway + return false; //can be overridden, although it will most likely be last anyway } void EditorInspectorRootMotionPlugin::parse_end() { diff --git a/editor/plugins/root_motion_editor_plugin.h b/editor/plugins/root_motion_editor_plugin.h index 84af47872f..2b5492350b 100644 --- a/editor/plugins/root_motion_editor_plugin.h +++ b/editor/plugins/root_motion_editor_plugin.h @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* root_motion_editor_plugin.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 ROOT_MOTION_EDITOR_PLUGIN_H #define ROOT_MOTION_EDITOR_PLUGIN_H diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 1bb7c98114..323dfa681b 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -59,25 +59,10 @@ void ScriptEditorBase::_bind_methods() { ADD_SIGNAL(MethodInfo("search_in_files_requested", PropertyInfo(Variant::STRING, "text"))); } -static bool _can_open_in_editor(Script *p_script) { - +static bool _is_built_in_script(Script *p_script) { String path = p_script->get_path(); - if (path.find("::") != -1) { - //refuse handling this if it can't be edited - - bool valid = false; - for (int i = 0; i < EditorNode::get_singleton()->get_editor_data().get_edited_scene_count(); i++) { - if (path.begins_with(EditorNode::get_singleton()->get_editor_data().get_scene_path(i))) { - valid = true; - break; - } - } - - return valid; - } - - return true; + return path.find("::") != -1; } class EditorScriptCodeCompletionCache : public ScriptCodeCompletionCache { @@ -111,7 +96,7 @@ public: } } - RES get_cached_resource(const String &p_path) { + virtual RES get_cached_resource(const String &p_path) { Map<String, Cache>::Element *E = cached.find(p_path); if (!E) { @@ -149,9 +134,11 @@ public: max_cache_size = 128; max_time_cache = 5 * 60 * 1000; //minutes, five } + + virtual ~EditorScriptCodeCompletionCache() {} }; -void ScriptEditorQuickOpen::popup(const Vector<String> &p_functions, bool p_dontclear) { +void ScriptEditorQuickOpen::popup_dialog(const Vector<String> &p_functions, bool p_dontclear) { popup_centered_ratio(0.6); if (p_dontclear) @@ -223,6 +210,9 @@ void ScriptEditorQuickOpen::_notification(int p_what) { search_box->set_right_icon(get_icon("Search", "EditorIcons")); search_box->set_clear_button_enabled(true); } break; + case NOTIFICATION_EXIT_TREE: { + disconnect("confirmed", this, "_confirmed"); + } break; } } @@ -503,7 +493,14 @@ void ScriptEditor::_open_recent_script(int p_idx) { edit(text_file, true); return; } - // if it's a path then its most likely a deleted file not help + // if it's a path then it's most likely a deleted file not help + } else if (path.find("::") != -1) { + // built-in script + Ref<Script> script = ResourceLoader::load(path); + if (script.is_valid()) { + edit(script, true); + return; + } } else if (!path.is_resource_file()) { _help_class_open(path); return; @@ -805,7 +802,7 @@ bool ScriptEditor::_test_script_times_on_disk(RES p_for_script) { if (se) { RES edited_res = se->get_edited_resource(); - if (edited_res.is_valid() && p_for_script != edited_res) + if (p_for_script.is_valid() && edited_res.is_valid() && p_for_script != edited_res) continue; if (edited_res->get_path() == "" || edited_res->get_path().find("local://") != -1 || edited_res->get_path().find("::") != -1) @@ -862,7 +859,7 @@ void ScriptEditor::_file_dialog_action(String p_file) { 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!")); + editor->show_warning(TTR("Error: could not load file."), TTR("Error!")); file_dialog_option = -1; return; } @@ -939,7 +936,7 @@ void ScriptEditor::_menu_option(int p_option) { switch (p_option) { case FILE_NEW: { - script_create_dialog->config("Node", ".gd"); + script_create_dialog->config("Node", "new_script"); script_create_dialog->popup_centered(Size2(300, 300) * EDSCALE); } break; case FILE_NEW_TEXTFILE: { @@ -976,15 +973,15 @@ void ScriptEditor::_menu_option(int p_option) { } break; case SEARCH_HELP: { - help_search_dialog->popup(); + help_search_dialog->popup_dialog(); } break; case SEARCH_CLASSES: { - help_index->popup(); + help_index->popup_dialog(); } break; case SEARCH_WEBSITE: { - OS::get_singleton()->shell_open("http://docs.godotengine.org/"); + OS::get_singleton()->shell_open("https://docs.godotengine.org/"); } break; case WINDOW_NEXT: { @@ -1212,7 +1209,7 @@ void ScriptEditor::_menu_option(int p_option) { case SEARCH_CLASSES: { - help_index->popup(); + help_index->popup_dialog(); help_index->call_deferred("select_class", help->get_class()); } break; case HELP_SEARCH_FIND: { @@ -1369,7 +1366,9 @@ void ScriptEditor::_notification(int p_what) { if (is_visible()) { find_in_files_button->show(); } else { - find_in_files->hide(); + if (find_in_files->is_visible_in_tree()) { + editor->hide_bottom_panel(); + } find_in_files_button->hide(); } @@ -1422,21 +1421,6 @@ void ScriptEditor::notify_script_changed(const Ref<Script> &p_script) { emit_signal("editor_script_changed", p_script); } -static const Node *_find_node_with_script(const Node *p_node, const RefPtr &p_script) { - - if (p_node->get_script() == p_script) - return p_node; - - for (int i = 0; i < p_node->get_child_count(); i++) { - - const Node *result = _find_node_with_script(p_node->get_child(i), p_script); - if (result) - return result; - } - - return NULL; -} - void ScriptEditor::get_breakpoints(List<String> *p_breakpoints) { for (int i = 0; i < tab_container->get_child_count(); i++) { @@ -1692,7 +1676,6 @@ void ScriptEditor::_update_script_names() { if (restoring_layout) return; - waiting_update_names = false; Set<Ref<Script> > used; Node *edited = EditorNode::get_singleton()->get_edited_scene(); if (edited) { @@ -1743,7 +1726,7 @@ void ScriptEditor::_update_script_names() { } break; case DISPLAY_DIR_AND_NAME: { if (!path.get_base_dir().get_file().empty()) { - sd.name = path.get_base_dir().get_file() + "/" + name; + sd.name = path.get_base_dir().get_file().plus_file(name); } else { sd.name = name; } @@ -1794,8 +1777,8 @@ void ScriptEditor::_update_script_names() { new_cur_tab = i; } } - tab_container->call_deferred("set_current_tab", new_prev_tab); - tab_container->call_deferred("set_current_tab", new_cur_tab); + tab_container->set_current_tab(new_prev_tab); + tab_container->set_current_tab(new_cur_tab); _sort_list_on_update = false; } @@ -1816,8 +1799,12 @@ void ScriptEditor::_update_script_names() { } } - _update_members_overview(); - _update_help_overview(); + if (!waiting_update_names) { + _update_members_overview(); + _update_help_overview(); + } else { + waiting_update_names = false; + } _update_members_overview_visibility(); _update_help_overview_visibility(); _update_script_colors(); @@ -1978,7 +1965,7 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra // doesn't have it, make a new one - ScriptEditorBase *se; + ScriptEditorBase *se = NULL; for (int i = script_editor_func_count - 1; i >= 0; i--) { se = script_editor_funcs[i](p_resource); @@ -2014,6 +2001,7 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra _go_to_tab(tab_container->get_tab_count() - 1); } + _sort_list_on_update = true; _update_script_names(); _save_layout(); se->connect("name_changed", this, "_update_script_names"); @@ -2103,8 +2091,6 @@ void ScriptEditor::_editor_play() { debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_STEP), true); debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_BREAK), false); debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_CONTINUE), true); - - //debugger_gui->start_listening(Globals::get_singleton()->get("debug/debug_port")); } void ScriptEditor::_editor_pause() { @@ -2588,6 +2574,7 @@ void ScriptEditor::_help_class_open(const String &p_class) { eh->go_to_class(p_class, 0); eh->connect("go_to_help", this, "_help_class_goto"); _add_recent_script(p_class); + _sort_list_on_update = true; _update_script_names(); _save_layout(); } @@ -2617,6 +2604,7 @@ void ScriptEditor::_help_class_goto(const String &p_desc) { eh->go_to_help(p_desc); eh->connect("go_to_help", this, "_help_class_goto"); _add_recent_script(eh->get_class()); + _sort_list_on_update = true; _update_script_names(); _save_layout(); } @@ -2725,7 +2713,7 @@ void ScriptEditor::set_scene_root_script(Ref<Script> p_script) { if (bool(EditorSettings::get_singleton()->get("text_editor/external/use_external_editor"))) return; - if (open_dominant && p_script.is_valid() && _can_open_in_editor(p_script.ptr())) { + if (open_dominant && p_script.is_valid()) { edit(p_script); } } @@ -2746,11 +2734,11 @@ void ScriptEditor::set_live_auto_reload_running_scripts(bool p_enabled) { } void ScriptEditor::_help_index(String p_text) { - help_index->popup(); + help_index->popup_dialog(); } void ScriptEditor::_help_search(String p_text) { - help_search_dialog->popup(p_text); + help_search_dialog->popup_dialog(p_text); } void ScriptEditor::_open_script_request(const String &p_path) { @@ -2823,8 +2811,7 @@ void ScriptEditor::_start_find_in_files(bool with_replace) { find_in_files->set_with_replace(with_replace); find_in_files->start_search(); - find_in_files_button->set_pressed(true); - find_in_files->show(); + editor->make_bottom_panel_item_visible(find_in_files); } void ScriptEditor::_on_find_in_files_modified_files(PoolStringArray paths) { @@ -2929,7 +2916,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { script_list = memnew(ItemList); list_split->add_child(script_list); - script_list->set_custom_minimum_size(Size2(150 * EDSCALE, 90)); //need to give a bit of limit to avoid it from disappearing + script_list->set_custom_minimum_size(Size2(150, 90) * EDSCALE); //need to give a bit of limit to avoid it from disappearing script_list->set_v_size_flags(SIZE_EXPAND_FILL); script_split->set_split_offset(140); _sort_list_on_update = true; @@ -2968,7 +2955,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { overview_vbox->add_child(members_overview); members_overview->set_allow_reselect(true); - members_overview->set_custom_minimum_size(Size2(0, 90)); //need to give a bit of limit to avoid it from disappearing + members_overview->set_custom_minimum_size(Size2(0, 90) * EDSCALE); //need to give a bit of limit to avoid it from disappearing members_overview->set_v_size_flags(SIZE_EXPAND_FILL); members_overview->set_allow_rmb_select(true); members_overview->set_drag_forwarding(this); @@ -2976,12 +2963,12 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { help_overview = memnew(ItemList); overview_vbox->add_child(help_overview); help_overview->set_allow_reselect(true); - help_overview->set_custom_minimum_size(Size2(0, 90)); //need to give a bit of limit to avoid it from disappearing + help_overview->set_custom_minimum_size(Size2(0, 90) * EDSCALE); //need to give a bit of limit to avoid it from disappearing help_overview->set_v_size_flags(SIZE_EXPAND_FILL); tab_container = memnew(TabContainer); tab_container->set_tabs_visible(false); - tab_container->set_custom_minimum_size(Size2(200 * EDSCALE, 0)); + tab_container->set_custom_minimum_size(Size2(200, 0) * EDSCALE); script_split->add_child(tab_container); tab_container->set_h_size_flags(SIZE_EXPAND_FILL); @@ -3014,10 +3001,10 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { file_menu->get_popup()->add_separator(); file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/reload_script_soft", TTR("Soft Reload Script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_R), FILE_TOOL_RELOAD_SOFT); file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/copy_path", TTR("Copy Script Path")), FILE_COPY_PATH); - file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/show_in_file_system", TTR("Show In File System")), SHOW_IN_FILE_SYSTEM); + file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/show_in_file_system", TTR("Show in FileSystem")), SHOW_IN_FILE_SYSTEM); file_menu->get_popup()->add_separator(); - file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/history_previous", TTR("History Prev"), KEY_MASK_ALT | KEY_LEFT), WINDOW_PREV); + file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/history_previous", TTR("History Previous"), KEY_MASK_ALT | KEY_LEFT), WINDOW_PREV); file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/history_next", TTR("History Next"), KEY_MASK_ALT | KEY_RIGHT), WINDOW_NEXT); file_menu->get_popup()->add_separator(); @@ -3064,7 +3051,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { debug_menu->get_popup()->add_separator(); //debug_menu->get_popup()->add_check_item("Show Debugger",DEBUG_SHOW); debug_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("debugger/keep_debugger_open", TTR("Keep Debugger Open")), DEBUG_SHOW_KEEP_OPEN); - debug_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("debugger/debug_with_exteral_editor", TTR("Debug with external editor")), DEBUG_WITH_EXTERNAL_EDITOR); + debug_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("debugger/debug_with_external_editor", TTR("Debug with External Editor")), DEBUG_WITH_EXTERNAL_EDITOR); debug_menu->get_popup()->connect("id_pressed", this, "_menu_option"); debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_NEXT), true); @@ -3137,7 +3124,6 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { error_dialog = memnew(AcceptDialog); add_child(error_dialog); - error_dialog->get_ok()->set_text(TTR("OK")); debugger = memnew(ScriptEditorDebugger(editor)); debugger->connect("goto_script_line", this, "_goto_script_line"); @@ -3191,9 +3177,8 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { find_in_files_dialog->connect(FindInFilesDialog::SIGNAL_REPLACE_REQUESTED, this, "_start_find_in_files", varray(true)); add_child(find_in_files_dialog); find_in_files = memnew(FindInFilesPanel); - find_in_files_button = editor->add_bottom_panel_item(TTR("Search results"), find_in_files); - find_in_files_button->set_tooltip(TTR("Search in files")); - find_in_files->set_custom_minimum_size(Size2(0, 200)); + find_in_files_button = editor->add_bottom_panel_item(TTR("Search Results"), find_in_files); + find_in_files->set_custom_minimum_size(Size2(0, 200) * EDSCALE); find_in_files->connect(FindInFilesPanel::SIGNAL_RESULT_SELECTED, this, "_on_find_in_files_result_selected"); find_in_files->connect(FindInFilesPanel::SIGNAL_FILES_MODIFIED, this, "_on_find_in_files_modified_files"); find_in_files->hide(); @@ -3221,7 +3206,17 @@ ScriptEditor::~ScriptEditor() { void ScriptEditorPlugin::edit(Object *p_object) { if (Object::cast_to<Script>(p_object)) { - script_editor->edit(Object::cast_to<Script>(p_object)); + + Script *p_script = Object::cast_to<Script>(p_object); + String scene_path = p_script->get_path().get_slice("::", 0); + + if (_is_built_in_script(p_script) && !EditorNode::get_singleton()->is_scene_open(scene_path)) { + EditorNode::get_singleton()->load_scene(scene_path); + + script_editor->call_deferred("edit", p_script); + } else { + script_editor->edit(p_script); + } } if (Object::cast_to<TextFile>(p_object)) { @@ -3236,13 +3231,7 @@ bool ScriptEditorPlugin::handles(Object *p_object) const { } if (Object::cast_to<Script>(p_object)) { - - bool valid = _can_open_in_editor(Object::cast_to<Script>(p_object)); - - if (!valid) { //user tried to open it by clicking - EditorNode::get_singleton()->show_warning(TTR("Built-in scripts can only be edited when the scene they belong to is loaded")); - } - return valid; + return true; } return p_object->is_class("Script"); diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index 737f17358b..28c07393f7 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -31,6 +31,7 @@ #ifndef SCRIPT_EDITOR_PLUGIN_H #define SCRIPT_EDITOR_PLUGIN_H +#include "core/script_language.h" #include "editor/code_editor.h" #include "editor/editor_help.h" #include "editor/editor_plugin.h" @@ -44,7 +45,6 @@ #include "scene/gui/tree.h" #include "scene/main/timer.h" #include "scene/resources/text_file.h" -#include "script_language.h" class ScriptEditorQuickOpen : public ConfirmationDialog { @@ -67,7 +67,7 @@ protected: static void _bind_methods(); public: - void popup(const Vector<String> &p_functions, bool p_dontclear = false); + void popup_dialog(const Vector<String> &p_functions, bool p_dontclear = false); ScriptEditorQuickOpen(); }; diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 4e7047ee38..c3e2aa86f0 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -30,10 +30,10 @@ #include "script_text_editor.h" +#include "core/os/keyboard.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" #include "editor/script_editor_debugger.h" -#include "os/keyboard.h" Vector<String> ScriptTextEditor::get_functions() { @@ -179,7 +179,7 @@ void ScriptTextEditor::_load_theme_settings() { 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)); + text_edit->add_constant_override("line_spacing", EDITOR_DEF("text_editor/theme/line_spacing", 6)); colors_cache.symbol_color = symbol_color; colors_cache.keyword_color = keyword_color; @@ -317,6 +317,7 @@ void ScriptTextEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_READY: _load_theme_settings(); + _change_syntax_highlighter(EditorSettings::get_singleton()->get_project_metadata("script_text_editor", "syntax_highlighter", 0)); break; } } @@ -772,7 +773,7 @@ void ScriptTextEditor::_edit_option(int p_op) { } break; case EDIT_CLONE_DOWN: { - code_editor->code_lines_down(); + code_editor->clone_lines_down(); } break; case EDIT_TOGGLE_FOLD_LINE: { @@ -816,6 +817,9 @@ void ScriptTextEditor::_edit_option(int p_op) { if (tx->get_selection_to_column() == 0) end -= 1; + int col_to = tx->get_selection_to_column(); + int cursor_pos = tx->cursor_get_column(); + // Check if all lines in the selected block are commented bool is_commented = true; for (int i = begin; i <= end; i++) { @@ -838,19 +842,42 @@ void ScriptTextEditor::_edit_option(int p_op) { } tx->set_line(i, line_text); } + + // Adjust selection & cursor position. + int offset = is_commented ? -1 : 1; + int col_from = tx->get_selection_from_column() > 0 ? tx->get_selection_from_column() + offset : 0; + + if (is_commented && tx->cursor_get_column() == tx->get_line(tx->cursor_get_line()).length() + 1) + cursor_pos += 1; + + if (tx->get_selection_to_column() != 0 && col_to != tx->get_line(tx->get_selection_to_line()).length() + 1) + col_to += offset; + + if (tx->cursor_get_column() != 0) + cursor_pos += offset; + + tx->select(begin, col_from, tx->get_selection_to_line(), col_to); + tx->cursor_set_column(cursor_pos); + } else { int begin = tx->cursor_get_line(); String line_text = tx->get_line(begin); - if (line_text.begins_with(delimiter)) + int col = tx->cursor_get_column(); + if (line_text.begins_with(delimiter)) { line_text = line_text.substr(delimiter.length(), line_text.length()); - else + col -= 1; + } else { line_text = delimiter + line_text; + col += 1; + } + tx->set_line(begin, line_text); + tx->cursor_set_column(col); } tx->end_complex_operation(); tx->update(); - //tx->deselect(); + } break; case EDIT_COMPLETE: { @@ -939,7 +966,8 @@ void ScriptTextEditor::_edit_option(int p_op) { } break; case SEARCH_LOCATE_FUNCTION: { - quick_open->popup(get_functions()); + quick_open->popup_dialog(get_functions()); + quick_open->set_title(TTR("Go to Function")); } break; case SEARCH_GOTO_LINE: { @@ -1058,6 +1086,7 @@ void ScriptTextEditor::_change_syntax_highlighter(int p_idx) { } // highlighter_menu->set_item_checked(p_idx, true); set_syntax_highlighter(highlighters[highlighter_menu->get_item_text(p_idx)]); + EditorSettings::get_singleton()->set_project_metadata("script_text_editor", "syntax_highlighter", p_idx); } void ScriptTextEditor::_bind_methods() { @@ -1256,7 +1285,7 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) { 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 + // Right click is outside the selected text tx->deselect(); } } @@ -1273,7 +1302,6 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) { 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; @@ -1475,9 +1503,9 @@ ScriptTextEditor::ScriptTextEditor() { 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->add_shortcut(ED_SHORTCUT("script_text_editor/convert_to_uppercase", TTR("Uppercase"), KEY_MASK_SHIFT | KEY_F4), EDIT_TO_UPPERCASE); + convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/convert_to_lowercase", TTR("Lowercase"), KEY_MASK_SHIFT | KEY_F5), EDIT_TO_LOWERCASE); + convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/capitalize", TTR("Capitalize"), KEY_MASK_SHIFT | KEY_F6), EDIT_CAPITALIZE); convert_case->connect("id_pressed", this, "_edit_option"); highlighters["Standard"] = NULL; @@ -1549,13 +1577,14 @@ void ScriptTextEditor::register_editor() { ED_SHORTCUT("script_text_editor/unfold_all_lines", TTR("Unfold All Lines"), 0); #ifdef OSX_ENABLED ED_SHORTCUT("script_text_editor/clone_down", TTR("Clone Down"), KEY_MASK_SHIFT | KEY_MASK_CMD | KEY_C); + ED_SHORTCUT("script_text_editor/complete_symbol", TTR("Complete Symbol"), KEY_MASK_CTRL | KEY_SPACE); #else ED_SHORTCUT("script_text_editor/clone_down", TTR("Clone Down"), KEY_MASK_CMD | KEY_B); -#endif ED_SHORTCUT("script_text_editor/complete_symbol", TTR("Complete Symbol"), KEY_MASK_CMD | KEY_SPACE); +#endif ED_SHORTCUT("script_text_editor/trim_trailing_whitespace", TTR("Trim Trailing Whitespace"), KEY_MASK_CMD | KEY_MASK_ALT | KEY_T); - ED_SHORTCUT("script_text_editor/convert_indent_to_spaces", TTR("Convert Indent To Spaces"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Y); - ED_SHORTCUT("script_text_editor/convert_indent_to_tabs", TTR("Convert Indent To Tabs"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_X); + ED_SHORTCUT("script_text_editor/convert_indent_to_spaces", TTR("Convert Indent to Spaces"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Y); + ED_SHORTCUT("script_text_editor/convert_indent_to_tabs", TTR("Convert Indent to Tabs"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_I); ED_SHORTCUT("script_text_editor/auto_indent", TTR("Auto Indent"), KEY_MASK_CMD | KEY_I); #ifdef OSX_ENABLED @@ -1564,27 +1593,28 @@ void ScriptTextEditor::register_editor() { ED_SHORTCUT("script_text_editor/toggle_breakpoint", TTR("Toggle Breakpoint"), KEY_F9); #endif ED_SHORTCUT("script_text_editor/remove_all_breakpoints", TTR("Remove All Breakpoints"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F9); - ED_SHORTCUT("script_text_editor/goto_next_breakpoint", TTR("Goto Next Breakpoint"), KEY_MASK_CMD | KEY_PERIOD); - ED_SHORTCUT("script_text_editor/goto_previous_breakpoint", TTR("Goto Previous Breakpoint"), KEY_MASK_CMD | KEY_COMMA); - - ED_SHORTCUT("script_text_editor/convert_to_uppercase", TTR("Convert To Uppercase"), KEY_MASK_SHIFT | KEY_F4); - ED_SHORTCUT("script_text_editor/convert_to_lowercase", TTR("Convert To Lowercase"), KEY_MASK_SHIFT | KEY_F3); - ED_SHORTCUT("script_text_editor/capitalize", TTR("Capitalize"), KEY_MASK_SHIFT | KEY_F2); + ED_SHORTCUT("script_text_editor/goto_next_breakpoint", TTR("Go to Next Breakpoint"), KEY_MASK_CMD | KEY_PERIOD); + ED_SHORTCUT("script_text_editor/goto_previous_breakpoint", TTR("Go to Previous Breakpoint"), KEY_MASK_CMD | KEY_COMMA); ED_SHORTCUT("script_text_editor/find", TTR("Find..."), KEY_MASK_CMD | KEY_F); #ifdef OSX_ENABLED ED_SHORTCUT("script_text_editor/find_next", TTR("Find Next"), KEY_MASK_CMD | KEY_G); ED_SHORTCUT("script_text_editor/find_previous", TTR("Find Previous"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_G); + ED_SHORTCUT("script_text_editor/replace", TTR("Replace..."), KEY_MASK_ALT | KEY_MASK_CMD | KEY_F); #else ED_SHORTCUT("script_text_editor/find_next", TTR("Find Next"), KEY_F3); ED_SHORTCUT("script_text_editor/find_previous", TTR("Find Previous"), KEY_MASK_SHIFT | KEY_F3); -#endif ED_SHORTCUT("script_text_editor/replace", TTR("Replace..."), KEY_MASK_CMD | KEY_R); +#endif - ED_SHORTCUT("script_text_editor/find_in_files", TTR("Find in files..."), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F); + ED_SHORTCUT("script_text_editor/find_in_files", TTR("Find in Files..."), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F); - ED_SHORTCUT("script_text_editor/goto_function", TTR("Goto Function..."), KEY_MASK_ALT | KEY_MASK_CMD | KEY_F); - ED_SHORTCUT("script_text_editor/goto_line", TTR("Goto Line..."), KEY_MASK_CMD | KEY_L); +#ifdef OSX_ENABLED + ED_SHORTCUT("script_text_editor/goto_function", TTR("Go to Function..."), KEY_MASK_CTRL | KEY_MASK_CMD | KEY_J); +#else + ED_SHORTCUT("script_text_editor/goto_function", TTR("Go to Function..."), KEY_MASK_ALT | KEY_MASK_CMD | KEY_F); +#endif + ED_SHORTCUT("script_text_editor/goto_line", TTR("Go to Line..."), KEY_MASK_CMD | KEY_L); #ifdef OSX_ENABLED ED_SHORTCUT("script_text_editor/contextual_help", TTR("Contextual Help"), KEY_MASK_ALT | KEY_MASK_SHIFT | KEY_SPACE); diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index 5ee40dc90a..638de1b213 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -86,10 +86,7 @@ void ShaderTextEditor::_load_theme_settings() { 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"); get_text_edit()->add_color_override("background_color", background_color); get_text_edit()->add_color_override("completion_background_color", completion_background_color); @@ -140,26 +137,9 @@ void ShaderTextEditor::_load_theme_settings() { get_text_edit()->add_keyword_color(E->get(), keyword_color); } - //colorize core types - //Color basetype_color= EDITOR_DEF("text_editor/base_type_color",Color(0.3,0.3,0.0)); - //colorize comments get_text_edit()->add_color_region("/*", "*/", comment_color, false); get_text_edit()->add_color_region("//", "", comment_color, false); - - /*//colorize strings - Color string_color = EDITOR_DEF("text_editor/string_color",Color::hex(0x6b6f00ff)); - - List<String> strings; - shader->get_shader_mode()->get_string_delimiters(&strings); - - for (List<String>::Element *E=strings.front();E;E=E->next()) { - - String string = E->get(); - String beg = string.get_slice(" ",0); - String end = string.get_slice_count(" ")>1?string.get_slice(" ",1):String(); - get_text_edit()->add_color_region(beg,end,string_color,end==""); - }*/ } void ShaderTextEditor::_check_shader_mode() { @@ -283,7 +263,7 @@ void ShaderEditor::_menu_option(int p_option) { shader_editor->delete_lines(); } break; case EDIT_CLONE_DOWN: { - shader_editor->code_lines_down(); + shader_editor->clone_lines_down(); } break; case EDIT_TOGGLE_COMMENT: { @@ -426,7 +406,7 @@ void ShaderEditor::ensure_select_current() { void ShaderEditor::edit(const Ref<Shader> &p_shader) { - if (p_shader.is_null()) + if (p_shader.is_null() || !p_shader->is_text_shader()) return; shader = p_shader; @@ -468,7 +448,6 @@ void ShaderEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) { int col, row; TextEdit *tx = shader_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")); if (tx->is_right_click_moving_caret()) { @@ -480,7 +459,7 @@ void ShaderEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) { 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 + // Right click is outside the selected text tx->deselect(); } } @@ -606,7 +585,7 @@ void ShaderEditorPlugin::edit(Object *p_object) { bool ShaderEditorPlugin::handles(Object *p_object) const { Shader *shader = Object::cast_to<Shader>(p_object); - return shader != NULL; + return shader != NULL && shader->is_text_shader(); } void ShaderEditorPlugin::make_visible(bool p_visible) { diff --git a/editor/plugins/skeleton_editor_plugin.cpp b/editor/plugins/skeleton_editor_plugin.cpp index 314db4198d..e7d9f1b702 100644 --- a/editor/plugins/skeleton_editor_plugin.cpp +++ b/editor/plugins/skeleton_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -29,9 +29,10 @@ /*************************************************************************/ #include "skeleton_editor_plugin.h" + #include "scene/3d/collision_shape.h" #include "scene/3d/physics_body.h" -#include "scene/3d/physics_joint.h"; +#include "scene/3d/physics_joint.h" #include "scene/resources/capsule_shape.h" #include "scene/resources/sphere_shape.h" #include "spatial_editor_plugin.h" diff --git a/editor/plugins/skeleton_editor_plugin.h b/editor/plugins/skeleton_editor_plugin.h index 0ab94c15b5..aac3e06063 100644 --- a/editor/plugins/skeleton_editor_plugin.h +++ b/editor/plugins/skeleton_editor_plugin.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/skeleton_ik_editor_plugin.cpp b/editor/plugins/skeleton_ik_editor_plugin.cpp index 2d343d3edd..c605548a6b 100644 --- a/editor/plugins/skeleton_ik_editor_plugin.cpp +++ b/editor/plugins/skeleton_ik_editor_plugin.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ diff --git a/editor/plugins/skeleton_ik_editor_plugin.h b/editor/plugins/skeleton_ik_editor_plugin.h index e645bea39a..3e412305c9 100644 --- a/editor/plugins/skeleton_ik_editor_plugin.h +++ b/editor/plugins/skeleton_ik_editor_plugin.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* skeleton_ik_editor_plugin.h */ +/* 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) */ +/* 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 */ diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index 906c51b9f6..fa2f54d0b3 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -30,23 +30,25 @@ #include "spatial_editor_plugin.h" -#include "camera_matrix.h" +#include "core/math/camera_matrix.h" #include "core/os/input.h" - +#include "core/os/keyboard.h" +#include "core/print_string.h" +#include "core/project_settings.h" +#include "core/sort.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" #include "editor/plugins/animation_player_editor_plugin.h" #include "editor/plugins/script_editor_plugin.h" #include "editor/script_editor_debugger.h" #include "editor/spatial_editor_gizmos.h" -#include "os/keyboard.h" -#include "print_string.h" -#include "project_settings.h" #include "scene/3d/camera.h" +#include "scene/3d/collision_shape.h" +#include "scene/3d/mesh_instance.h" +#include "scene/3d/physics_body.h" #include "scene/3d/visual_instance.h" #include "scene/resources/packed_scene.h" #include "scene/resources/surface_tool.h" -#include "sort.h" #define DISTANCE_DEFAULT 4 @@ -274,7 +276,7 @@ void SpatialEditorViewport::_select_clicked(bool p_append, bool p_single) { _select(sp, clicked_wants_append, true); } -void SpatialEditorViewport::_select(Spatial *p_node, bool p_append, bool p_single) { +void SpatialEditorViewport::_select(Node *p_node, bool p_append, bool p_single) { if (!p_append) { editor_selection->clear(); @@ -307,7 +309,7 @@ ObjectID SpatialEditorViewport::_select_ray(const Point2 &p_pos, bool p_append, Node *edited_scene = get_tree()->get_edited_scene_root(); ObjectID closest = 0; - Spatial *item = NULL; + Node *item = NULL; float closest_dist = 1e20; int selected_handle = -1; @@ -340,20 +342,15 @@ ObjectID SpatialEditorViewport::_select_ray(const Point2 &p_pos, bool p_append, continue; if (dist < closest_dist) { - //make sure that whathever is selected is editable - while (spat && spat != edited_scene && spat->get_owner() != edited_scene && !edited_scene->is_editable_instance(spat->get_owner())) { - spat = Object::cast_to<Spatial>(spat->get_owner()); + item = Object::cast_to<Node>(spat); + while (item->get_owner() && item->get_owner() != edited_scene && !edited_scene->is_editable_instance(item->get_owner())) { + item = item->get_owner(); } - if (spat) { - item = spat; - closest = spat->get_instance_id(); - closest_dist = dist; - selected_handle = handle; - } else { - ERR_PRINT("Bug?"); - } + closest = item->get_instance_id(); + closest_dist = dist; + selected_handle = handle; } } @@ -499,7 +496,7 @@ void SpatialEditorViewport::_select_region() { } Vector<ObjectID> instances = VisualServer::get_singleton()->instances_cull_convex(frustum, get_tree()->get_root()->get_world()->get_scenario()); - Vector<Spatial *> selected; + Vector<Node *> selected; Node *edited_scene = get_tree()->get_edited_scene_root(); @@ -509,12 +506,12 @@ void SpatialEditorViewport::_select_region() { if (!sp) continue; - Spatial *root_sp = sp; - while (root_sp && root_sp != edited_scene && root_sp->get_owner() != edited_scene && !edited_scene->is_editable_instance(root_sp->get_owner())) { - root_sp = Object::cast_to<Spatial>(root_sp->get_owner()); + Node *item = Object::cast_to<Node>(sp); + while (item->get_owner() && item->get_owner() != edited_scene && !edited_scene->is_editable_instance(item->get_owner())) { + item = item->get_owner(); } - if (selected.find(root_sp) != -1) continue; + if (selected.find(item) != -1) continue; Ref<EditorSpatialGizmo> seg = sp->get_gizmo(); @@ -522,7 +519,7 @@ void SpatialEditorViewport::_select_region() { continue; if (seg->intersect_frustum(camera, frustum)) { - selected.push_back(root_sp); + selected.push_back(item); } } @@ -534,12 +531,12 @@ void SpatialEditorViewport::_select_region() { void SpatialEditorViewport::_update_name() { - String ortho = orthogonal ? TTR("Orthogonal") : TTR("Perspective"); + String view_mode = orthogonal ? TTR("Orthogonal") : TTR("Perspective"); if (name != "") - view_menu->set_text("[ " + name + " " + ortho + " ]"); + view_menu->set_text(name + " " + view_mode); else - view_menu->set_text("[ " + ortho + " ]"); + view_menu->set_text(view_mode); view_menu->set_size(Vector2(0, 0)); // resets the button size } @@ -850,11 +847,7 @@ void SpatialEditorViewport::_list_select(Ref<InputEventMouseButton> b) { Spatial *spat = selection_results[i].item; - Ref<Texture> icon; - if (spat->has_meta("_editor_icon")) - icon = spat->get_meta("_editor_icon"); - else - icon = get_icon(has_icon(spat->get_class(), "EditorIcons") ? spat->get_class() : String("Object"), "EditorIcons"); + Ref<Texture> icon = EditorNode::get_singleton()->get_object_icon(spat, "Node"); String node_path = "/" + root_name + "/" + root_path.rel_path_to(spat->get_path()); @@ -959,8 +952,10 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { if (b->is_pressed()) { int mod = _get_key_modifier(b); - if (mod == _get_key_modifier_setting("editors/3d/freelook/freelook_activation_modifier")) { - set_freelook_active(true); + if (!orthogonal) { + if (mod == _get_key_modifier_setting("editors/3d/freelook/freelook_activation_modifier")) { + set_freelook_active(true); + } } } else { set_freelook_active(false); @@ -1004,6 +999,10 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { set_message(TTR("View Plane Transform."), 2); } break; + case TRANSFORM_YZ: + case TRANSFORM_XZ: + case TRANSFORM_XY: { + } break; } } } break; @@ -1137,7 +1136,7 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { } if (clicked) { _select_clicked(clicked_wants_append, true); - //clickd processing was deferred + // Processing was deferred. clicked = 0; } @@ -1244,7 +1243,7 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { if (!clicked_includes_current) { _select_clicked(clicked_wants_append, true); - //clickd processing was deferred + // Processing was deferred. } _compute_edit(_edit.mouse_pos); @@ -1342,7 +1341,8 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { List<Node *> &selection = editor_selection->get_selected_node_list(); - bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW); // Disable local transformation for TRANSFORM_VIEW + // Disable local transformation for TRANSFORM_VIEW + bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW); float snap = 0; if (_edit.snap || spatial_editor->is_snap_enabled()) { @@ -1471,7 +1471,8 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { List<Node *> &selection = editor_selection->get_selected_node_list(); - bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW); // Disable local transformation for TRANSFORM_VIEW + // Disable local transformation for TRANSFORM_VIEW + bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW); float snap = 0; if (_edit.snap || spatial_editor->is_snap_enabled()) { @@ -1552,6 +1553,10 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(2)); axis = Vector3(0, 0, 1); break; + case TRANSFORM_YZ: + case TRANSFORM_XZ: + case TRANSFORM_XY: + break; } Vector3 intersection; @@ -1642,6 +1647,8 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { nav_mode = NAVIGATION_ZOOM; } else if (freelook_active) { nav_mode = NAVIGATION_LOOK; + } else if (orthogonal) { + nav_mode = NAVIGATION_PAN; } } else if (m->get_button_mask() & BUTTON_MASK_MIDDLE) { @@ -1792,7 +1799,8 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { if (ED_IS_SHORTCUT("spatial_editor/focus_selection", p_event)) { _menu_option(VIEW_CENTER_TO_SELECTION); } - if (ED_IS_SHORTCUT("spatial_editor/switch_perspective_orthogonal", p_event)) { + // Orthgonal mode doesn't work in freelook. + if (!freelook_active && ED_IS_SHORTCUT("spatial_editor/switch_perspective_orthogonal", p_event)) { _menu_option(orthogonal ? VIEW_PERSPECTIVE : VIEW_ORTHOGONAL); _update_name(); } @@ -1822,7 +1830,8 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { set_message(TTR("Animation Key Inserted.")); } - if (ED_IS_SHORTCUT("spatial_editor/freelook_toggle", p_event)) { + // Freelook doesn't work in orthogonal mode. + if (!orthogonal && ED_IS_SHORTCUT("spatial_editor/freelook_toggle", p_event)) { set_freelook_active(!is_freelook_active()); } else if (k->get_scancode() == KEY_ESCAPE) { @@ -1910,37 +1919,38 @@ void SpatialEditorViewport::_nav_orbit(Ref<InputEventWithModifiers> p_event, con void SpatialEditorViewport::_nav_look(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative) { - // Freelook only works properly in perspective. - // It technically works too in ortho, but it's awful for a user due to fov being near zero - if (!orthogonal) { - 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"); - - // Note: do NOT assume the camera has the "current" transform, because it is interpolated and may have "lag". - Transform prev_camera_transform = to_camera_transform(cursor); + if (orthogonal) { + _nav_pan(p_event, p_relative); + return; + } - if (invert_y_axis) { - cursor.x_rot -= p_relative.y * radians_per_pixel; - } else { - cursor.x_rot += p_relative.y * radians_per_pixel; - } - cursor.y_rot += p_relative.x * radians_per_pixel; - if (cursor.x_rot > Math_PI / 2.0) - cursor.x_rot = Math_PI / 2.0; - if (cursor.x_rot < -Math_PI / 2.0) - cursor.x_rot = -Math_PI / 2.0; + 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"); - // Look is like the opposite of Orbit: the focus point rotates around the camera - Transform camera_transform = to_camera_transform(cursor); - Vector3 pos = camera_transform.xform(Vector3(0, 0, 0)); - Vector3 prev_pos = prev_camera_transform.xform(Vector3(0, 0, 0)); - Vector3 diff = prev_pos - pos; - cursor.pos += diff; + // Note: do NOT assume the camera has the "current" transform, because it is interpolated and may have "lag". + Transform prev_camera_transform = to_camera_transform(cursor); - name = ""; - _update_name(); + if (invert_y_axis) { + cursor.x_rot -= p_relative.y * radians_per_pixel; + } else { + cursor.x_rot += p_relative.y * radians_per_pixel; } + cursor.y_rot += p_relative.x * radians_per_pixel; + if (cursor.x_rot > Math_PI / 2.0) + cursor.x_rot = Math_PI / 2.0; + if (cursor.x_rot < -Math_PI / 2.0) + cursor.x_rot = -Math_PI / 2.0; + + // Look is like the opposite of Orbit: the focus point rotates around the camera + Transform camera_transform = to_camera_transform(cursor); + Vector3 pos = camera_transform.xform(Vector3(0, 0, 0)); + Vector3 prev_pos = prev_camera_transform.xform(Vector3(0, 0, 0)); + Vector3 diff = prev_pos - pos; + cursor.pos += diff; + + name = ""; + _update_name(); } void SpatialEditorViewport::set_freelook_active(bool active_now) { @@ -2082,7 +2092,7 @@ void SpatialEditorViewport::set_message(String p_message, float p_time) { } void SpatialEditorPlugin::edited_scene_changed() { - for (int i = 0; i < SpatialEditor::VIEWPORTS_COUNT; i++) { + for (uint32_t i = 0; i < SpatialEditor::VIEWPORTS_COUNT; i++) { SpatialEditorViewport *viewport = SpatialEditor::get_singleton()->get_editor_viewport(i); if (viewport->is_visible()) { viewport->notification(Control::NOTIFICATION_VISIBILITY_CHANGED); @@ -2123,7 +2133,7 @@ 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) { + if (previewing_cinema && 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 @@ -2206,7 +2216,7 @@ void SpatialEditorViewport::_notification(int p_what) { bool shrink = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_HALF_RESOLUTION)); - if (shrink != viewport_container->get_stretch_shrink() > 1) { + if (shrink != (viewport_container->get_stretch_shrink() > 1)) { viewport_container->set_stretch_shrink(shrink ? 2 : 1); } @@ -2221,8 +2231,21 @@ void SpatialEditorViewport::_notification(int p_what) { bool show_info = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_INFORMATION)); info_label->set_visible(show_info); + Camera *current_camera; + + if (previewing) { + current_camera = previewing; + } else { + current_camera = camera; + } + if (show_info) { String text; + text += "X: " + rtos(current_camera->get_translation().x).pad_decimals(1) + "\n"; + text += "Y: " + rtos(current_camera->get_translation().y).pad_decimals(1) + "\n"; + text += "Z: " + rtos(current_camera->get_translation().z).pad_decimals(1) + "\n"; + text += TTR("Pitch") + ": " + itos(Math::round(current_camera->get_rotation_degrees().x)) + "\n"; + text += TTR("Yaw") + ": " + itos(Math::round(current_camera->get_rotation_degrees().y)) + "\n\n"; text += TTR("Objects Drawn") + ": " + itos(viewport->get_render_info(Viewport::RENDER_INFO_OBJECTS_IN_FRAME)) + "\n"; text += TTR("Material Changes") + ": " + itos(viewport->get_render_info(Viewport::RENDER_INFO_MATERIAL_CHANGES_IN_FRAME)) + "\n"; text += TTR("Shader Changes") + ": " + itos(viewport->get_render_info(Viewport::RENDER_INFO_SHADER_CHANGES_IN_FRAME)) + "\n"; @@ -2249,6 +2272,11 @@ void SpatialEditorViewport::_notification(int p_what) { 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 (lock_rotation) { + float locked_half_width = locked_label->get_size().width / 2.0f; + locked_label->set_anchor_and_margin(MARGIN_LEFT, 0.5f, -locked_half_width); + } } if (p_what == NOTIFICATION_ENTER_TREE) { @@ -2259,21 +2287,36 @@ void SpatialEditorViewport::_notification(int p_what) { surface->connect("mouse_exited", this, "_surface_mouse_exit"); surface->connect("focus_entered", this, "_surface_focus_enter"); 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); } + if (p_what == NOTIFICATION_EXIT_TREE) { _finish_gizmo_instances(); } - if (p_what == NOTIFICATION_MOUSE_ENTER) { - } + if (p_what == NOTIFICATION_THEME_CHANGED) { + + view_menu->set_icon(get_icon("GuiMiniTabMenu", "EditorIcons")); + preview_camera->set_icon(get_icon("Camera", "EditorIcons")); + + view_menu->add_style_override("normal", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles")); + view_menu->add_style_override("hover", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles")); + view_menu->add_style_override("pressed", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles")); + view_menu->add_style_override("focus", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles")); + view_menu->add_style_override("disabled", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles")); - if (p_what == NOTIFICATION_DRAW) { + preview_camera->add_style_override("normal", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles")); + preview_camera->add_style_override("hover", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles")); + preview_camera->add_style_override("pressed", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles")); + preview_camera->add_style_override("focus", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles")); + preview_camera->add_style_override("disabled", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles")); + + 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")); + locked_label->add_style_override("normal", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles")); } } @@ -2319,12 +2362,12 @@ void SpatialEditorViewport::_draw() { EditorPluginList *over_plugin_list = EditorNode::get_singleton()->get_editor_plugins_over(); if (!over_plugin_list->empty()) { - over_plugin_list->forward_draw_over_viewport(surface); + over_plugin_list->forward_spatial_draw_over_viewport(surface); } EditorPluginList *force_over_plugin_list = editor->get_editor_plugins_force_over(); if (!force_over_plugin_list->empty()) { - force_over_plugin_list->forward_force_draw_over_viewport(surface); + force_over_plugin_list->forward_spatial_force_draw_over_viewport(surface); } if (surface->has_focus()) { @@ -2353,7 +2396,6 @@ void SpatialEditorViewport::_draw() { Point2 center = _point_to_screen(_edit.center); VisualServer::get_singleton()->canvas_item_add_line(ci, _edit.mouse_pos, center, Color(0.4, 0.7, 1.0, 0.8)); } - if (previewing) { Size2 ss = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height")); @@ -2515,8 +2557,14 @@ void SpatialEditorViewport::_menu_option(int p_option) { if (!se) continue; - Transform xform = camera_transform; - xform.scale_basis(sp->get_scale()); + Transform xform; + if (orthogonal) { + xform = sp->get_global_transform(); + xform.basis.set_euler(camera_transform.basis.get_euler()); + } else { + xform = camera_transform; + xform.scale_basis(sp->get_scale()); + } undo_redo->add_do_method(sp, "set_global_transform", xform); undo_redo->add_undo_method(sp, "set_global_transform", sp->get_global_gizmo_transform()); @@ -2564,9 +2612,9 @@ void SpatialEditorViewport::_menu_option(int p_option) { lock_rotation = !current; view_menu->get_popup()->set_item_checked(idx, !current); if (lock_rotation) { - view_menu->set_icon(get_icon("Lock", "EditorIcons")); + locked_label->show(); } else { - view_menu->set_icon(Ref<Texture>()); + locked_label->hide(); } } break; @@ -2636,11 +2684,6 @@ void SpatialEditorViewport::_menu_option(int p_option) { bool current = view_menu->get_popup()->is_item_checked(idx); view_menu->get_popup()->set_item_checked(idx, !current); - if (current) - preview_camera->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 10 * EDSCALE); - else - preview_camera->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 15 * EDSCALE + fps_label->get_size().height); - } break; case VIEW_DISPLAY_NORMAL: { @@ -2754,7 +2797,7 @@ void SpatialEditorViewport::_toggle_camera_preview(bool p_activate) { VS::get_singleton()->viewport_attach_camera(viewport->get_viewport_rid(), camera->get_camera()); //restore if (!preview) preview_camera->hide(); - view_menu->show(); + view_menu->set_disabled(false); surface->update(); } else { @@ -2762,7 +2805,7 @@ void SpatialEditorViewport::_toggle_camera_preview(bool p_activate) { previewing = preview; previewing->connect("tree_exiting", this, "_preview_exited_scene"); VS::get_singleton()->viewport_attach_camera(viewport->get_viewport_rid(), preview->get_camera()); //replace - view_menu->hide(); + view_menu->set_disabled(true); surface->update(); } } @@ -3274,7 +3317,6 @@ 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("OK")); accept->set_text(vformat(TTR("Error instancing scene from %s"), files_str.c_str())); accept->popup_centered_minsize(); } @@ -3355,7 +3397,6 @@ void SpatialEditorViewport::drop_data_fw(const Point2 &p_point, const Variant &p if (root_node) { list.push_back(root_node); } else { - accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("No parent to instance a child at.")); accept->popup_centered_minsize(); _remove_preview(); @@ -3363,7 +3404,6 @@ void SpatialEditorViewport::drop_data_fw(const Point2 &p_point, const Variant &p } } if (list.size() != 1) { - accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("This operation requires a single selected node.")); accept->popup_centered_minsize(); _remove_preview(); @@ -3422,9 +3462,10 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed surface->set_focus_mode(FOCUS_ALL); view_menu = memnew(MenuButton); + view_menu->set_flat(false); surface->add_child(view_menu); - view_menu->set_position(Point2(4, 4) * EDSCALE); - view_menu->set_self_modulate(Color(1, 1, 1, 0.5)); + view_menu->set_position(Point2(10, 10) * EDSCALE); + view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/top_view"), VIEW_TOP); view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/bottom_view"), VIEW_BOTTOM); view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/left_view"), VIEW_LEFT); @@ -3475,12 +3516,8 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed ED_SHORTCUT("spatial_editor/freelook_down", TTR("Freelook Down"), KEY_Q); ED_SHORTCUT("spatial_editor/freelook_speed_modifier", TTR("Freelook Speed Modifier"), KEY_SHIFT); - preview_camera = memnew(Button); - preview_camera->set_toggle_mode(true); - preview_camera->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_END, -90 * EDSCALE); - preview_camera->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 10 * EDSCALE); - preview_camera->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, -10 * EDSCALE); - preview_camera->set_h_grow_direction(GROW_DIRECTION_BEGIN); + preview_camera = memnew(CheckBox); + preview_camera->set_position(Point2(10, 38) * EDSCALE); // Below the 'view_menu' MenuButton. preview_camera->set_text(TTR("Preview")); surface->add_child(preview_camera); preview_camera->hide(); @@ -3500,7 +3537,6 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed surface->add_child(info_label); info_label->hide(); - // FPS Counter. fps_label = memnew(Label); fps_label->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_END, -90 * EDSCALE); fps_label->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 10 * EDSCALE); @@ -3518,6 +3554,16 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed cinema_label->hide(); previewing_cinema = false; + locked_label = memnew(Label); + locked_label->set_anchor_and_margin(MARGIN_TOP, ANCHOR_END, -20 * EDSCALE); + locked_label->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, -10 * EDSCALE); + locked_label->set_h_grow_direction(GROW_DIRECTION_END); + locked_label->set_v_grow_direction(GROW_DIRECTION_BEGIN); + locked_label->set_align(Label::ALIGN_CENTER); + surface->add_child(locked_label); + locked_label->set_text(TTR("View Rotation Locked")); + locked_label->hide(); + accept = NULL; freelook_active = false; @@ -3546,69 +3592,77 @@ void SpatialEditorViewportContainer::_gui_input(const Ref<InputEvent> &p_event) Ref<InputEventMouseButton> mb = p_event; - if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT) { - Vector2 size = get_size(); + if (mb->is_pressed()) { + Vector2 size = get_size(); - int h_sep = get_constant("separation", "HSplitContainer"); - int v_sep = get_constant("separation", "VSplitContainer"); + int h_sep = get_constant("separation", "HSplitContainer"); + int v_sep = get_constant("separation", "VSplitContainer"); - int mid_w = size.width * ratio_h; - int mid_h = size.height * ratio_v; + int mid_w = size.width * ratio_h; + int mid_h = size.height * ratio_v; - dragging_h = mb->get_position().x > (mid_w - h_sep / 2) && mb->get_position().x < (mid_w + h_sep / 2); - dragging_v = mb->get_position().y > (mid_h - v_sep / 2) && mb->get_position().y < (mid_h + v_sep / 2); + dragging_h = mb->get_position().x > (mid_w - h_sep / 2) && mb->get_position().x < (mid_w + h_sep / 2); + dragging_v = mb->get_position().y > (mid_h - v_sep / 2) && mb->get_position().y < (mid_h + v_sep / 2); - drag_begin_pos = mb->get_position(); - drag_begin_ratio.x = ratio_h; - drag_begin_ratio.y = ratio_v; + drag_begin_pos = mb->get_position(); + drag_begin_ratio.x = ratio_h; + drag_begin_ratio.y = ratio_v; - switch (view) { - case VIEW_USE_1_VIEWPORT: { - - dragging_h = false; - dragging_v = false; + switch (view) { + case VIEW_USE_1_VIEWPORT: { - } break; - case VIEW_USE_2_VIEWPORTS: { + dragging_h = false; + dragging_v = false; - dragging_h = false; + } break; + case VIEW_USE_2_VIEWPORTS: { - } break; - case VIEW_USE_2_VIEWPORTS_ALT: { - - dragging_v = false; + dragging_h = false; - } break; - case VIEW_USE_3_VIEWPORTS: { + } break; + case VIEW_USE_2_VIEWPORTS_ALT: { - if (dragging_v) - dragging_h = false; - else dragging_v = false; - } break; - case VIEW_USE_3_VIEWPORTS_ALT: { + } break; + case VIEW_USE_3_VIEWPORTS: + case VIEW_USE_3_VIEWPORTS_ALT: + case VIEW_USE_4_VIEWPORTS: { - if (dragging_h) - dragging_v = false; - else - dragging_h = false; - } break; - case VIEW_USE_4_VIEWPORTS: { + // Do nothing. - } break; + } break; + } + } else { + dragging_h = false; + dragging_v = false; } } - if (mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { - dragging_h = false; - dragging_v = false; - } - Ref<InputEventMouseMotion> mm = p_event; - if (mm.is_valid() && (dragging_h || dragging_v)) { + if (mm.is_valid()) { + + if (view == VIEW_USE_3_VIEWPORTS || view == VIEW_USE_3_VIEWPORTS_ALT || view == VIEW_USE_4_VIEWPORTS) { + Vector2 size = get_size(); + + int h_sep = get_constant("separation", "HSplitContainer"); + int v_sep = get_constant("separation", "VSplitContainer"); + + int mid_w = size.width * ratio_h; + int mid_h = size.height * ratio_v; + + bool was_hovering_h = hovering_h; + bool was_hovering_v = hovering_v; + hovering_h = mm->get_position().x > (mid_w - h_sep / 2) && mm->get_position().x < (mid_w + h_sep / 2); + hovering_v = mm->get_position().y > (mid_h - v_sep / 2) && mm->get_position().y < (mid_h + v_sep / 2); + + if (was_hovering_h != hovering_h || was_hovering_v != hovering_v) { + update(); + } + } if (dragging_h) { float new_ratio = drag_begin_ratio.x + (mm->get_position().x - drag_begin_pos.x) / get_size().width; @@ -3638,9 +3692,12 @@ void SpatialEditorViewportContainer::_notification(int p_what) { if (p_what == NOTIFICATION_DRAW && mouseover) { Ref<Texture> h_grabber = get_icon("grabber", "HSplitContainer"); - Ref<Texture> v_grabber = get_icon("grabber", "VSplitContainer"); + Ref<Texture> hdiag_grabber = get_icon("GuiViewportHdiagsplitter", "EditorIcons"); + Ref<Texture> vdiag_grabber = get_icon("GuiViewportVdiagsplitter", "EditorIcons"); + Ref<Texture> vh_grabber = get_icon("GuiViewportVhsplitter", "EditorIcons"); + Vector2 size = get_size(); int h_sep = get_constant("separation", "HSplitContainer"); @@ -3657,35 +3714,62 @@ void SpatialEditorViewportContainer::_notification(int p_what) { case VIEW_USE_1_VIEWPORT: { - //nothing to show + // Nothing to show. } break; case VIEW_USE_2_VIEWPORTS: { draw_texture(v_grabber, Vector2((size.width - v_grabber->get_width()) / 2, mid_h - v_grabber->get_height() / 2)); + set_default_cursor_shape(CURSOR_VSPLIT); } break; case VIEW_USE_2_VIEWPORTS_ALT: { draw_texture(h_grabber, Vector2(mid_w - h_grabber->get_width() / 2, (size.height - h_grabber->get_height()) / 2)); + set_default_cursor_shape(CURSOR_HSPLIT); } break; case VIEW_USE_3_VIEWPORTS: { - draw_texture(v_grabber, Vector2((size.width - v_grabber->get_width()) / 2, mid_h - v_grabber->get_height() / 2)); - draw_texture(h_grabber, Vector2(mid_w - h_grabber->get_width() / 2, mid_h + v_grabber->get_height() / 2 + (size_bottom - h_grabber->get_height()) / 2)); + if ((hovering_v && hovering_h && !dragging_v && !dragging_h) || (dragging_v && dragging_h)) { + draw_texture(hdiag_grabber, Vector2(mid_w - hdiag_grabber->get_width() / 2, mid_h - v_grabber->get_height() / 4)); + set_default_cursor_shape(CURSOR_DRAG); + } else if ((hovering_v && !dragging_h) || dragging_v) { + draw_texture(v_grabber, Vector2((size.width - v_grabber->get_width()) / 2, mid_h - v_grabber->get_height() / 2)); + set_default_cursor_shape(CURSOR_VSPLIT); + } else if (hovering_h || dragging_h) { + draw_texture(h_grabber, Vector2(mid_w - h_grabber->get_width() / 2, mid_h + v_grabber->get_height() / 2 + (size_bottom - h_grabber->get_height()) / 2)); + set_default_cursor_shape(CURSOR_HSPLIT); + } } break; case VIEW_USE_3_VIEWPORTS_ALT: { - draw_texture(v_grabber, Vector2((size_left - v_grabber->get_width()) / 2, mid_h - v_grabber->get_height() / 2)); - draw_texture(h_grabber, Vector2(mid_w - h_grabber->get_width() / 2, (size.height - h_grabber->get_height()) / 2)); + if ((hovering_v && hovering_h && !dragging_v && !dragging_h) || (dragging_v && dragging_h)) { + draw_texture(vdiag_grabber, Vector2(mid_w - vdiag_grabber->get_width() + v_grabber->get_height() / 4, mid_h - vdiag_grabber->get_height() / 2)); + set_default_cursor_shape(CURSOR_DRAG); + } else if ((hovering_v && !dragging_h) || dragging_v) { + draw_texture(v_grabber, Vector2((size_left - v_grabber->get_width()) / 2, mid_h - v_grabber->get_height() / 2)); + set_default_cursor_shape(CURSOR_VSPLIT); + } else if (hovering_h || dragging_h) { + draw_texture(h_grabber, Vector2(mid_w - h_grabber->get_width() / 2, (size.height - h_grabber->get_height()) / 2)); + set_default_cursor_shape(CURSOR_HSPLIT); + } + } break; case VIEW_USE_4_VIEWPORTS: { Vector2 half(mid_w, mid_h); - draw_texture(v_grabber, half - v_grabber->get_size() / 2.0); - draw_texture(h_grabber, half - h_grabber->get_size() / 2.0); + if ((hovering_v && hovering_h && !dragging_v && !dragging_h) || (dragging_v && dragging_h)) { + draw_texture(vh_grabber, half - vh_grabber->get_size() / 2.0); + set_default_cursor_shape(CURSOR_DRAG); + } else if ((hovering_v && !dragging_h) || dragging_v) { + draw_texture(v_grabber, half - v_grabber->get_size() / 2.0); + set_default_cursor_shape(CURSOR_VSPLIT); + } else if (hovering_h || dragging_h) { + draw_texture(h_grabber, half - h_grabber->get_size() / 2.0); + set_default_cursor_shape(CURSOR_HSPLIT); + } } break; } @@ -3729,6 +3813,7 @@ void SpatialEditorViewportContainer::_notification(int p_what) { case VIEW_USE_1_VIEWPORT: { + viewports[0]->show(); for (int i = 1; i < 4; i++) { viewports[i]->hide(); @@ -3739,7 +3824,7 @@ void SpatialEditorViewportContainer::_notification(int p_what) { } break; case VIEW_USE_2_VIEWPORTS: { - for (int i = 1; i < 4; i++) { + for (int i = 0; i < 4; i++) { if (i == 1 || i == 3) viewports[i]->hide(); @@ -3753,7 +3838,7 @@ void SpatialEditorViewportContainer::_notification(int p_what) { } break; case VIEW_USE_2_VIEWPORTS_ALT: { - for (int i = 1; i < 4; i++) { + for (int i = 0; i < 4; i++) { if (i == 1 || i == 3) viewports[i]->hide(); @@ -3766,7 +3851,7 @@ void SpatialEditorViewportContainer::_notification(int p_what) { } break; case VIEW_USE_3_VIEWPORTS: { - for (int i = 1; i < 4; i++) { + for (int i = 0; i < 4; i++) { if (i == 1) viewports[i]->hide(); @@ -3781,7 +3866,7 @@ void SpatialEditorViewportContainer::_notification(int p_what) { } break; case VIEW_USE_3_VIEWPORTS_ALT: { - for (int i = 1; i < 4; i++) { + for (int i = 0; i < 4; i++) { if (i == 1) viewports[i]->hide(); @@ -3796,7 +3881,7 @@ void SpatialEditorViewportContainer::_notification(int p_what) { } break; case VIEW_USE_4_VIEWPORTS: { - for (int i = 1; i < 4; i++) { + for (int i = 0; i < 4; i++) { viewports[i]->show(); } @@ -3829,10 +3914,13 @@ void SpatialEditorViewportContainer::_bind_methods() { SpatialEditorViewportContainer::SpatialEditorViewportContainer() { + set_clip_contents(true); view = VIEW_USE_1_VIEWPORT; mouseover = false; ratio_h = 0.5; ratio_v = 0.5; + hovering_v = false; + hovering_h = false; dragging_v = false; dragging_h = false; } @@ -3914,8 +4002,9 @@ void _update_all_gizmos(Node *p_node) { } } -void SpatialEditor::update_all_gizmos() { - _update_all_gizmos(SceneTree::get_singleton()->get_root()); +void SpatialEditor::update_all_gizmos(Node *p_node) { + if (!p_node) p_node = SceneTree::get_singleton()->get_root(); + _update_all_gizmos(p_node); } Object *SpatialEditor::_get_editor_data(Object *p_what) { @@ -4103,7 +4192,7 @@ void SpatialEditor::set_state(const Dictionary &p_state) { 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++) { + for (int i = 0; i < keys.size(); i++) { if (gizmo_plugins.write[j]->get_name() == keys[i]) { state = gizmos_status[keys[i]]; } @@ -4427,6 +4516,8 @@ void SpatialEditor::_menu_item_pressed(int p_option) { void SpatialEditor::_init_indicators() { { + origin_enabled = true; + grid_enabled = true; indicator_mat.instance(); indicator_mat->set_flag(SpatialMaterial::FLAG_UNSHADED, true); @@ -4467,9 +4558,6 @@ void SpatialEditor::_init_indicators() { VS::get_singleton()->instance_set_layer_mask(origin_instance, 1 << SpatialEditorViewport::GIZMO_GRID_LAYER); VisualServer::get_singleton()->instance_geometry_set_cast_shadows_setting(origin_instance, VS::SHADOW_CASTING_SETTING_OFF); - - grid_enabled = true; - last_grid_snap = 1; } { @@ -4990,32 +5078,29 @@ void SpatialEditor::_unhandled_key_input(Ref<InputEvent> p_event) { if (!k->is_pressed()) return; - if (ED_IS_SHORTCUT("spatial_editor/tool_select", p_event)) + if (ED_IS_SHORTCUT("spatial_editor/tool_select", p_event)) { _menu_item_pressed(MENU_TOOL_SELECT); - - else if (ED_IS_SHORTCUT("spatial_editor/tool_move", p_event)) + } else if (ED_IS_SHORTCUT("spatial_editor/tool_move", p_event)) { _menu_item_pressed(MENU_TOOL_MOVE); - - else if (ED_IS_SHORTCUT("spatial_editor/tool_rotate", p_event)) + } else if (ED_IS_SHORTCUT("spatial_editor/tool_rotate", p_event)) { _menu_item_pressed(MENU_TOOL_ROTATE); - - else if (ED_IS_SHORTCUT("spatial_editor/tool_scale", 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)) + } 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)) + } else if (ED_IS_SHORTCUT("spatial_editor/local_coords", p_event)) { if (are_local_coords_enabled()) { _menu_item_toggled(false, MENU_TOOL_LOCAL_COORDS); } else { _menu_item_toggled(true, MENU_TOOL_LOCAL_COORDS); } - else if (ED_IS_SHORTCUT("spatial_editor/snap", p_event)) + } else if (ED_IS_SHORTCUT("spatial_editor/snap", p_event)) { if (is_snap_enabled()) { _menu_item_toggled(false, MENU_TOOL_USE_SNAP); } else { _menu_item_toggled(true, MENU_TOOL_USE_SNAP); } + } } } } @@ -5291,6 +5376,8 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { 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")); + hbc_menu->add_child(memnew(VSeparator)); + tool_button[TOOL_MODE_MOVE] = memnew(ToolButton); hbc_menu->add_child(tool_button[TOOL_MODE_MOVE]); tool_button[TOOL_MODE_MOVE]->set_toggle_mode(true); @@ -5315,6 +5402,8 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { tool_button[TOOL_MODE_SCALE]->connect("pressed", this, "_menu_item_pressed", button_binds); tool_button[TOOL_MODE_SCALE]->set_tooltip(TTR("Scale Mode (R)")); + hbc_menu->add_child(memnew(VSeparator)); + tool_button[TOOL_MODE_LIST_SELECT] = memnew(ToolButton); hbc_menu->add_child(tool_button[TOOL_MODE_LIST_SELECT]); tool_button[TOOL_MODE_LIST_SELECT]->set_toggle_mode(true); @@ -5335,8 +5424,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { 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).")); - VSeparator *vs = memnew(VSeparator); - hbc_menu->add_child(vs); + hbc_menu->add_child(memnew(VSeparator)); tool_option_button[TOOL_OPT_LOCAL_COORDS] = memnew(ToolButton); hbc_menu->add_child(tool_option_button[TOOL_OPT_LOCAL_COORDS]); @@ -5358,8 +5446,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { sct = ED_GET_SHORTCUT("spatial_editor/snap").ptr()->get_as_text(); tool_option_button[TOOL_OPT_USE_SNAP]->set_tooltip(vformat(TTR("Snap Mode (%s)"), sct)); - vs = memnew(VSeparator); - hbc_menu->add_child(vs); + hbc_menu->add_child(memnew(VSeparator)); // Drag and drop support; preview_node = memnew(Spatial); @@ -5381,7 +5468,6 @@ 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); @@ -5392,7 +5478,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/snap_to_floor", TTR("Snap object to floor"), KEY_PAGEDOWN), 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); @@ -5401,7 +5487,6 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { view_menu = memnew(MenuButton); view_menu->set_text(TTR("View")); - view_menu->set_position(Point2(212, 0)); hbc_menu->add_child(view_menu); p = view_menu->get_popup(); @@ -5593,7 +5678,6 @@ void SpatialEditorPlugin::make_visible(bool p_visible) { spatial_editor->show(); spatial_editor->set_process(true); - spatial_editor->grab_focus(); } else { @@ -5664,7 +5748,7 @@ 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_DEF("editors/3d_gizmos/gizmo_colors/instanced", Color(0.7, 0.7, 0.7, 0.5)); + Color instanced_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/instanced", Color(0.7, 0.7, 0.7, 0.6)); Vector<Ref<SpatialMaterial> > mats; @@ -5706,7 +5790,7 @@ void EditorSpatialGizmoPlugin::create_material(const String &p_name, const Color 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_DEF("editors/3d_gizmos/gizmo_colors/instanced", Color(0.7, 0.7, 0.7, 0.5)); + Color instanced_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/instanced", Color(0.7, 0.7, 0.7, 0.6)); Vector<Ref<SpatialMaterial> > icons; @@ -5719,7 +5803,7 @@ void EditorSpatialGizmoPlugin::create_icon_material(const String &p_name, const Color color = instanced ? instanced_color : p_albedo; if (!selected) { - color.a *= 0.3; + color.a *= 0.85; } icon->set_albedo(color); diff --git a/editor/plugins/spatial_editor_plugin.h b/editor/plugins/spatial_editor_plugin.h index 5850c0dbf1..3cce76cc17 100644 --- a/editor/plugins/spatial_editor_plugin.h +++ b/editor/plugins/spatial_editor_plugin.h @@ -86,7 +86,7 @@ public: Vector<Vector3> handles; Vector<Vector3> secondary_handles; - float selectable_icon_size = -1.0f; + float selectable_icon_size; bool billboard_handle; bool valid; @@ -127,9 +127,7 @@ public: 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); @@ -194,7 +192,7 @@ private: EditorSelection *editor_selection; UndoRedo *undo_redo; - Button *preview_camera; + CheckBox *preview_camera; ViewportContainer *viewport_container; MenuButton *view_menu; @@ -213,6 +211,7 @@ private: Label *info_label; Label *fps_label; Label *cinema_label; + Label *locked_label; struct _RayResult { @@ -226,7 +225,7 @@ private: void _compute_edit(const Point2 &p_point); void _clear_selected(); void _select_clicked(bool p_append, bool p_single); - void _select(Spatial *p_node, bool p_append, bool p_single); + void _select(Node *p_node, bool p_append, bool p_single); ObjectID _select_ray(const Point2 &p_pos, bool p_append, bool &r_includes_current, int *r_gizmo_handle = NULL, bool p_alt_select = false); void _find_items_at_pos(const Point2 &p_pos, bool &r_includes_current, Vector<_RayResult> &results, bool p_alt_select = false); Vector3 _get_ray_pos(const Vector2 &p_pos) const; @@ -406,6 +405,7 @@ public: AcceptDialog *p_accept); Viewport *get_viewport_node() { return viewport; } + Camera *get_camera() { return camera; } // return the default camera object. SpatialEditorViewport(SpatialEditor *p_spatial_editor, EditorNode *p_editor, int p_index); }; @@ -445,6 +445,9 @@ private: float ratio_h; float ratio_v; + bool hovering_v; + bool hovering_h; + bool dragging_v; bool dragging_h; Vector2 drag_begin_pos; @@ -512,7 +515,6 @@ private: RID grid[3]; RID grid_instance[3]; bool grid_visible[3]; //currently visible - float last_grid_snap; bool grid_enable[3]; //should be always visible if true bool grid_enabled; @@ -677,7 +679,7 @@ public: Ref<ArrayMesh> get_scale_plane_gizmo(int idx) const { return scale_plane_gizmo[idx]; } void update_transform_gizmo(); - void update_all_gizmos(); + void update_all_gizmos(Node *p_node = NULL); 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; } @@ -710,7 +712,6 @@ public: void register_gizmo_plugin(Ref<EditorSpatialGizmoPlugin> ref); - Camera *get_camera() { return NULL; } void edit(Spatial *p_spatial); void clear(); diff --git a/editor/plugins/sprite_editor_plugin.cpp b/editor/plugins/sprite_editor_plugin.cpp index 58a1835e68..c574b5e8ba 100644 --- a/editor/plugins/sprite_editor_plugin.cpp +++ b/editor/plugins/sprite_editor_plugin.cpp @@ -97,7 +97,7 @@ Vector<Vector2> expand(const Vector<Vector2> &points, const Rect2i &rect, float int lasti = p2->Contour.size() - 1; Vector2 prev = Vector2(p2->Contour[lasti].X / PRECISION, p2->Contour[lasti].Y / PRECISION); - for (int i = 0; i < p2->Contour.size(); i++) { + for (unsigned int i = 0; i < p2->Contour.size(); i++) { Vector2 cur = Vector2(p2->Contour[i].X / PRECISION, p2->Contour[i].Y / PRECISION); if (cur.distance_to(prev) > 0.5) { diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index fcbbee2b9c..40781908fd 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -30,9 +30,9 @@ #include "sprite_frames_editor_plugin.h" +#include "core/io/resource_loader.h" +#include "core/project_settings.h" #include "editor/editor_settings.h" -#include "io/resource_loader.h" -#include "project_settings.h" #include "scene/3d/sprite_3d.h" void SpriteFramesEditor::_gui_input(Ref<InputEvent> p_event) { @@ -548,7 +548,6 @@ void SpriteFramesEditor::edit(SpriteFrames *p_frames) { } else { hide(); - //set_physics_process(false); } } @@ -816,16 +815,26 @@ SpriteFramesEditor::SpriteFramesEditor() { void SpriteFramesEditorPlugin::edit(Object *p_object) { frames_editor->set_undo_redo(&get_undo_redo()); - SpriteFrames *s = Object::cast_to<SpriteFrames>(p_object); - if (!s) - return; + + SpriteFrames *s; + AnimatedSprite *animated_sprite = Object::cast_to<AnimatedSprite>(p_object); + if (animated_sprite) { + s = *animated_sprite->get_sprite_frames(); + } else { + s = Object::cast_to<SpriteFrames>(p_object); + } frames_editor->edit(s); } bool SpriteFramesEditorPlugin::handles(Object *p_object) const { - return p_object->is_class("SpriteFrames"); + AnimatedSprite *animated_sprite = Object::cast_to<AnimatedSprite>(p_object); + if (animated_sprite && *animated_sprite->get_sprite_frames()) { + return true; + } else { + return p_object->is_class("SpriteFrames"); + } } void SpriteFramesEditorPlugin::make_visible(bool p_visible) { @@ -833,14 +842,11 @@ void SpriteFramesEditorPlugin::make_visible(bool p_visible) { if (p_visible) { button->show(); editor->make_bottom_panel_item_visible(frames_editor); - //frames_editor->set_process(true); } else { button->hide(); if (frames_editor->is_visible_in_tree()) editor->hide_bottom_panel(); - - //frames_editor->set_process(false); } } diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp index 16c25f3074..4a8eae1ba4 100644 --- a/editor/plugins/text_editor.cpp +++ b/editor/plugins/text_editor.cpp @@ -30,6 +30,8 @@ #include "text_editor.h" +#include "editor_node.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()); @@ -64,6 +66,7 @@ void TextEditor::_change_syntax_highlighter(int p_idx) { el = el->next(); } set_syntax_highlighter(highlighters[highlighter_menu->get_item_text(p_idx)]); + EditorSettings::get_singleton()->set_project_metadata("text_editor", "syntax_highlighter", p_idx); } void TextEditor::_load_theme_settings() { @@ -128,7 +131,7 @@ void TextEditor::_load_theme_settings() { 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)); + text_edit->add_constant_override("line_spacing", EDITOR_DEF("text_editor/theme/line_spacing", 6)); colors_cache.font_color = text_color; colors_cache.symbol_color = symbol_color; @@ -158,10 +161,7 @@ String TextEditor::get_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>(); + return EditorNode::get_singleton()->get_object_icon(text_file.operator->(), ""); } RES TextEditor::get_edited_resource() const { @@ -299,7 +299,7 @@ void TextEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_READY: _load_theme_settings(); - set_syntax_highlighter(NULL); + _change_syntax_highlighter(EditorSettings::get_singleton()->get_project_metadata("text_editor", "syntax_highlighter", 0)); break; } } @@ -360,7 +360,7 @@ void TextEditor::_edit_option(int p_op) { } break; case EDIT_CLONE_DOWN: { - code_editor->code_lines_down(); + code_editor->clone_lines_down(); } break; case EDIT_TOGGLE_FOLD_LINE: { @@ -475,7 +475,7 @@ void TextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) { 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 + // Right click is outside the selected text tx->deselect(); } } diff --git a/editor/plugins/texture_editor_plugin.cpp b/editor/plugins/texture_editor_plugin.cpp index e891850870..140d37fdb5 100644 --- a/editor/plugins/texture_editor_plugin.cpp +++ b/editor/plugins/texture_editor_plugin.cpp @@ -30,9 +30,9 @@ #include "texture_editor_plugin.h" +#include "core/io/resource_loader.h" +#include "core/project_settings.h" #include "editor/editor_settings.h" -#include "io/resource_loader.h" -#include "project_settings.h" void TextureEditor::_gui_input(Ref<InputEvent> p_event) { } diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index 4a9cbfe535..33e1f7c6a3 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -31,8 +31,8 @@ #include "texture_region_editor_plugin.h" #include "core/core_string_names.h" -#include "os/input.h" -#include "os/keyboard.h" +#include "core/os/input.h" +#include "core/os/keyboard.h" #include "scene/gui/check_box.h" /** @@ -356,8 +356,6 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { undo_redo->add_do_method(atlas_tex.ptr(), "set_region", atlas_tex->get_region()); undo_redo->add_undo_method(atlas_tex.ptr(), "set_region", rect_prev); } else if (node_ninepatch) { - // FIXME: Is this intentional? - } else if (node_ninepatch) { undo_redo->add_do_method(node_ninepatch, "set_region_rect", node_ninepatch->get_region_rect()); undo_redo->add_undo_method(node_ninepatch, "set_region_rect", rect_prev); } else if (obj_styleBox.is_valid()) { @@ -521,6 +519,10 @@ void TextureRegionEditor::_set_snap_mode(int p_mode) { else hb_grid->hide(); + if (snap_mode == SNAP_AUTOSLICE && is_visible() && autoslice_is_dirty) { + _update_autoslice(); + } + edit_draw->update(); } @@ -562,7 +564,8 @@ void TextureRegionEditor::_zoom_in() { } void TextureRegionEditor::_zoom_reset() { - if (draw_zoom == 1) return; + if (draw_zoom == 1) + return; draw_zoom = 1; edit_draw->update(); } @@ -585,25 +588,91 @@ void TextureRegionEditor::apply_rect(const Rect2 &rect) { atlas_tex->set_region(rect); } -void TextureRegionEditor::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_PROCESS: { - if (node_sprite) { - if (node_sprite->is_region()) { +void TextureRegionEditor::_update_autoslice() { + autoslice_is_dirty = false; + autoslice_cache.clear(); - set_process(false); - EditorNode::get_singleton()->make_bottom_panel_item_visible(this); + Ref<Texture> texture = NULL; + if (node_sprite) + texture = node_sprite->get_texture(); + else if (node_ninepatch) + texture = node_ninepatch->get_texture(); + else if (obj_styleBox.is_valid()) + texture = obj_styleBox->get_texture(); + else if (atlas_tex.is_valid()) + texture = atlas_tex->get_atlas(); + + if (texture.is_null()) { + return; + } + + for (int y = 0; y < texture->get_height(); y++) { + for (int x = 0; x < texture->get_width(); x++) { + if (texture->is_pixel_opaque(x, y)) { + bool found = false; + for (List<Rect2>::Element *E = autoslice_cache.front(); E; E = E->next()) { + Rect2 grown = E->get().grow(1.5); + if (grown.has_point(Point2(x, y))) { + E->get().expand_to(Point2(x, y)); + E->get().expand_to(Point2(x + 1, y + 1)); + x = E->get().position.x + E->get().size.x - 1; + bool merged = true; + while (merged) { + merged = false; + bool queue_erase = false; + for (List<Rect2>::Element *F = autoslice_cache.front(); F; F = F->next()) { + if (queue_erase) { + autoslice_cache.erase(F->prev()); + queue_erase = false; + } + if (F == E) + continue; + if (E->get().grow(1).intersects(F->get())) { + E->get().expand_to(F->get().position); + E->get().expand_to(F->get().position + F->get().size); + if (F->prev()) { + F = F->prev(); + autoslice_cache.erase(F->next()); + } else { + queue_erase = true; + // Can't delete the first rect in the list. + } + merged = true; + } + } + } + found = true; + break; + } + } + if (!found) { + Rect2 new_rect(x, y, 1, 1); + autoslice_cache.push_back(new_rect); } - } else { - set_process(false); } - } break; - case NOTIFICATION_THEME_CHANGED: + } + } + cache_map[texture->get_rid()] = autoslice_cache; +} + +void TextureRegionEditor::_notification(int p_what) { + switch (p_what) { case NOTIFICATION_READY: { zoom_out->set_icon(get_icon("ZoomLess", "EditorIcons")); zoom_reset->set_icon(get_icon("ZoomReset", "EditorIcons")); zoom_in->set_icon(get_icon("ZoomMore", "EditorIcons")); } break; + case NOTIFICATION_VISIBILITY_CHANGED: { + if (snap_mode == SNAP_AUTOSLICE && is_visible() && autoslice_is_dirty) { + _update_autoslice(); + } + } break; + case MainLoop::NOTIFICATION_WM_FOCUS_IN: { + // This happens when the user leaves the Editor and returns, + // he/she could have changed the textures, so the cache is cleared + cache_map.clear(); + _edit_region(); + } break; } } @@ -709,57 +778,15 @@ void TextureRegionEditor::_edit_region() { return; } - autoslice_cache.clear(); - Ref<Image> i; - i.instance(); - if (i->load(texture->get_path()) == OK) { - BitMap bm; - bm.create_from_image_alpha(i); - for (int y = 0; y < i->get_height(); y++) { - for (int x = 0; x < i->get_width(); x++) { - if (bm.get_bit(Point2(x, y))) { - bool found = false; - for (List<Rect2>::Element *E = autoslice_cache.front(); E; E = E->next()) { - Rect2 grown = E->get().grow(1.5); - if (grown.has_point(Point2(x, y))) { - E->get().expand_to(Point2(x, y)); - E->get().expand_to(Point2(x + 1, y + 1)); - x = E->get().position.x + E->get().size.x - 1; - bool merged = true; - while (merged) { - merged = false; - bool queue_erase = false; - for (List<Rect2>::Element *F = autoslice_cache.front(); F; F = F->next()) { - if (queue_erase) { - autoslice_cache.erase(F->prev()); - queue_erase = false; - } - if (F == E) - continue; - if (E->get().grow(1).intersects(F->get())) { - E->get().expand_to(F->get().position); - E->get().expand_to(F->get().position + F->get().size); - if (F->prev()) { - F = F->prev(); - autoslice_cache.erase(F->next()); - } else { - queue_erase = true; - //Can't delete the first rect in the list. - } - merged = true; - } - } - } - found = true; - break; - } - } - if (!found) { - Rect2 new_rect(x, y, 1, 1); - autoslice_cache.push_back(new_rect); - } - } - } + if (cache_map.has(texture->get_rid())) { + autoslice_cache = cache_map[texture->get_rid()]; + autoslice_is_dirty = false; + return; + } else { + if (is_visible() && snap_mode == SNAP_AUTOSLICE) { + _update_autoslice(); + } else { + autoslice_is_dirty = true; } } diff --git a/editor/plugins/texture_region_editor_plugin.h b/editor/plugins/texture_region_editor_plugin.h index 670cc86bbb..61ef769f89 100644 --- a/editor/plugins/texture_region_editor_plugin.h +++ b/editor/plugins/texture_region_editor_plugin.h @@ -92,7 +92,9 @@ class TextureRegionEditor : public Control { Rect2 rect_prev; float prev_margin; int edited_margin; + Map<RID, List<Rect2> > cache_map; List<Rect2> autoslice_cache; + bool autoslice_is_dirty; bool drag; bool creating; @@ -110,6 +112,7 @@ class TextureRegionEditor : public Control { void _zoom_reset(); void _zoom_out(); void apply_rect(const Rect2 &rect); + void _update_autoslice(); protected: void _notification(int p_what); diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index 92b95963f9..acee1a6942 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -30,8 +30,8 @@ #include "theme_editor_plugin.h" -#include "os/file_access.h" -#include "version.h" +#include "core/os/file_access.h" +#include "core/version.h" void ThemeEditor::edit(const Ref<Theme> &p_theme) { diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp index 34dd36692c..b5e2a8675f 100644 --- a/editor/plugins/tile_map_editor_plugin.cpp +++ b/editor/plugins/tile_map_editor_plugin.cpp @@ -31,10 +31,11 @@ #include "tile_map_editor_plugin.h" #include "canvas_item_editor_plugin.h" +#include "core/math/math_funcs.h" +#include "core/os/input.h" +#include "core/os/keyboard.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" -#include "os/input.h" -#include "os/keyboard.h" #include "scene/gui/split_container.h" void TileMapEditor::_notification(int p_what) { @@ -43,8 +44,8 @@ void TileMapEditor::_notification(int p_what) { case NOTIFICATION_PROCESS: { - if (bucket_queue.size() && canvas_item_editor) { - canvas_item_editor->update(); + if (bucket_queue.size()) { + CanvasItemEditor::get_singleton()->update_viewport(); } } break; @@ -65,13 +66,11 @@ void TileMapEditor::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { - transp->set_icon(get_icon("Transpose", "EditorIcons")); - mirror_x->set_icon(get_icon("MirrorX", "EditorIcons")); - mirror_y->set_icon(get_icon("MirrorY", "EditorIcons")); - rotate_0->set_icon(get_icon("Rotate0", "EditorIcons")); - rotate_90->set_icon(get_icon("Rotate90", "EditorIcons")); - rotate_180->set_icon(get_icon("Rotate180", "EditorIcons")); - rotate_270->set_icon(get_icon("Rotate270", "EditorIcons")); + rotate_left_button->set_icon(get_icon("Rotate270", "EditorIcons")); + rotate_right_button->set_icon(get_icon("Rotate90", "EditorIcons")); + flip_horizontal_button->set_icon(get_icon("MirrorX", "EditorIcons")); + flip_vertical_button->set_icon(get_icon("MirrorY", "EditorIcons")); + clear_transform_button->set_icon(get_icon("Clear", "EditorIcons")); search_box->set_right_icon(get_icon("Search", "EditorIcons")); search_box->set_clear_button_enabled(true); @@ -97,27 +96,27 @@ void TileMapEditor::_menu_option(int p_option) { // immediately without pressing the left mouse button first tool = TOOL_NONE; - canvas_item_editor->update(); + CanvasItemEditor::get_singleton()->update_viewport(); } break; case OPTION_BUCKET: { tool = TOOL_BUCKET; - canvas_item_editor->update(); + CanvasItemEditor::get_singleton()->update_viewport(); } break; case OPTION_PICK_TILE: { tool = TOOL_PICKING; - canvas_item_editor->update(); + CanvasItemEditor::get_singleton()->update_viewport(); } break; case OPTION_SELECT: { tool = TOOL_SELECTING; selection_active = false; - canvas_item_editor->update(); + CanvasItemEditor::get_singleton()->update_viewport(); } break; case OPTION_COPY: { @@ -126,7 +125,7 @@ void TileMapEditor::_menu_option(int p_option) { if (selection_active) { tool = TOOL_PASTING; - canvas_item_editor->update(); + CanvasItemEditor::get_singleton()->update_viewport(); } } break; case OPTION_ERASE_SELECTION: { @@ -141,7 +140,7 @@ void TileMapEditor::_menu_option(int p_option) { selection_active = false; copydata.clear(); - canvas_item_editor->update(); + CanvasItemEditor::get_singleton()->update_viewport(); } break; case OPTION_FIX_INVALID: { @@ -165,7 +164,7 @@ void TileMapEditor::_menu_option(int p_option) { tool = TOOL_PASTING; - canvas_item_editor->update(); + CanvasItemEditor::get_singleton()->update_viewport(); } } break; } @@ -182,13 +181,13 @@ void TileMapEditor::_palette_multi_selected(int index, bool selected) { void TileMapEditor::_canvas_mouse_enter() { mouse_over = true; - canvas_item_editor->update(); + CanvasItemEditor::get_singleton()->update_viewport(); } void TileMapEditor::_canvas_mouse_exit() { mouse_over = false; - canvas_item_editor->update(); + CanvasItemEditor::get_singleton()->update_viewport(); } Vector<int> TileMapEditor::get_selected_tiles() const { @@ -238,8 +237,8 @@ void TileMapEditor::_create_set_cell_undo(const Vector2 &p_vec, const CellOp &p_ cell_new["transpose"] = p_cell_new.tr; cell_new["auto_coord"] = p_cell_new.ac; - undo_redo->add_undo_method(node, "set_celld", p_vec, cell_old); - undo_redo->add_do_method(node, "set_celld", p_vec, cell_new); + undo_redo->add_undo_method(node, "_set_celld", p_vec, cell_old); + undo_redo->add_do_method(node, "_set_celld", p_vec, cell_new); } void TileMapEditor::_start_undo(const String &p_action) { @@ -318,7 +317,7 @@ void TileMapEditor::_manual_toggled(bool p_enabled) { void TileMapEditor::_text_entered(const String &p_text) { - canvas_item_editor->grab_focus(); + canvas_item_editor_viewport->grab_focus(); } void TileMapEditor::_text_changed(const String &p_text) { @@ -357,6 +356,10 @@ void TileMapEditor::_update_palette() { if (!node) return; + // Update the clear button + clear_transform_button->set_disabled(!flip_h && !flip_v && !transpose); + + // Update the palette Vector<int> selected = get_selected_tiles(); palette->clear(); manual_palette->clear(); @@ -429,9 +432,6 @@ void TileMapEditor::_update_palette() { Ref<Texture> tex = tileset->tile_get_texture(entries[i].id); if (tex.is_valid()) { - Color color = tileset->tile_get_modulate(entries[i].id); - palette->set_item_icon_modulate(palette->get_item_count() - 1, color); - Rect2 region = tileset->tile_get_region(entries[i].id); if (tileset->tile_get_tile_mode(entries[i].id) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(entries[i].id) == TileSet::ATLAS_TILE) { @@ -440,10 +440,25 @@ void TileMapEditor::_update_palette() { region.position += (region.size + Vector2(spacing, spacing)) * tileset->autotile_get_icon_coordinate(entries[i].id); } - if (!region.has_no_area()) + // Transpose and flip + palette->set_item_icon_transposed(palette->get_item_count() - 1, transpose); + if (flip_h) { + region.size.x = -region.size.x; + } + if (flip_v) { + region.size.y = -region.size.y; + } + + // Set region + if (region.size != Size2()) palette->set_item_icon_region(palette->get_item_count() - 1, region); + // Set icon palette->set_item_icon(palette->get_item_count() - 1, tex); + + // Modulation + Color color = tileset->tile_get_modulate(entries[i].id); + palette->set_item_icon_modulate(palette->get_item_count() - 1, color); } palette->set_item_metadata(palette->get_item_count() - 1, entries[i].id); @@ -457,36 +472,38 @@ void TileMapEditor::_update_palette() { palette->select(0); } - if ((manual_autotile && tileset->tile_get_tile_mode(sel_tile) == TileSet::AUTO_TILE) || tileset->tile_get_tile_mode(sel_tile) == TileSet::ATLAS_TILE) { + if (sel_tile != TileMap::INVALID_CELL) { + 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); + const Map<Vector2, uint16_t> &tiles = tileset->autotile_get_bitmask_map(sel_tile); - Vector<Vector2> entries; - for (const Map<Vector2, uint16_t>::Element *E = tiles.front(); E; E = E->next()) { - entries.push_back(E->key()); - } - entries.sort(); + Vector<Vector2> entries; + for (const Map<Vector2, uint16_t>::Element *E = tiles.front(); E; E = E->next()) { + entries.push_back(E->key()); + } + entries.sort(); - Ref<Texture> tex = tileset->tile_get_texture(sel_tile); + Ref<Texture> tex = tileset->tile_get_texture(sel_tile); - for (int i = 0; i < entries.size(); i++) { + for (int i = 0; i < entries.size(); i++) { - manual_palette->add_item(String()); + manual_palette->add_item(String()); - if (tex.is_valid()) { + if (tex.is_valid()) { - Rect2 region = tileset->tile_get_region(sel_tile); - int spacing = tileset->autotile_get_spacing(sel_tile); - region.size = tileset->autotile_get_size(sel_tile); // !! - region.position += (region.size + Vector2(spacing, spacing)) * entries[i]; + Rect2 region = tileset->tile_get_region(sel_tile); + int spacing = tileset->autotile_get_spacing(sel_tile); + region.size = tileset->autotile_get_size(sel_tile); // !! + region.position += (region.size + Vector2(spacing, spacing)) * entries[i]; - if (!region.has_no_area()) - manual_palette->set_item_icon_region(manual_palette->get_item_count() - 1, region); + if (!region.has_no_area()) + manual_palette->set_item_icon_region(manual_palette->get_item_count() - 1, region); - manual_palette->set_item_icon(manual_palette->get_item_count() - 1, tex); - } + manual_palette->set_item_icon(manual_palette->get_item_count() - 1, tex); + } - manual_palette->set_item_metadata(manual_palette->get_item_count() - 1, entries[i]); + manual_palette->set_item_metadata(manual_palette->get_item_count() - 1, entries[i]); + } } } @@ -517,12 +534,12 @@ void TileMapEditor::_pick_tile(const Point2 &p_pos) { selected.push_back(id); set_selected_tiles(selected); - mirror_x->set_pressed(node->is_cell_x_flipped(p_pos.x, p_pos.y)); - mirror_y->set_pressed(node->is_cell_y_flipped(p_pos.x, p_pos.y)); - transp->set_pressed(node->is_cell_transposed(p_pos.x, p_pos.y)); + flip_h = node->is_cell_x_flipped(p_pos.x, p_pos.y); + flip_v = node->is_cell_y_flipped(p_pos.x, p_pos.y); + transpose = node->is_cell_transposed(p_pos.x, p_pos.y); - _update_transform_buttons(); - canvas_item_editor->update(); + _update_palette(); + CanvasItemEditor::get_singleton()->update_viewport(); } PoolVector<Vector2> TileMapEditor::_bucket_fill(const Point2i &p_start, bool erase, bool preview) { @@ -669,7 +686,7 @@ void TileMapEditor::_select(const Point2i &p_from, const Point2i &p_to) { rectangle.position = begin; rectangle.size = end - begin; - canvas_item_editor->update(); + CanvasItemEditor::get_singleton()->update_viewport(); } void TileMapEditor::_erase_selection() { @@ -684,7 +701,7 @@ void TileMapEditor::_erase_selection() { } } -void TileMapEditor::_draw_cell(int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Transform2D &p_xform) { +void TileMapEditor::_draw_cell(Control *p_viewport, int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Transform2D &p_xform) { Ref<Texture> t = node->get_tileset()->tile_get_texture(p_cell); @@ -781,19 +798,19 @@ void TileMapEditor::_draw_cell(int p_cell, const Point2i &p_point, bool p_flip_h modulate.a = 0.5; if (r.has_no_area()) - canvas_item_editor->draw_texture_rect(t, rect, false, modulate, p_transpose); + p_viewport->draw_texture_rect(t, rect, false, modulate, p_transpose); else - canvas_item_editor->draw_texture_rect_region(t, rect, r, modulate, p_transpose); + p_viewport->draw_texture_rect_region(t, rect, r, modulate, p_transpose); } -void TileMapEditor::_draw_fill_preview(int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Transform2D &p_xform) { +void TileMapEditor::_draw_fill_preview(Control *p_viewport, int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Transform2D &p_xform) { PoolVector<Vector2> points = _bucket_fill(p_point, false, true); PoolVector<Vector2>::Read pr = points.read(); int len = points.size(); for (int i = 0; i < len; ++i) { - _draw_cell(p_cell, pr[i], p_flip_h, p_flip_v, p_transpose, p_xform); + _draw_cell(p_viewport, p_cell, pr[i], p_flip_h, p_flip_v, p_transpose, p_xform); } } @@ -976,7 +993,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { paint_undo.clear(); - canvas_item_editor->update(); + CanvasItemEditor::get_singleton()->update_viewport(); } } else if (tool == TOOL_RECTANGLE_PAINT) { @@ -993,7 +1010,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { } _finish_undo(); - canvas_item_editor->update(); + CanvasItemEditor::get_singleton()->update_viewport(); } } else if (tool == TOOL_PASTING) { @@ -1009,12 +1026,12 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { } _finish_undo(); - canvas_item_editor->update(); + CanvasItemEditor::get_singleton()->update_viewport(); return true; // We want to keep the Pasting tool } else if (tool == TOOL_SELECTING) { - canvas_item_editor->update(); + CanvasItemEditor::get_singleton()->update_viewport(); } else if (tool == TOOL_BUCKET) { @@ -1053,7 +1070,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { tool = TOOL_NONE; selection_active = false; - canvas_item_editor->update(); + CanvasItemEditor::get_singleton()->update_viewport(); return true; } @@ -1063,7 +1080,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { tool = TOOL_NONE; copydata.clear(); - canvas_item_editor->update(); + CanvasItemEditor::get_singleton()->update_viewport(); return true; } @@ -1104,7 +1121,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { _finish_undo(); if (tool == TOOL_RECTANGLE_ERASE || tool == TOOL_LINE_ERASE) { - canvas_item_editor->update(); + CanvasItemEditor::get_singleton()->update_viewport(); } tool = TOOL_NONE; @@ -1147,7 +1164,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { if (new_over_tile != over_tile) { over_tile = new_over_tile; - canvas_item_editor->update(); + CanvasItemEditor::get_singleton()->update_viewport(); } if (show_tile_info) { @@ -1233,7 +1250,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { _set_cell(points[i], invalid_cell); } - canvas_item_editor->update(); + CanvasItemEditor::get_singleton()->update_viewport(); } return true; @@ -1292,7 +1309,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { tool = TOOL_NONE; - canvas_item_editor->update(); + CanvasItemEditor::get_singleton()->update_viewport(); return true; } @@ -1306,13 +1323,13 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { // NOTE: We do not set tool = TOOL_PAINTING as this begins painting // immediately without pressing the left mouse button first tool = TOOL_NONE; - canvas_item_editor->update(); + CanvasItemEditor::get_singleton()->update_viewport(); return true; } if (ED_IS_SHORTCUT("tile_map_editor/bucket_fill", p_event)) { tool = TOOL_BUCKET; - canvas_item_editor->update(); + CanvasItemEditor::get_singleton()->update_viewport(); return true; } @@ -1325,7 +1342,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { tool = TOOL_SELECTING; selection_active = false; - canvas_item_editor->update(); + CanvasItemEditor::get_singleton()->update_viewport(); return true; } @@ -1335,7 +1352,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { if (selection_active) { tool = TOOL_PASTING; - canvas_item_editor->update(); + CanvasItemEditor::get_singleton()->update_viewport(); return true; } @@ -1352,7 +1369,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { tool = TOOL_PASTING; - canvas_item_editor->update(); + CanvasItemEditor::get_singleton()->update_viewport(); return true; } } @@ -1364,23 +1381,20 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { } if (ED_IS_SHORTCUT("tile_map_editor/mirror_x", p_event)) { flip_h = !flip_h; - mirror_x->set_pressed(flip_h); - _update_transform_buttons(); - canvas_item_editor->update(); + _update_palette(); + CanvasItemEditor::get_singleton()->update_viewport(); return true; } if (ED_IS_SHORTCUT("tile_map_editor/mirror_y", p_event)) { flip_v = !flip_v; - mirror_y->set_pressed(flip_v); - _update_transform_buttons(); - canvas_item_editor->update(); + _update_palette(); + CanvasItemEditor::get_singleton()->update_viewport(); return true; } if (ED_IS_SHORTCUT("tile_map_editor/transpose", p_event)) { transpose = !transpose; - transp->set_pressed(transpose); - _update_transform_buttons(); - canvas_item_editor->update(); + _update_palette(); + CanvasItemEditor::get_singleton()->update_viewport(); return true; } } @@ -1388,17 +1402,16 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { return false; } -void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) { +void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { if (!node) return; Transform2D cell_xf = node->get_cell_transform(); - Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * node->get_global_transform(); Transform2D xform_inv = xform.affine_inverse(); - Size2 screen_size = canvas_item_editor->get_size(); + Size2 screen_size = p_overlay->get_size(); { Rect2 aabb; aabb.position = node->world_to_map(xform_inv.xform(Vector2())); @@ -1417,7 +1430,7 @@ void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) { Vector2 to = xform.xform(node->map_to_world(Vector2(i, si.position.y + si.size.y + 1))); Color col = i == 0 ? Color(1, 0.8, 0.2, 0.5) : Color(1, 0.3, 0.1, 0.2); - canvas_item_editor->draw_line(from, to, col, 1); + p_overlay->draw_line(from, to, col, 1); if (max_lines-- == 0) break; } @@ -1437,7 +1450,7 @@ void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) { Vector2 from = xform.xform(node->map_to_world(Vector2(i, j), true) + ofs); Vector2 to = xform.xform(node->map_to_world(Vector2(i, j + 1), true) + ofs); Color col = i == 0 ? Color(1, 0.8, 0.2, 0.5) : Color(1, 0.3, 0.1, 0.2); - canvas_item_editor->draw_line(from, to, col, 1); + p_overlay->draw_line(from, to, col, 1); if (max_lines-- == 0) break; @@ -1455,7 +1468,7 @@ void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) { Vector2 to = xform.xform(node->map_to_world(Vector2(si.position.x + si.size.x + 1, i))); Color col = i == 0 ? Color(1, 0.8, 0.2, 0.5) : Color(1, 0.3, 0.1, 0.2); - canvas_item_editor->draw_line(from, to, col, 1); + p_overlay->draw_line(from, to, col, 1); if (max_lines-- == 0) break; @@ -1474,7 +1487,7 @@ void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) { Vector2 from = xform.xform(node->map_to_world(Vector2(j, i), true) + ofs); Vector2 to = xform.xform(node->map_to_world(Vector2(j + 1, i), true) + ofs); Color col = i == 0 ? Color(1, 0.8, 0.2, 0.5) : Color(1, 0.3, 0.1, 0.2); - canvas_item_editor->draw_line(from, to, col, 1); + p_overlay->draw_line(from, to, col, 1); if (max_lines-- == 0) break; @@ -1491,7 +1504,7 @@ void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) { points.push_back(xform.xform(node->map_to_world((rectangle.position + Point2(rectangle.size.x + 1, rectangle.size.y + 1))))); points.push_back(xform.xform(node->map_to_world((rectangle.position + Point2(0, rectangle.size.y + 1))))); - canvas_item_editor->draw_colored_polygon(points, Color(0.2, 0.8, 1, 0.4)); + p_overlay->draw_colored_polygon(points, Color(0.2, 0.8, 1, 0.4)); } if (mouse_over) { @@ -1517,7 +1530,7 @@ void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) { col = Color(1.0, 0.4, 0.2, 0.8); for (int i = 0; i < 4; i++) - canvas_item_editor->draw_line(endpoints[i], endpoints[(i + 1) % 4], col, 2); + p_overlay->draw_line(endpoints[i], endpoints[(i + 1) % 4], col, 2); bool bucket_preview = EditorSettings::get_singleton()->get("editors/tile_map/bucket_fill_preview"); if (tool == TOOL_SELECTING || tool == TOOL_PICKING || !bucket_preview) { @@ -1536,7 +1549,7 @@ void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) { for (Map<Point2i, CellOp>::Element *E = paint_undo.front(); E; E = E->next()) { - _draw_cell(ids[0], E->key(), flip_h, flip_v, transpose, xform); + _draw_cell(p_overlay, ids[0], E->key(), flip_h, flip_v, transpose, xform); } } else if (tool == TOOL_RECTANGLE_PAINT) { @@ -1549,7 +1562,7 @@ void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) { for (int i = rectangle.position.y; i <= rectangle.position.y + rectangle.size.y; i++) { for (int j = rectangle.position.x; j <= rectangle.position.x + rectangle.size.x; j++) { - _draw_cell(ids[0], Point2i(j, i), flip_h, flip_v, transpose, xform); + _draw_cell(p_overlay, ids[0], Point2i(j, i), flip_h, flip_v, transpose, xform); } } } else if (tool == TOOL_PASTING) { @@ -1571,7 +1584,7 @@ void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) { TileData tcd = E->get(); - _draw_cell(tcd.cell, tcd.pos + ofs, tcd.flip_h, tcd.flip_v, tcd.transpose, xform); + _draw_cell(p_overlay, tcd.cell, tcd.pos + ofs, tcd.flip_h, tcd.flip_v, tcd.transpose, xform); } Rect2i duplicate = rectangle; @@ -1583,12 +1596,12 @@ void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) { points.push_back(xform.xform(node->map_to_world((duplicate.position + Point2(duplicate.size.x + 1, duplicate.size.y + 1))))); points.push_back(xform.xform(node->map_to_world((duplicate.position + Point2(0, duplicate.size.y + 1))))); - canvas_item_editor->draw_colored_polygon(points, Color(0.2, 1.0, 0.8, 0.2)); + p_overlay->draw_colored_polygon(points, Color(0.2, 1.0, 0.8, 0.2)); } else if (tool == TOOL_BUCKET) { Vector<int> tiles = get_selected_tiles(); - _draw_fill_preview(tiles[0], over_tile, flip_h, flip_v, transpose, xform); + _draw_fill_preview(p_overlay, tiles[0], over_tile, flip_h, flip_v, transpose, xform); } else { @@ -1597,7 +1610,7 @@ void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) { if (st.size() == 1 && st[0] == TileMap::INVALID_CELL) return; - _draw_cell(st[0], over_tile, flip_h, flip_v, transpose, xform); + _draw_cell(p_overlay, st[0], over_tile, flip_h, flip_v, transpose, xform); } } } @@ -1606,8 +1619,8 @@ void TileMapEditor::edit(Node *p_tile_map) { search_box->set_text(""); - if (!canvas_item_editor) { - canvas_item_editor = CanvasItemEditor::get_singleton()->get_viewport_control(); + if (!canvas_item_editor_viewport) { + canvas_item_editor_viewport = CanvasItemEditor::get_singleton()->get_viewport_control(); } if (node) @@ -1615,20 +1628,20 @@ void TileMapEditor::edit(Node *p_tile_map) { if (p_tile_map) { node = Object::cast_to<TileMap>(p_tile_map); - if (!canvas_item_editor->is_connected("mouse_entered", this, "_canvas_mouse_enter")) - canvas_item_editor->connect("mouse_entered", this, "_canvas_mouse_enter"); - if (!canvas_item_editor->is_connected("mouse_exited", this, "_canvas_mouse_exit")) - canvas_item_editor->connect("mouse_exited", this, "_canvas_mouse_exit"); + if (!canvas_item_editor_viewport->is_connected("mouse_entered", this, "_canvas_mouse_enter")) + canvas_item_editor_viewport->connect("mouse_entered", this, "_canvas_mouse_enter"); + if (!canvas_item_editor_viewport->is_connected("mouse_exited", this, "_canvas_mouse_exit")) + canvas_item_editor_viewport->connect("mouse_exited", this, "_canvas_mouse_exit"); _update_palette(); } else { node = NULL; - if (canvas_item_editor->is_connected("mouse_entered", this, "_canvas_mouse_enter")) - canvas_item_editor->disconnect("mouse_entered", this, "_canvas_mouse_enter"); - if (canvas_item_editor->is_connected("mouse_exited", this, "_canvas_mouse_exit")) - canvas_item_editor->disconnect("mouse_exited", this, "_canvas_mouse_exit"); + if (canvas_item_editor_viewport->is_connected("mouse_entered", this, "_canvas_mouse_enter")) + canvas_item_editor_viewport->disconnect("mouse_entered", this, "_canvas_mouse_enter"); + if (canvas_item_editor_viewport->is_connected("mouse_exited", this, "_canvas_mouse_exit")) + canvas_item_editor_viewport->disconnect("mouse_exited", this, "_canvas_mouse_exit"); _update_palette(); } @@ -1642,9 +1655,7 @@ void TileMapEditor::edit(Node *p_tile_map) { void TileMapEditor::_tileset_settings_changed() { _update_palette(); - - if (canvas_item_editor) - canvas_item_editor->update(); + CanvasItemEditor::get_singleton()->update_viewport(); } void TileMapEditor::_icon_size_changed(float p_value) { @@ -1665,7 +1676,10 @@ void TileMapEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_canvas_mouse_enter"), &TileMapEditor::_canvas_mouse_enter); ClassDB::bind_method(D_METHOD("_canvas_mouse_exit"), &TileMapEditor::_canvas_mouse_exit); 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("_rotate"), &TileMapEditor::_rotate); + ClassDB::bind_method(D_METHOD("_flip_horizontal"), &TileMapEditor::_flip_horizontal); + ClassDB::bind_method(D_METHOD("_flip_vertical"), &TileMapEditor::_flip_vertical); + ClassDB::bind_method(D_METHOD("_clear_transform"), &TileMapEditor::_clear_transform); ClassDB::bind_method(D_METHOD("_palette_selected"), &TileMapEditor::_palette_selected); ClassDB::bind_method(D_METHOD("_palette_multi_selected"), &TileMapEditor::_palette_multi_selected); @@ -1690,37 +1704,66 @@ TileMapEditor::CellOp TileMapEditor::_get_op_from_cell(const Point2i &p_pos) { return op; } -void TileMapEditor::_update_transform_buttons(Object *p_button) { - //ERR_FAIL_NULL(p_button); - ToolButton *b = Object::cast_to<ToolButton>(p_button); - //ERR_FAIL_COND(!b); - - if (b == rotate_0) { - mirror_x->set_pressed(false); - mirror_y->set_pressed(false); - transp->set_pressed(false); - } else if (b == rotate_90) { - mirror_x->set_pressed(true); - mirror_y->set_pressed(false); - transp->set_pressed(true); - } else if (b == rotate_180) { - mirror_x->set_pressed(true); - mirror_y->set_pressed(true); - transp->set_pressed(false); - } else if (b == rotate_270) { - mirror_x->set_pressed(false); - mirror_y->set_pressed(true); - transp->set_pressed(true); +void TileMapEditor::_rotate(int steps) { + const bool normal_rotation_matrix[][3] = { + { false, false, false }, + { true, true, false }, + { false, true, true }, + { true, false, true } + }; + + const bool mirrored_rotation_matrix[][3] = { + { false, true, false }, + { true, true, true }, + { false, false, true }, + { true, false, false } + }; + + if (transpose ^ flip_h ^ flip_v) { + // Odd number of flags activated = mirrored rotation + for (int i = 0; i < 4; i++) { + if (transpose == mirrored_rotation_matrix[i][0] && + flip_h == mirrored_rotation_matrix[i][1] && + flip_v == mirrored_rotation_matrix[i][2]) { + int new_id = Math::wrapi(i + steps, 0, 4); + transpose = mirrored_rotation_matrix[new_id][0]; + flip_h = mirrored_rotation_matrix[new_id][1]; + flip_v = mirrored_rotation_matrix[new_id][2]; + } + } + } else { + // Even number of flags activated = normal rotation + for (int i = 0; i < 4; i++) { + if (transpose == normal_rotation_matrix[i][0] && + flip_h == normal_rotation_matrix[i][1] && + flip_v == normal_rotation_matrix[i][2]) { + int new_id = Math::wrapi(i + steps, 0, 4); + transpose = normal_rotation_matrix[new_id][0]; + flip_h = normal_rotation_matrix[new_id][1]; + flip_v = normal_rotation_matrix[new_id][2]; + break; + } + } } - flip_h = mirror_x->is_pressed(); - flip_v = mirror_y->is_pressed(); - transpose = transp->is_pressed(); + _update_palette(); +} - rotate_0->set_pressed(!flip_h && !flip_v && !transpose); - rotate_90->set_pressed(flip_h && !flip_v && transpose); - rotate_180->set_pressed(flip_h && flip_v && !transpose); - rotate_270->set_pressed(!flip_h && flip_v && transpose); +void TileMapEditor::_flip_horizontal() { + flip_h = !flip_h; + _update_palette(); +} + +void TileMapEditor::_flip_vertical() { + flip_v = !flip_v; + _update_palette(); +} + +void TileMapEditor::_clear_transform() { + transpose = false; + flip_h = false; + flip_v = false; + _update_palette(); } TileMapEditor::TileMapEditor(EditorNode *p_editor) { @@ -1728,7 +1771,7 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) { node = NULL; manual_autotile = false; manual_position = Vector2(0, 0); - canvas_item_editor = NULL; + canvas_item_editor_viewport = NULL; editor = p_editor; undo_redo = editor->get_undo_redo(); @@ -1753,10 +1796,8 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) { ED_SHORTCUT("tile_map_editor/mirror_x", TTR("Mirror X"), KEY_A); ED_SHORTCUT("tile_map_editor/mirror_y", TTR("Mirror Y"), KEY_S); - HBoxContainer *tool_hb1 = memnew(HBoxContainer); - add_child(tool_hb1); - HBoxContainer *tool_hb2 = memnew(HBoxContainer); - add_child(tool_hb2); + HBoxContainer *tool_hb = memnew(HBoxContainer); + add_child(tool_hb); manual_button = memnew(CheckBox); manual_button->set_text("Disable Autotile"); @@ -1841,52 +1882,37 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) { p->connect("id_pressed", this, "_menu_option"); toolbar->add_child(options); - - transp = memnew(ToolButton); - transp->set_toggle_mode(true); - transp->set_tooltip(TTR("Transpose") + " (" + ED_GET_SHORTCUT("tile_map_editor/transpose")->get_as_text() + ")"); - transp->set_focus_mode(FOCUS_NONE); - transp->connect("pressed", this, "_update_transform_buttons", make_binds(transp)); - tool_hb1->add_child(transp); - mirror_x = memnew(ToolButton); - mirror_x->set_toggle_mode(true); - mirror_x->set_tooltip(TTR("Mirror X") + " (" + ED_GET_SHORTCUT("tile_map_editor/mirror_x")->get_as_text() + ")"); - mirror_x->set_focus_mode(FOCUS_NONE); - mirror_x->connect("pressed", this, "_update_transform_buttons", make_binds(mirror_x)); - tool_hb1->add_child(mirror_x); - mirror_y = memnew(ToolButton); - mirror_y->set_toggle_mode(true); - mirror_y->set_tooltip(TTR("Mirror Y") + " (" + ED_GET_SHORTCUT("tile_map_editor/mirror_y")->get_as_text() + ")"); - mirror_y->set_focus_mode(FOCUS_NONE); - mirror_y->connect("pressed", this, "_update_transform_buttons", make_binds(mirror_y)); - tool_hb1->add_child(mirror_y); - - rotate_0 = memnew(ToolButton); - rotate_0->set_toggle_mode(true); - rotate_0->set_tooltip(TTR("Rotate 0 degrees")); - rotate_0->set_focus_mode(FOCUS_NONE); - rotate_0->connect("pressed", this, "_update_transform_buttons", make_binds(rotate_0)); - tool_hb2->add_child(rotate_0); - rotate_90 = memnew(ToolButton); - rotate_90->set_toggle_mode(true); - rotate_90->set_tooltip(TTR("Rotate 90 degrees")); - rotate_90->set_focus_mode(FOCUS_NONE); - rotate_90->connect("pressed", this, "_update_transform_buttons", make_binds(rotate_90)); - tool_hb2->add_child(rotate_90); - rotate_180 = memnew(ToolButton); - rotate_180->set_toggle_mode(true); - rotate_180->set_tooltip(TTR("Rotate 180 degrees")); - rotate_180->set_focus_mode(FOCUS_NONE); - rotate_180->connect("pressed", this, "_update_transform_buttons", make_binds(rotate_180)); - tool_hb2->add_child(rotate_180); - rotate_270 = memnew(ToolButton); - rotate_270->set_toggle_mode(true); - rotate_270->set_tooltip(TTR("Rotate 270 degrees")); - rotate_270->set_focus_mode(FOCUS_NONE); - rotate_270->connect("pressed", this, "_update_transform_buttons", make_binds(rotate_270)); - tool_hb2->add_child(rotate_270); - - rotate_0->set_pressed(true); + rotate_left_button = memnew(ToolButton); + rotate_left_button->set_tooltip(TTR("Rotate left")); + rotate_left_button->set_focus_mode(FOCUS_NONE); + rotate_left_button->connect("pressed", this, "_rotate", varray(-1)); + tool_hb->add_child(rotate_left_button); + + rotate_right_button = memnew(ToolButton); + rotate_right_button->set_tooltip(TTR("Rotate right")); + rotate_right_button->set_focus_mode(FOCUS_NONE); + rotate_right_button->connect("pressed", this, "_rotate", varray(1)); + tool_hb->add_child(rotate_right_button); + + flip_horizontal_button = memnew(ToolButton); + flip_horizontal_button->set_tooltip(TTR("Flip horizontally")); + flip_horizontal_button->set_focus_mode(FOCUS_NONE); + flip_horizontal_button->connect("pressed", this, "_flip_horizontal"); + tool_hb->add_child(flip_horizontal_button); + + flip_vertical_button = memnew(ToolButton); + flip_vertical_button->set_tooltip(TTR("Flip vertically")); + flip_vertical_button->set_focus_mode(FOCUS_NONE); + flip_vertical_button->connect("pressed", this, "_flip_vertical"); + tool_hb->add_child(flip_vertical_button); + + clear_transform_button = memnew(ToolButton); + clear_transform_button->set_tooltip(TTR("Clear transform")); + clear_transform_button->set_focus_mode(FOCUS_NONE); + clear_transform_button->connect("pressed", this, "_clear_transform"); + tool_hb->add_child(clear_transform_button); + + clear_transform_button->set_disabled(true); } TileMapEditor::~TileMapEditor() { diff --git a/editor/plugins/tile_map_editor_plugin.h b/editor/plugins/tile_map_editor_plugin.h index c824824d56..68e5806ee5 100644 --- a/editor/plugins/tile_map_editor_plugin.h +++ b/editor/plugins/tile_map_editor_plugin.h @@ -82,7 +82,7 @@ class TileMapEditor : public VBoxContainer { EditorNode *editor; UndoRedo *undo_redo; - Control *canvas_item_editor; + Control *canvas_item_editor_viewport; LineEdit *search_box; HSlider *size_slider; @@ -93,13 +93,13 @@ class TileMapEditor : public VBoxContainer { Label *tile_info; MenuButton *options; - ToolButton *transp; - ToolButton *mirror_x; - ToolButton *mirror_y; - ToolButton *rotate_0; - ToolButton *rotate_90; - ToolButton *rotate_180; - ToolButton *rotate_270; + + ToolButton *flip_horizontal_button; + ToolButton *flip_vertical_button; + ToolButton *rotate_left_button; + ToolButton *rotate_right_button; + ToolButton *clear_transform_button; + CheckBox *manual_button; Tool tool; @@ -168,8 +168,8 @@ class TileMapEditor : public VBoxContainer { void _select(const Point2i &p_from, const Point2i &p_to); void _erase_selection(); - void _draw_cell(int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Transform2D &p_xform); - void _draw_fill_preview(int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Transform2D &p_xform); + void _draw_cell(Control *p_viewport, int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Transform2D &p_xform); + void _draw_fill_preview(Control *p_viewport, int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Transform2D &p_xform); void _clear_bucket_cache(); void _update_copydata(); @@ -196,17 +196,21 @@ class TileMapEditor : public VBoxContainer { void _tileset_settings_changed(); void _icon_size_changed(float p_value); + void _clear_transform(); + void _flip_horizontal(); + void _flip_vertical(); + void _rotate(int steps); + protected: void _notification(int p_what); static void _bind_methods(); CellOp _get_op_from_cell(const Point2i &p_pos); - void _update_transform_buttons(Object *p_button = NULL); public: HBoxContainer *get_toolbar() const { return toolbar; } bool forward_gui_input(const Ref<InputEvent> &p_event); - void forward_draw_over_viewport(Control *p_overlay); + void forward_canvas_draw_over_viewport(Control *p_overlay); void edit(Node *p_tile_map); @@ -225,7 +229,7 @@ protected: public: virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return tile_map_editor->forward_gui_input(p_event); } - virtual void forward_draw_over_viewport(Control *p_overlay) { tile_map_editor->forward_draw_over_viewport(p_overlay); } + virtual void forward_canvas_draw_over_viewport(Control *p_overlay) { tile_map_editor->forward_canvas_draw_over_viewport(p_overlay); } virtual String get_name() const { return "TileMap"; } bool has_main_screen() const { return false; } diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp index 55a04a51b3..01768c201e 100644 --- a/editor/plugins/tile_set_editor_plugin.cpp +++ b/editor/plugins/tile_set_editor_plugin.cpp @@ -483,6 +483,11 @@ TileSetEditor::TileSetEditor(EditorNode *p_editor) { //--------------- helper = memnew(TilesetEditorContext(this)); tile_names_opacity = 0; + + // config scale + max_scale = 10.0f; + min_scale = 0.1f; + scale_ratio = 1.2f; } TileSetEditor::~TileSetEditor() { @@ -498,7 +503,7 @@ void TileSetEditor::_on_tileset_toolbar_button_pressed(int p_index) { } 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->set_text(TTR("Remove selected texture and ALL TILES which use it?")); cd->popup_centered(Size2(300, 60)); } else { err_dialog->set_text(TTR("You haven't selected a texture to remove.")); @@ -569,6 +574,10 @@ 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])); + + ERR_EXPLAIN("'" + p_paths[i] + "' is not a valid texture."); + ERR_CONTINUE(!t.is_valid()); + if (texture_map.has(t->get_rid())) { invalid_count++; } else { @@ -577,11 +586,15 @@ void TileSetEditor::_on_textures_added(const PoolStringArray &p_paths) { 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 (texture_list->get_item_count() > 0) { + 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->set_text(vformat(TTR("%s file(s) were not added because was already on the list."), String::num(invalid_count, 0))); err_dialog->popup_centered(Size2(300, 60)); } } @@ -795,6 +808,7 @@ void TileSetEditor::_on_workspace_draw() { spin_priority->set_suffix(" / " + String::num(total, 0)); draw_highlight_subtile(edited_shape_coord, queue_others); } break; + default: {} } draw_tile_subdivision(get_current_tile(), Color(0.347214, 0.722656, 0.617063)); @@ -963,6 +977,15 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { } } } + + // Mouse Wheel Event + const int _mouse_button_index = mb->get_button_index(); + if (_mouse_button_index == BUTTON_WHEEL_UP && mb->get_control()) { + _zoom_in(); + + } else if (_mouse_button_index == BUTTON_WHEEL_DOWN && mb->get_control()) { + _zoom_out(); + } } // Drag Middle Mouse if (mm.is_valid()) { @@ -1365,6 +1388,7 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { } } } break; + default: {} } } } @@ -1434,26 +1458,15 @@ void TileSetEditor::_on_tool_clicked(int p_tool) { workspace->update(); } } break; + default: {} } } } else if (p_tool == ZOOM_OUT) { - float scale = workspace->get_scale().x; - if (scale > 0.1) { - scale /= 2; - workspace->set_scale(Vector2(scale, scale)); - workspace_container->set_custom_minimum_size(workspace->get_rect().size * scale); - workspace_overlay->set_custom_minimum_size(workspace->get_rect().size * scale); - } + _zoom_out(); } else if (p_tool == ZOOM_1) { - workspace->set_scale(Vector2(1, 1)); - workspace_container->set_custom_minimum_size(workspace->get_rect().size); - workspace_overlay->set_custom_minimum_size(workspace->get_rect().size); + _reset_zoom(); } 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(workspace->get_rect().size * scale); - workspace_overlay->set_custom_minimum_size(workspace->get_rect().size * scale); + _zoom_in(); } else if (p_tool == TOOL_SELECT) { if (creating_shape) { // Cancel Creation @@ -1492,6 +1505,31 @@ void TileSetEditor::_set_snap_sep(Vector2 p_val) { workspace->update(); } +void TileSetEditor::_zoom_in() { + float scale = workspace->get_scale().x; + if (scale < max_scale) { + scale *= scale_ratio; + workspace->set_scale(Vector2(scale, scale)); + workspace_container->set_custom_minimum_size(workspace->get_rect().size * scale); + workspace_overlay->set_custom_minimum_size(workspace->get_rect().size * scale); + } +} +void TileSetEditor::_zoom_out() { + + float scale = workspace->get_scale().x; + if (scale > min_scale) { + scale /= scale_ratio; + workspace->set_scale(Vector2(scale, scale)); + workspace_container->set_custom_minimum_size(workspace->get_rect().size * scale); + workspace_overlay->set_custom_minimum_size(workspace->get_rect().size * scale); + } +} +void TileSetEditor::_reset_zoom() { + workspace->set_scale(Vector2(1, 1)); + workspace_container->set_custom_minimum_size(workspace->get_rect().size); + workspace_overlay->set_custom_minimum_size(workspace->get_rect().size); +} + void TileSetEditor::draw_highlight_current_tile() { if (get_current_tile() >= 0) { @@ -1715,16 +1753,18 @@ void TileSetEditor::draw_polygon_shapes() { Vector<Vector2> polygon; Vector<Color> colors; + Vector2 anchor = WORKSPACE_MARGIN; + anchor += tileset->tile_get_region(get_current_tile()).position; for (int j = 0; j < shape->get_polygon().size(); j++) { - polygon.push_back(shape->get_polygon()[j]); + polygon.push_back(shape->get_polygon()[j] + anchor); colors.push_back(c_bg); } workspace->draw_polygon(polygon, colors); for (int j = 0; j < shape->get_polygon().size() - 1; j++) { - workspace->draw_line(shape->get_polygon()[j], shape->get_polygon()[j + 1], c_border, 1, true); + workspace->draw_line(shape->get_polygon()[j] + anchor, shape->get_polygon()[j + 1] + anchor, c_border, 1, true); } - workspace->draw_line(shape->get_polygon()[shape->get_polygon().size() - 1], shape->get_polygon()[0], c_border, 1, true); + workspace->draw_line(shape->get_polygon()[shape->get_polygon().size() - 1] + anchor, shape->get_polygon()[0] + anchor, c_border, 1, true); if (shape == edited_occlusion_shape) { draw_handles = true; } @@ -1788,10 +1828,11 @@ void TileSetEditor::draw_polygon_shapes() { Vector<Vector2> polygon; Vector<Color> colors; - + Vector2 anchor = WORKSPACE_MARGIN; + anchor += tileset->tile_get_region(get_current_tile()).position; PoolVector<Vector2> vertices = shape->get_vertices(); for (int j = 0; j < shape->get_polygon(0).size(); j++) { - polygon.push_back(vertices[shape->get_polygon(0)[j]]); + polygon.push_back(vertices[shape->get_polygon(0)[j]] + anchor); colors.push_back(c_bg); } workspace->draw_polygon(polygon, colors); @@ -1799,7 +1840,7 @@ void TileSetEditor::draw_polygon_shapes() { if (shape->get_polygon_count() > 0) { PoolVector<Vector2> vertices = shape->get_vertices(); for (int j = 0; j < shape->get_polygon(0).size() - 1; j++) { - workspace->draw_line(vertices[shape->get_polygon(0)[j]], vertices[shape->get_polygon(0)[j + 1]], c_border, 1, true); + workspace->draw_line(vertices[shape->get_polygon(0)[j]] + anchor, vertices[shape->get_polygon(0)[j + 1]] + anchor, c_border, 1, true); } if (shape == edited_navigation_shape) { draw_handles = true; @@ -1859,6 +1900,7 @@ void TileSetEditor::draw_polygon_shapes() { } } } break; + default: {} } if (creating_shape) { for (int j = 0; j < current_shape.size() - 1; j++) { @@ -1954,6 +1996,8 @@ void TileSetEditor::close_shape(const Vector2 &shape_anchor) { void TileSetEditor::select_coord(const Vector2 &coord) { current_shape = PoolVector2Array(); + if (get_current_tile() == -1) + return; 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) { @@ -2038,8 +2082,10 @@ Vector2 TileSetEditor::snap_point(const Vector2 &point) { 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) + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) { region.position = tileset->tile_get_region(get_current_tile()).position + WORKSPACE_MARGIN; + region.size = tileset->tile_get_region(get_current_tile()).size; + } if (tools[TOOL_GRID_SNAP]->is_pressed()) { p.x = Math::snap_scalar_seperation(snap_offset.x, snap_step.x, p.x, snap_separation.x); @@ -2065,13 +2111,24 @@ void TileSetEditor::update_texture_list() { List<int> ids; tileset->get_tile_list(&ids); + Vector<int> ids_to_remove; for (List<int>::Element *E = ids.front(); E; E = E->next()) { + // Clear tiles referencing gone textures (user has been already given the chance to fix broken deps) + if (!tileset->tile_get_texture(E->get()).is_valid()) { + ids_to_remove.push_back(E->get()); + ERR_CONTINUE(!tileset->tile_get_texture(E->get()).is_valid()); + } + 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()); } } + for (int i = 0; i < ids_to_remove.size(); i++) { + tileset->remove_tile(ids_to_remove[i]); + } + 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) @@ -2254,6 +2311,9 @@ bool TilesetEditorContext::_set(const StringName &p_name, const Variant &p_value tileset_editor->workspace_overlay->update(); } return v; + } else if (name == "tileset_script") { + tileset->set_script(p_value); + return true; } tileset_editor->err_dialog->set_text(TTR("This property can't be changed.")); @@ -2302,6 +2362,9 @@ bool TilesetEditorContext::_get(const StringName &p_name, Variant &r_ret) const } else if (name == "selected_occlusion") { r_ret = tileset_editor->edited_occlusion_shape; v = true; + } else if (name == "tileset_script") { + r_ret = tileset->get_script(); + v = true; } return v; } @@ -2346,6 +2409,9 @@ void TilesetEditorContext::_get_property_list(List<PropertyInfo> *p_list) const 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())); } + if (!tileset.is_null()) { + p_list->push_back(PropertyInfo(Variant::OBJECT, "tileset_script", PROPERTY_HINT_RESOURCE_TYPE, "Script")); + } } TilesetEditorContext::TilesetEditorContext(TileSetEditor *p_tileset_editor) { diff --git a/editor/plugins/tile_set_editor_plugin.h b/editor/plugins/tile_set_editor_plugin.h index 23bf68b90f..bd8a2ddb98 100644 --- a/editor/plugins/tile_set_editor_plugin.h +++ b/editor/plugins/tile_set_editor_plugin.h @@ -141,6 +141,10 @@ class TileSetEditor : public Control { EditMode edit_mode; int current_tile; + float max_scale; + float min_scale; + float scale_ratio; + void update_texture_list(); void update_texture_list_icon(); @@ -178,6 +182,10 @@ private: void _set_snap_off(Vector2 p_val); void _set_snap_sep(Vector2 p_val); + void _zoom_in(); + void _zoom_out(); + void _reset_zoom(); + 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; diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 63e89b78ea..39e50ec7f8 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -1,10 +1,40 @@ +/*************************************************************************/ +/* visual_shader_editor_plugin.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_shader_editor_plugin.h" #include "core/io/resource_loader.h" +#include "core/os/input.h" +#include "core/os/keyboard.h" #include "core/project_settings.h" #include "editor/editor_properties.h" -#include "os/input.h" -#include "os/keyboard.h" #include "scene/animation/animation_player.h" #include "scene/gui/menu_button.h" #include "scene/gui/panel.h" @@ -480,6 +510,17 @@ void VisualShaderEditor::_connection_request(const String &p_from, int p_from_in } undo_redo->create_action("Nodes Connected"); + + List<VisualShader::Connection> conns; + visual_shader->get_node_connections(type, &conns); + + for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) { + if (E->get().to_node == to && E->get().to_port == p_to_index) { + undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port); + undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes", type, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port); + } + } + undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes", type, from, p_from_index, to, p_to_index); undo_redo->add_undo_method(visual_shader.ptr(), "disconnect_nodes", type, from, p_from_index, to, p_to_index); undo_redo->add_do_method(this, "_update_graph"); @@ -599,7 +640,7 @@ void VisualShaderEditor::_duplicate_nodes() { int id = String(graph->get_child(i)->get_name()).to_int(); Ref<VisualShaderNode> node = visual_shader->get_node(type, id); Ref<VisualShaderNodeOutput> output = node; - if (output.is_valid()) //cant duplicate output + if (output.is_valid()) //can't duplicate output continue; if (node.is_valid()) { nodes.push_back(id); @@ -902,7 +943,10 @@ public: class VisualShaderNodePluginDefaultEditor : public VBoxContainer { GDCLASS(VisualShaderNodePluginDefaultEditor, VBoxContainer) public: - void _property_changed(const String &prop, const Variant &p_value) { + void _property_changed(const String &prop, const Variant &p_value, bool p_changing = false) { + + if (p_changing) + return; UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); @@ -949,7 +993,7 @@ public: } static void _bind_methods() { - ClassDB::bind_method("_property_changed", &VisualShaderNodePluginDefaultEditor::_property_changed); + ClassDB::bind_method("_property_changed", &VisualShaderNodePluginDefaultEditor::_property_changed, DEFVAL(false)); ClassDB::bind_method("_node_changed", &VisualShaderNodePluginDefaultEditor::_node_changed); ClassDB::bind_method("_refresh_request", &VisualShaderNodePluginDefaultEditor::_refresh_request); } @@ -1128,7 +1172,7 @@ bool EditorInspectorShaderModePlugin::parse_property(Object *p_object, Variant:: return true; } - return false; //can be overriden, although it will most likely be last anyway + return false; //can be overridden, although it will most likely be last anyway } void EditorInspectorShaderModePlugin::parse_end() { diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h index f86374ff6b..49a51ede8f 100644 --- a/editor/plugins/visual_shader_editor_plugin.h +++ b/editor/plugins/visual_shader_editor_plugin.h @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* visual_shader_editor_plugin.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 VISUAL_SHADER_EDITOR_PLUGIN_H #define VISUAL_SHADER_EDITOR_PLUGIN_H diff --git a/editor/progress_dialog.cpp b/editor/progress_dialog.cpp index f735ef97db..4a98aa411d 100644 --- a/editor/progress_dialog.cpp +++ b/editor/progress_dialog.cpp @@ -30,16 +30,16 @@ #include "progress_dialog.h" +#include "core/message_queue.h" +#include "core/os/os.h" #include "editor_scale.h" #include "main/main.h" -#include "message_queue.h" -#include "os/os.h" void BackgroundProgress::_add_task(const String &p_task, const String &p_label, int p_steps) { _THREAD_SAFE_METHOD_ ERR_FAIL_COND(tasks.has(p_task)); - Task t; + BackgroundProgress::Task t; t.hb = memnew(HBoxContainer); Label *l = memnew(Label); l->set_text(p_label + " "); @@ -112,7 +112,7 @@ void BackgroundProgress::add_task(const String &p_task, const String &p_label, i void BackgroundProgress::task_step(const String &p_task, int p_step) { //this code is weird, but it prevents deadlock. - bool no_updates; + bool no_updates = true; { _THREAD_SAFE_METHOD_ no_updates = updates.empty(); @@ -167,7 +167,7 @@ void ProgressDialog::_popup() { void ProgressDialog::add_task(const String &p_task, const String &p_label, int p_steps, bool p_can_cancel) { ERR_FAIL_COND(tasks.has(p_task)); - Task t; + ProgressDialog::Task t; t.vb = memnew(VBoxContainer); VBoxContainer *vb2 = memnew(VBoxContainer); t.vb->add_margin_child(p_label, vb2); diff --git a/editor/project_export.cpp b/editor/project_export.cpp index 019d5d382c..f4f1f8068d 100644 --- a/editor/project_export.cpp +++ b/editor/project_export.cpp @@ -30,17 +30,17 @@ #include "project_export.h" -#include "compressed_translation.h" +#include "core/compressed_translation.h" +#include "core/io/image_loader.h" +#include "core/io/resource_loader.h" +#include "core/io/resource_saver.h" +#include "core/os/dir_access.h" +#include "core/os/file_access.h" +#include "core/os/os.h" +#include "core/project_settings.h" #include "editor_data.h" #include "editor_node.h" #include "editor_settings.h" -#include "io/image_loader.h" -#include "io/resource_loader.h" -#include "io/resource_saver.h" -#include "os/dir_access.h" -#include "os/file_access.h" -#include "os/os.h" -#include "project_settings.h" #include "scene/gui/box_container.h" #include "scene/gui/margin_container.h" #include "scene/gui/scroll_container.h" @@ -50,6 +50,7 @@ void ProjectExportDialog::_notification(int p_what) { switch (p_what) { case NOTIFICATION_READY: { + duplicate_preset->set_icon(get_icon("Duplicate", "EditorIcons")); delete_preset->set_icon(get_icon("Remove", "EditorIcons")); connect("confirmed", this, "_export_pck_zip"); custom_feature_display->get_parent_control()->add_style_override("panel", get_stylebox("bg", "Tree")); @@ -58,6 +59,7 @@ void ProjectExportDialog::_notification(int p_what) { EditorSettings::get_singleton()->set("interface/dialogs/export_bounds", get_rect()); } break; case NOTIFICATION_THEME_CHANGED: { + duplicate_preset->set_icon(get_icon("Duplicate", "EditorIcons")); delete_preset->set_icon(get_icon("Remove", "EditorIcons")); Control *panel = custom_feature_display->get_parent_control(); if (panel) @@ -168,9 +170,11 @@ void ProjectExportDialog::_edit_preset(int p_index) { if (p_index < 0 || p_index >= presets->get_item_count()) { name->set_text(""); name->set_editable(false); + export_path->set_editable(false); runnable->set_disabled(true); parameters->edit(NULL); presets->unselect_all(); + duplicate_preset->set_disabled(true); delete_preset->set_disabled(true); sections->hide(); patches->clear(); @@ -188,8 +192,11 @@ void ProjectExportDialog::_edit_preset(int p_index) { sections->show(); name->set_editable(true); + export_path->set_editable(true); + duplicate_preset->set_disabled(false); delete_preset->set_disabled(false); name->set_text(current->get_name()); + export_path->set_text(current->get_export_path()); runnable->set_disabled(false); runnable->set_pressed(current->is_runnable()); parameters->edit(current.ptr()); @@ -428,6 +435,73 @@ void ProjectExportDialog::_name_changed(const String &p_string) { _update_presets(); } +void ProjectExportDialog::_export_path_changed(const String &p_string) { + + if (updating) + return; + + Ref<EditorExportPreset> current = EditorExport::get_singleton()->get_export_preset(presets->get_current()); + ERR_FAIL_COND(current.is_null()); + + current->set_export_path(p_string); + _update_presets(); +} + +void ProjectExportDialog::_duplicate_preset() { + + Ref<EditorExportPreset> current = EditorExport::get_singleton()->get_export_preset(presets->get_current()); + if (current.is_null()) + return; + + Ref<EditorExportPreset> preset = current->get_platform()->create_preset(); + ERR_FAIL_COND(!preset.is_valid()); + + String name = current->get_name() + "" + itos(1); + bool make_runnable = true; + int attempt = 2; + while (true) { + + bool valid = true; + + for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) { + Ref<EditorExportPreset> p = EditorExport::get_singleton()->get_export_preset(i); + if (p->get_platform() == preset->get_platform() && p->is_runnable()) { + make_runnable = false; + } + if (p->get_name() == name) { + valid = false; + break; + } + } + + if (valid) + break; + + attempt++; + name = current->get_name() + " " + itos(attempt); + } + + preset->set_name(name); + if (make_runnable) + preset->set_runnable(make_runnable); + preset->set_export_filter(current->get_export_filter()); + preset->set_include_filter(current->get_include_filter()); + preset->set_exclude_filter(current->get_exclude_filter()); + Vector<String> list = current->get_patches(); + for (int i = 0; i < list.size(); i++) { + preset->add_patch(list[i]); + } + preset->set_custom_features(current->get_custom_features()); + + for (const List<PropertyInfo>::Element *E = current->get_properties().front(); E; E = E->next()) { + preset->set(E->get().name, current->get(E->get().name)); + } + + EditorExport::get_singleton()->add_export_preset(preset); + _update_presets(); + _edit_preset(EditorExport::get_singleton()->get_export_preset_count() - 1); +} + void ProjectExportDialog::_delete_preset() { Ref<EditorExportPreset> current = EditorExport::get_singleton()->get_export_preset(presets->get_current()); @@ -638,10 +712,10 @@ bool ProjectExportDialog::_fill_tree(EditorFileSystemDirectory *p_dir, TreeItem for (int i = 0; i < p_dir->get_subdir_count(); i++) { TreeItem *subdir = include_files->create_item(p_item); - if (_fill_tree(p_dir->get_subdir(i), subdir, current, p_only_scenes) == false) { - memdelete(subdir); - } else { + if (_fill_tree(p_dir->get_subdir(i), subdir, current, p_only_scenes)) { used = true; + } else { + memdelete(subdir); } } @@ -655,16 +729,9 @@ bool ProjectExportDialog::_fill_tree(EditorFileSystemDirectory *p_dir, TreeItem file->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); file->set_text(0, p_dir->get_file(i)); - Ref<Texture> tex; - if (has_icon(type, editor_icons)) { - tex = get_icon(type, editor_icons); - } else { - tex = get_icon("Object", editor_icons); - } - String path = p_dir->get_file_path(i); - file->set_icon(0, tex); + file->set_icon(0, EditorNode::get_singleton()->get_class_icon(type)); file->set_editable(0, true); file->set_checked(0, current->has_export_file(path)); file->set_metadata(0, path); @@ -723,6 +790,25 @@ void ProjectExportDialog::_open_export_template_manager() { hide(); } +void ProjectExportDialog::_validate_export_path(const String &p_path) { + // Disable export via OK button or Enter key if LineEdit has an empty filename + bool invalid_path = (p_path.get_file().get_basename() == ""); + + // Check if state change before needlessly messing with signals + if (invalid_path && export_project->get_ok()->is_disabled()) + return; + if (!invalid_path && !export_project->get_ok()->is_disabled()) + return; + + if (invalid_path) { + export_project->get_ok()->set_disabled(true); + export_project->get_line_edit()->disconnect("text_entered", export_project, "_file_entered"); + } else { + export_project->get_ok()->set_disabled(false); + export_project->get_line_edit()->connect("text_entered", export_project, "_file_entered"); + } +} + void ProjectExportDialog::_export_project() { Ref<EditorExportPreset> current = EditorExport::get_singleton()->get_export_preset(presets->get_current()); @@ -732,12 +818,26 @@ void ProjectExportDialog::_export_project() { export_project->set_access(FileDialog::ACCESS_FILESYSTEM); export_project->clear_filters(); - export_project->set_current_file(default_filename); - String extension = platform->get_binary_extension(current); + List<String> extension_list = platform->get_binary_extensions(current); + for (int i = 0; i < extension_list.size(); i++) { + export_project->add_filter("*." + extension_list[i] + " ; " + platform->get_name() + " Export"); + } - if (extension != String()) { - export_project->add_filter("*." + extension + " ; " + platform->get_name() + " Export"); + if (current->get_export_path() != "") { + export_project->set_current_path(current->get_export_path()); + } else { + if (extension_list.size() >= 1) { + export_project->set_current_file(default_filename + "." + extension_list[0]); + } else { + export_project->set_current_file(default_filename); + } + } + + // Ensure that signal is connected if previous attempt left it disconnected with _validate_export_path + if (!export_project->get_line_edit()->is_connected("text_entered", export_project, "_file_entered")) { + export_project->get_ok()->set_disabled(false); + export_project->get_line_edit()->connect("text_entered", export_project, "_file_entered"); } export_project->set_mode(FileDialog::MODE_SAVE_FILE); @@ -746,13 +846,14 @@ void ProjectExportDialog::_export_project() { void ProjectExportDialog::_export_project_to_path(const String &p_path) { // Save this name for use in future exports (but drop the file extension) - default_filename = p_path.get_basename().get_file(); + default_filename = p_path.get_file().get_basename(); EditorSettings::get_singleton()->set_project_metadata("export_options", "default_filename", default_filename); Ref<EditorExportPreset> current = EditorExport::get_singleton()->get_export_preset(presets->get_current()); ERR_FAIL_COND(current.is_null()); Ref<EditorExportPlatform> platform = current->get_platform(); ERR_FAIL_COND(platform.is_null()); + current->set_export_path(p_path); Error err = platform->export_project(current, export_debug->is_pressed(), p_path, 0); if (err != OK) { @@ -770,6 +871,7 @@ void ProjectExportDialog::_bind_methods() { ClassDB::bind_method("_update_parameters", &ProjectExportDialog::_update_parameters); ClassDB::bind_method("_runnable_pressed", &ProjectExportDialog::_runnable_pressed); ClassDB::bind_method("_name_changed", &ProjectExportDialog::_name_changed); + ClassDB::bind_method("_duplicate_preset", &ProjectExportDialog::_duplicate_preset); ClassDB::bind_method("_delete_preset", &ProjectExportDialog::_delete_preset); ClassDB::bind_method("_delete_preset_confirm", &ProjectExportDialog::_delete_preset_confirm); ClassDB::bind_method("get_drag_data_fw", &ProjectExportDialog::get_drag_data_fw); @@ -785,11 +887,13 @@ void ProjectExportDialog::_bind_methods() { ClassDB::bind_method("_export_pck_zip", &ProjectExportDialog::_export_pck_zip); ClassDB::bind_method("_export_pck_zip_selected", &ProjectExportDialog::_export_pck_zip_selected); ClassDB::bind_method("_open_export_template_manager", &ProjectExportDialog::_open_export_template_manager); + ClassDB::bind_method("_validate_export_path", &ProjectExportDialog::_validate_export_path); ClassDB::bind_method("_export_project", &ProjectExportDialog::_export_project); ClassDB::bind_method("_export_project_to_path", &ProjectExportDialog::_export_project_to_path); ClassDB::bind_method("_custom_features_changed", &ProjectExportDialog::_custom_features_changed); ClassDB::bind_method("_tab_changed", &ProjectExportDialog::_tab_changed); } + ProjectExportDialog::ProjectExportDialog() { set_title(TTR("Export")); @@ -821,6 +925,9 @@ ProjectExportDialog::ProjectExportDialog() { presets->set_drag_forwarding(this); mc->add_child(presets); presets->connect("item_selected", this, "_edit_preset"); + duplicate_preset = memnew(ToolButton); + preset_hb->add_child(duplicate_preset); + duplicate_preset->connect("pressed", this, "_duplicate_preset"); delete_preset = memnew(ToolButton); preset_hb->add_child(delete_preset); delete_preset->connect("pressed", this, "_delete_preset"); @@ -837,6 +944,10 @@ ProjectExportDialog::ProjectExportDialog() { runnable->connect("pressed", this, "_runnable_pressed"); settings_vb->add_child(runnable); + export_path = memnew(LineEdit); + settings_vb->add_margin_child(TTR("Export Path:"), export_path); + export_path->connect("text_changed", this, "_export_path_changed"); + sections = memnew(TabContainer); sections->set_tab_align(TabContainer::ALIGN_LEFT); settings_vb->add_child(sections); @@ -927,7 +1038,9 @@ ProjectExportDialog::ProjectExportDialog() { //disable by default name->set_editable(false); + export_path->set_editable(false); runnable->set_disabled(true); + duplicate_preset->set_disabled(true); delete_preset->set_disabled(true); sections->hide(); parameters->edit(NULL); @@ -942,6 +1055,9 @@ ProjectExportDialog::ProjectExportDialog() { get_cancel()->set_text(TTR("Close")); get_ok()->set_text(TTR("Export PCK/Zip")); export_button = add_button(TTR("Export Project"), !OS::get_singleton()->get_swap_ok_cancel(), "export"); + export_button->connect("pressed", this, "_export_project"); + // Disable initially before we select a valid preset + export_button->set_disabled(true); export_pck_zip = memnew(FileDialog); export_pck_zip->add_filter("*.zip ; ZIP File"); @@ -981,7 +1097,7 @@ ProjectExportDialog::ProjectExportDialog() { export_project->set_access(FileDialog::ACCESS_FILESYSTEM); add_child(export_project); export_project->connect("file_selected", this, "_export_project_to_path"); - export_button->connect("pressed", this, "_export_project"); + export_project->get_line_edit()->connect("text_changed", this, "_validate_export_path"); export_debug = memnew(CheckButton); export_debug->set_text(TTR("Export With Debug")); @@ -997,10 +1113,14 @@ ProjectExportDialog::ProjectExportDialog() { editor_icons = "EditorIcons"; - default_filename = EditorSettings::get_singleton()->get_project_metadata("export_options", "default_filename", String()); - + default_filename = EditorSettings::get_singleton()->get_project_metadata("export_options", "default_filename", ""); + // If no default set, use project name if (default_filename == "") { + // If no project name defined, use a sane default default_filename = ProjectSettings::get_singleton()->get("application/config/name"); + if (default_filename == "") { + default_filename = "UnnamedProject"; + } } } diff --git a/editor/project_export.h b/editor/project_export.h index 1f8723febd..23a6db8942 100644 --- a/editor/project_export.h +++ b/editor/project_export.h @@ -61,10 +61,12 @@ private: TabContainer *sections; MenuButton *add_preset; + Button *duplicate_preset; Button *delete_preset; ItemList *presets; LineEdit *name; + LineEdit *export_path; EditorInspector *parameters; CheckButton *runnable; @@ -106,8 +108,10 @@ private: void _runnable_pressed(); void _update_parameters(const String &p_edited_property); void _name_changed(const String &p_string); + void _export_path_changed(const String &p_string); void _add_preset(int p_platform); void _edit_preset(int p_index); + void _duplicate_preset(); void _delete_preset(); void _delete_preset_confirm(); @@ -136,6 +140,7 @@ private: void _export_pck_zip(); void _export_pck_zip_selected(const String &p_path); + void _validate_export_path(const String &p_path); void _export_project(); void _export_project_to_path(const String &p_path); diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index 3a6a73d3cc..8c906e5f0b 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -30,18 +30,20 @@ #include "project_manager.h" -#include "editor_initialize_ssl.h" +#include "core/io/config_file.h" +#include "core/io/resource_saver.h" +#include "core/io/stream_peer_ssl.h" +#include "core/io/zip_io.h" +#include "core/os/dir_access.h" +#include "core/os/file_access.h" +#include "core/os/keyboard.h" +#include "core/os/os.h" +#include "core/translation.h" +#include "core/version.h" +#include "core/version_hash.gen.h" #include "editor_scale.h" #include "editor_settings.h" #include "editor_themes.h" -#include "io/config_file.h" -#include "io/resource_saver.h" -#include "io/stream_peer_ssl.h" -#include "io/zip_io.h" -#include "os/dir_access.h" -#include "os/file_access.h" -#include "os/keyboard.h" -#include "os/os.h" #include "scene/gui/center_container.h" #include "scene/gui/line_edit.h" #include "scene/gui/margin_container.h" @@ -49,9 +51,6 @@ #include "scene/gui/separator.h" #include "scene/gui/texture_rect.h" #include "scene/gui/tool_button.h" -#include "translation.h" -#include "version.h" -#include "version_hash.gen.h" class ProjectDialog : public ConfirmationDialog { @@ -1000,6 +999,10 @@ void ProjectManager::_unhandled_input(const Ref<InputEvent> &p_ev) { _open_project(); } break; + case KEY_DELETE: { + + _erase_project(); + } break; case KEY_HOME: { for (int i = 0; i < scroll_children->get_child_count(); i++) { @@ -1309,7 +1312,7 @@ void ProjectManager::_load_recent_projects() { show->set_modulate(Color(1, 1, 1, 0.5)); path_hb->add_child(show); show->connect("pressed", this, "_show_project", varray(path)); - show->set_tooltip(TTR("Show In File Manager")); + show->set_tooltip(TTR("Show in File Manager")); Label *fpath = memnew(Label(path)); fpath->set_name("path"); @@ -1751,7 +1754,11 @@ ProjectManager::ProjectManager() { } break; } +#ifndef OSX_ENABLED + // The macOS platform implementation uses its own hiDPI window resizing code + // TODO: Resize windows on hiDPI displays on Windows and Linux and remove the line below OS::get_singleton()->set_window_size(OS::get_singleton()->get_window_size() * MAX(1, EDSCALE)); +#endif } FileDialog::set_default_show_hidden_files(EditorSettings::get_singleton()->get("filesystem/file_dialog/show_hidden_files")); @@ -2055,8 +2062,6 @@ void ProjectListFilter::_bind_methods() { ProjectListFilter::ProjectListFilter() { - editor_initialize_certificates(); //for asset sharing - _current_filter = FILTER_NAME; filter_option = memnew(OptionButton); diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index a7c336eb16..7a68646f40 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -1750,12 +1750,9 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { globals_editor = memnew(SectionedInspector); props_base->add_child(globals_editor); globals_editor->get_inspector()->set_undo_redo(EditorNode::get_singleton()->get_undo_redo()); - globals_editor->get_inspector()->set_property_selectable(true); - //globals_editor->hide_top_label(); globals_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL); globals_editor->register_search_box(search_box); globals_editor->get_inspector()->connect("property_selected", this, "_item_selected"); - //globals_editor->get_inspector()->connect("property_toggled", this, "_item_checked", varray(), CONNECT_DEFERRED); globals_editor->get_inspector()->connect("property_edited", this, "_settings_prop_edited"); globals_editor->get_inspector()->connect("restart_requested", this, "_editor_restart_request"); @@ -1843,9 +1840,9 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { input_editor->set_column_title(0, TTR("Action")); input_editor->set_column_title(1, TTR("Deadzone")); input_editor->set_column_expand(1, false); - input_editor->set_column_min_width(1, 80); + input_editor->set_column_min_width(1, 80 * EDSCALE); input_editor->set_column_expand(2, false); - input_editor->set_column_min_width(2, 50); + input_editor->set_column_min_width(2, 50 * EDSCALE); input_editor->connect("item_edited", this, "_action_edited"); input_editor->connect("item_activated", this, "_action_activated"); input_editor->connect("cell_selected", this, "_action_selected"); diff --git a/editor/property_editor.cpp b/editor/property_editor.cpp index d9812f7425..8da75b7b3f 100644 --- a/editor/property_editor.cpp +++ b/editor/property_editor.cpp @@ -34,6 +34,7 @@ #include "core/io/image_loader.h" #include "core/io/marshalls.h" #include "core/io/resource_loader.h" +#include "core/math/expression.h" #include "core/os/input.h" #include "core/os/keyboard.h" #include "core/pair.h" @@ -131,7 +132,7 @@ void CustomPropertyEditor::_menu_option(int p_which) { emit_signal("variant_changed"); } else if (hint == PROPERTY_HINT_ENUM) { - v = p_which; + v = menu->get_item_metadata(p_which); emit_signal("variant_changed"); } } break; @@ -426,12 +427,14 @@ bool CustomPropertyEditor::edit(Object *p_owner, const String &p_name, Variant:: } else if (hint == PROPERTY_HINT_ENUM) { Vector<String> options = hint_text.split(","); + int current_val = 0; for (int i = 0; i < options.size(); i++) { - if (options[i].find(":") != -1) { - menu->add_item(options[i].get_slicec(':', 0), options[i].get_slicec(':', 1).to_int()); - } else { - menu->add_item(options[i], i); - } + Vector<String> text_split = options[i].split(":"); + if (text_split.size() != 1) + current_val = text_split[1].to_int(); + menu->add_item(text_split[0]); + menu->set_item_metadata(i, current_val); + current_val += 1; } menu->set_position(get_position()); menu->popup(); @@ -965,7 +968,7 @@ bool CustomPropertyEditor::edit(Object *p_owner, const String &p_name, Variant:: RES r = v; if (r.is_valid() && r->get_path().is_resource_file()) { menu->add_separator(); - menu->add_item(TTR("Show in File System"), OBJ_MENU_SHOW_IN_FILE_SYSTEM); + menu->add_item(TTR("Show in FileSystem"), OBJ_MENU_SHOW_IN_FILE_SYSTEM); } } else { } @@ -1531,21 +1534,24 @@ void CustomPropertyEditor::_modified(String p_string) { updating = true; switch (type) { case Variant::INT: { - - if (evaluator) - v = evaluator->eval(value_editor[0]->get_text()); - else + String text = value_editor[0]->get_text(); + Ref<Expression> expr; + expr.instance(); + Error err = expr->parse(text); + if (err != OK) { v = value_editor[0]->get_text().to_int(); + return; + } else { + v = expr->execute(Array(), NULL, false); + } emit_signal("variant_changed"); } break; case Variant::REAL: { if (hint != PROPERTY_HINT_EXP_EASING) { - if (evaluator) - v = evaluator->eval(value_editor[0]->get_text()); - else - v = value_editor[0]->get_text().to_double(); + String text = value_editor[0]->get_text(); + v = _parse_real_expression(text); emit_signal("variant_changed"); } @@ -1558,13 +1564,8 @@ void CustomPropertyEditor::_modified(String p_string) { case Variant::VECTOR2: { Vector2 vec; - if (evaluator) { - vec.x = evaluator->eval(value_editor[0]->get_text()); - vec.y = evaluator->eval(value_editor[1]->get_text()); - } else { - vec.x = value_editor[0]->get_text().to_double(); - vec.y = value_editor[1]->get_text().to_double(); - } + vec.x = _parse_real_expression(value_editor[0]->get_text()); + vec.y = _parse_real_expression(value_editor[1]->get_text()); v = vec; _emit_changed_whole_or_field(); @@ -1572,17 +1573,11 @@ void CustomPropertyEditor::_modified(String p_string) { case Variant::RECT2: { Rect2 r2; - if (evaluator) { - r2.position.x = evaluator->eval(value_editor[0]->get_text()); - r2.position.y = evaluator->eval(value_editor[1]->get_text()); - r2.size.x = evaluator->eval(value_editor[2]->get_text()); - r2.size.y = evaluator->eval(value_editor[3]->get_text()); - } else { - r2.position.x = value_editor[0]->get_text().to_double(); - r2.position.y = value_editor[1]->get_text().to_double(); - r2.size.x = value_editor[2]->get_text().to_double(); - r2.size.y = value_editor[3]->get_text().to_double(); - } + + r2.position.x = _parse_real_expression(value_editor[0]->get_text()); + r2.position.y = _parse_real_expression(value_editor[1]->get_text()); + r2.size.x = _parse_real_expression(value_editor[2]->get_text()); + r2.size.y = _parse_real_expression(value_editor[3]->get_text()); v = r2; _emit_changed_whole_or_field(); @@ -1591,15 +1586,9 @@ void CustomPropertyEditor::_modified(String p_string) { case Variant::VECTOR3: { Vector3 vec; - if (evaluator) { - vec.x = evaluator->eval(value_editor[0]->get_text()); - vec.y = evaluator->eval(value_editor[1]->get_text()); - vec.z = evaluator->eval(value_editor[2]->get_text()); - } else { - vec.x = value_editor[0]->get_text().to_double(); - vec.y = value_editor[1]->get_text().to_double(); - vec.z = value_editor[2]->get_text().to_double(); - } + vec.x = _parse_real_expression(value_editor[0]->get_text()); + vec.y = _parse_real_expression(value_editor[1]->get_text()); + vec.z = _parse_real_expression(value_editor[2]->get_text()); v = vec; _emit_changed_whole_or_field(); @@ -1607,17 +1596,10 @@ void CustomPropertyEditor::_modified(String p_string) { case Variant::PLANE: { Plane pl; - if (evaluator) { - pl.normal.x = evaluator->eval(value_editor[0]->get_text()); - pl.normal.y = evaluator->eval(value_editor[1]->get_text()); - pl.normal.z = evaluator->eval(value_editor[2]->get_text()); - pl.d = evaluator->eval(value_editor[3]->get_text()); - } else { - pl.normal.x = value_editor[0]->get_text().to_double(); - pl.normal.y = value_editor[1]->get_text().to_double(); - pl.normal.z = value_editor[2]->get_text().to_double(); - pl.d = value_editor[3]->get_text().to_double(); - } + pl.normal.x = _parse_real_expression(value_editor[0]->get_text()); + pl.normal.y = _parse_real_expression(value_editor[1]->get_text()); + pl.normal.z = _parse_real_expression(value_editor[2]->get_text()); + pl.d = _parse_real_expression(value_editor[3]->get_text()); v = pl; _emit_changed_whole_or_field(); @@ -1625,17 +1607,10 @@ void CustomPropertyEditor::_modified(String p_string) { case Variant::QUAT: { Quat q; - if (evaluator) { - q.x = evaluator->eval(value_editor[0]->get_text()); - q.y = evaluator->eval(value_editor[1]->get_text()); - q.z = evaluator->eval(value_editor[2]->get_text()); - q.w = evaluator->eval(value_editor[3]->get_text()); - } else { - q.x = value_editor[0]->get_text().to_double(); - q.y = value_editor[1]->get_text().to_double(); - q.z = value_editor[2]->get_text().to_double(); - q.w = value_editor[3]->get_text().to_double(); - } + q.x = _parse_real_expression(value_editor[0]->get_text()); + q.y = _parse_real_expression(value_editor[1]->get_text()); + q.z = _parse_real_expression(value_editor[2]->get_text()); + q.w = _parse_real_expression(value_editor[3]->get_text()); v = q; _emit_changed_whole_or_field(); @@ -1645,21 +1620,12 @@ void CustomPropertyEditor::_modified(String p_string) { Vector3 pos; Vector3 size; - if (evaluator) { - pos.x = evaluator->eval(value_editor[0]->get_text()); - pos.y = evaluator->eval(value_editor[1]->get_text()); - pos.z = evaluator->eval(value_editor[2]->get_text()); - size.x = evaluator->eval(value_editor[3]->get_text()); - size.y = evaluator->eval(value_editor[4]->get_text()); - size.z = evaluator->eval(value_editor[5]->get_text()); - } else { - pos.x = value_editor[0]->get_text().to_double(); - pos.y = value_editor[1]->get_text().to_double(); - pos.z = value_editor[2]->get_text().to_double(); - size.x = value_editor[3]->get_text().to_double(); - size.y = value_editor[4]->get_text().to_double(); - size.z = value_editor[5]->get_text().to_double(); - } + pos.x = _parse_real_expression(value_editor[0]->get_text()); + pos.y = _parse_real_expression(value_editor[1]->get_text()); + pos.z = _parse_real_expression(value_editor[2]->get_text()); + size.x = _parse_real_expression(value_editor[3]->get_text()); + size.y = _parse_real_expression(value_editor[4]->get_text()); + size.z = _parse_real_expression(value_editor[5]->get_text()); v = AABB(pos, size); _emit_changed_whole_or_field(); @@ -1668,11 +1634,7 @@ void CustomPropertyEditor::_modified(String p_string) { Transform2D m; for (int i = 0; i < 6; i++) { - if (evaluator) { - m.elements[i / 2][i % 2] = evaluator->eval(value_editor[i]->get_text()); - } else { - m.elements[i / 2][i % 2] = value_editor[i]->get_text().to_double(); - } + m.elements[i / 2][i % 2] = _parse_real_expression(value_editor[i]->get_text()); } v = m; @@ -1683,12 +1645,7 @@ void CustomPropertyEditor::_modified(String p_string) { Basis m; for (int i = 0; i < 9; i++) { - - if (evaluator) { - m.elements[i / 3][i % 3] = evaluator->eval(value_editor[i]->get_text()); - } else { - m.elements[i / 3][i % 3] = value_editor[i]->get_text().to_double(); - } + m.elements[i / 3][i % 3] = _parse_real_expression(value_editor[i]->get_text()); } v = m; @@ -1699,25 +1656,14 @@ void CustomPropertyEditor::_modified(String p_string) { Basis basis; for (int i = 0; i < 9; i++) { - - if (evaluator) { - basis.elements[i / 3][i % 3] = evaluator->eval(value_editor[(i / 3) * 4 + i % 3]->get_text()); - } else { - basis.elements[i / 3][i % 3] = value_editor[(i / 3) * 4 + i % 3]->get_text().to_double(); - } + basis.elements[i / 3][i % 3] = _parse_real_expression(value_editor[(i / 3) * 4 + i % 3]->get_text()); } Vector3 origin; - if (evaluator) { - origin.x = evaluator->eval(value_editor[3]->get_text()); - origin.y = evaluator->eval(value_editor[7]->get_text()); - origin.z = evaluator->eval(value_editor[11]->get_text()); - } else { - origin.x = value_editor[3]->get_text().to_double(); - origin.y = value_editor[7]->get_text().to_double(); - origin.z = value_editor[11]->get_text().to_double(); - } + origin.x = _parse_real_expression(value_editor[3]->get_text()); + origin.y = _parse_real_expression(value_editor[7]->get_text()); + origin.z = _parse_real_expression(value_editor[11]->get_text()); v = Transform(basis, origin); _emit_changed_whole_or_field(); @@ -1759,6 +1705,19 @@ void CustomPropertyEditor::_modified(String p_string) { updating = false; } +real_t CustomPropertyEditor::_parse_real_expression(String text) { + Ref<Expression> expr; + expr.instance(); + Error err = expr->parse(text); + real_t out; + if (err != OK) { + out = value_editor[0]->get_text().to_double(); + } else { + out = expr->execute(Array(), NULL, false); + } + return out; +} + void CustomPropertyEditor::_emit_changed_whole_or_field() { if (!Input::get_singleton()->is_key_pressed(KEY_SHIFT)) { @@ -1910,6 +1869,7 @@ void CustomPropertyEditor::_bind_methods() { ADD_SIGNAL(MethodInfo("variant_field_changed", PropertyInfo(Variant::STRING, "field"))); ADD_SIGNAL(MethodInfo("resource_edit_request")); } + CustomPropertyEditor::CustomPropertyEditor() { read_only = false; @@ -2033,2743 +1993,3 @@ CustomPropertyEditor::CustomPropertyEditor() { create_dialog = NULL; property_select = NULL; } - -bool PropertyEditor::_might_be_in_instance() { - - if (!obj) - return false; - - Node *node = Object::cast_to<Node>(obj); - - Node *edited_scene = EditorNode::get_singleton()->get_edited_scene(); - - bool might_be = false; - - while (node) { - - if (node->get_scene_instance_state().is_valid()) { - might_be = true; - break; - } - if (node == edited_scene) { - if (node->get_scene_inherited_state().is_valid()) { - might_be = true; - break; - } - might_be = false; - break; - } - node = node->get_owner(); - } - - return might_be; -} - -bool PropertyEditor::_get_instanced_node_original_property(const StringName &p_prop, Variant &value) { - - Node *node = Object::cast_to<Node>(obj); - - if (!node) - return false; - - Node *orig = node; - - Node *edited_scene = EditorNode::get_singleton()->get_edited_scene(); - - bool found = false; - - while (node) { - - Ref<SceneState> ss; - - if (node == edited_scene) { - ss = node->get_scene_inherited_state(); - - } else { - ss = node->get_scene_instance_state(); - } - - if (ss.is_valid()) { - - NodePath np = node->get_path_to(orig); - int node_idx = ss->find_node_by_path(np); - if (node_idx >= 0) { - bool lfound = false; - Variant lvar; - lvar = ss->get_property_value(node_idx, p_prop, lfound); - if (lfound) { - - found = true; - value = lvar; - } - } - } - if (node == edited_scene) { - //just in case - break; - } - node = node->get_owner(); - } - - return found; -} - -bool PropertyEditor::_is_property_different(const Variant &p_current, const Variant &p_orig, int p_usage) { - - { - Node *node = Object::cast_to<Node>(obj); - if (!node) - return false; - - Node *edited_scene = EditorNode::get_singleton()->get_edited_scene(); - bool found_state = false; - - while (node) { - - Ref<SceneState> ss; - - if (node == edited_scene) { - ss = node->get_scene_inherited_state(); - - } else { - ss = node->get_scene_instance_state(); - } - - if (ss.is_valid()) { - found_state = true; - } - if (node == edited_scene) { - //just in case - break; - } - node = node->get_owner(); - } - - if (!found_state) - return false; //pointless to check if we are not comparing against anything. - } - - if (p_orig.get_type() == Variant::NIL) { - - //special cases - if (p_current.is_zero() && p_usage & PROPERTY_USAGE_STORE_IF_NONZERO) - return false; - if (p_current.is_one() && p_usage & PROPERTY_USAGE_STORE_IF_NONONE) - return false; - } - - if (p_current.get_type() == Variant::REAL && p_orig.get_type() == Variant::REAL) { - float a = p_current; - float b = p_orig; - - return Math::abs(a - b) > CMP_EPSILON; //this must be done because, as some scenes save as text, there might be a tiny difference in floats due to numerical error - } - - return bool(Variant::evaluate(Variant::OP_NOT_EQUAL, p_current, p_orig)); -} - -bool PropertyEditor::_is_instanced_node_with_original_property_different(const String &p_name, TreeItem *item) { - bool mbi = _might_be_in_instance(); - if (mbi) { - Variant vorig; - Dictionary d = item->get_metadata(0); - int usage = d.has("usage") ? int(int(d["usage"]) & (PROPERTY_USAGE_STORE_IF_NONONE | PROPERTY_USAGE_STORE_IF_NONZERO)) : 0; - if (_get_instanced_node_original_property(p_name, vorig) || usage) { - Variant v = obj->get(p_name); - - if (_is_property_different(v, vorig, usage)) { - return true; - } - } - } - return false; -} - -TreeItem *PropertyEditor::find_item(TreeItem *p_item, const String &p_name) { - - if (!p_item) - return NULL; - - String name = p_item->get_metadata(1); - - if (name == p_name) { - - return p_item; - } - - TreeItem *c = p_item->get_children(); - - while (c) { - - TreeItem *found = find_item(c, p_name); - if (found) - return found; - c = c->get_next(); - } - - return NULL; -} - -void PropertyEditor::_changed_callback(Object *p_changed, const char *p_prop) { - - _changed_callbacks(p_changed, p_prop); -} - -void PropertyEditor::_changed_callbacks(Object *p_changed, const String &p_prop) { - - if (p_changed != obj) - return; - - if (changing) - return; - - if (p_prop == String()) - update_tree_pending = true; - else { - - pending[p_prop] = p_prop; - } -} - -void PropertyEditor::update_property(const String &p_prop) { - - if (obj) - _changed_callbacks(obj, p_prop); -} - -void PropertyEditor::set_item_text(TreeItem *p_item, int p_type, const String &p_name, int p_hint, const String &p_hint_text) { - - switch (p_type) { - - case Variant::BOOL: { - - p_item->set_checked(1, obj->get(p_name)); - } break; - case Variant::REAL: - case Variant::INT: { - - if (p_hint == PROPERTY_HINT_LAYERS_2D_PHYSICS || p_hint == PROPERTY_HINT_LAYERS_2D_RENDER || p_hint == PROPERTY_HINT_LAYERS_3D_PHYSICS || p_hint == PROPERTY_HINT_LAYERS_3D_RENDER) { - tree->update(); - break; - } - - if (p_hint == PROPERTY_HINT_FLAGS) { - Vector<String> values = p_hint_text.split(","); - String flags; - int val = obj->get(p_name); - for (int i = 0; i < values.size(); i++) { - - String v = values[i]; - if (v == "") - continue; - if (!(val & (1 << i))) - continue; - - if (flags != "") - flags += ", "; - flags += v; - } - p_item->set_text(1, flags); - break; - } - - if (p_hint == PROPERTY_HINT_EXP_EASING) { - - p_item->set_text(1, String::num(obj->get(p_name), 2)); - break; - } - - if (p_type == Variant::REAL) { - p_item->set_range(1, obj->get(p_name)); - - } else { - /* FIXME: Why are both statements equal? */ - p_item->set_range(1, obj->get(p_name)); - } - - p_item->set_editable(1, !read_only); - - } break; - case Variant::STRING: - - if (p_hint == PROPERTY_HINT_TYPE_STRING) { - - p_item->set_text(1, obj->get(p_name)); - } - - if (p_hint == PROPERTY_HINT_METHOD_OF_VARIANT_TYPE || - p_hint == PROPERTY_HINT_METHOD_OF_BASE_TYPE || - p_hint == PROPERTY_HINT_METHOD_OF_INSTANCE || - p_hint == PROPERTY_HINT_METHOD_OF_SCRIPT || - p_hint == PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE || - p_hint == PROPERTY_HINT_PROPERTY_OF_BASE_TYPE || - p_hint == PROPERTY_HINT_PROPERTY_OF_INSTANCE || - p_hint == PROPERTY_HINT_PROPERTY_OF_SCRIPT) { - - p_item->set_text(1, obj->get(p_name)); - } - - if (p_hint == PROPERTY_HINT_ENUM) { - - Vector<String> strings = p_hint_text.split(","); - String current = obj->get(p_name); - int idx = 0; - for (int x = 0; x < strings.size(); x++) { - if (strings[x] == current) { - idx = x; - break; - } - } - p_item->set_text(1, p_hint_text); - p_item->set_range(1, idx); - break; - } - - case Variant::VECTOR3: - case Variant::QUAT: - case Variant::VECTOR2: - case Variant::AABB: - case Variant::RECT2: - case Variant::TRANSFORM2D: - case Variant::BASIS: - case Variant::TRANSFORM: { - - p_item->set_text(1, obj->get(p_name)); - - } break; - case Variant::COLOR: { - - tree->update(); - - } break; - case Variant::NODE_PATH: { - - p_item->set_text(1, obj->get(p_name)); - } break; - case Variant::OBJECT: { - - Ref<EncodedObjectAsID> encoded = obj->get(p_name); //for debugger and remote tools - - if (encoded.is_valid()) { - - p_item->set_text(1, "Object: " + itos(encoded->get_object_id())); - p_item->set_icon(1, Ref<Texture>()); - p_item->set_custom_as_button(1, true); - - } else if (obj->get(p_name).get_type() == Variant::NIL || obj->get(p_name).operator RefPtr().is_null()) { - p_item->set_text(1, "<null>"); - p_item->set_icon(1, Ref<Texture>()); - p_item->set_custom_as_button(1, false); - - Dictionary d = p_item->get_metadata(0); - int hint = d.has("hint") ? d["hint"].operator int() : -1; - String hint_text = d.has("hint_text") ? d["hint_text"] : ""; - if (hint == PROPERTY_HINT_RESOURCE_TYPE && hint_text == "Texture") { - p_item->set_icon(1, NULL); - } - - } else { - p_item->set_custom_as_button(1, true); - RES res = obj->get(p_name).operator RefPtr(); - if (res->is_class("Texture")) { - int tw = EditorSettings::get_singleton()->get("docks/property_editor/texture_preview_width"); - Vector2 size(res->call("get_width"), res->call("get_height")); - if (size.width < size.height) { - tw = MAX((size.width / size.height) * tw, 1); - } - p_item->set_icon_max_width(1, tw); - p_item->set_icon(1, res); - p_item->set_text(1, ""); - - } else if (res->get_name() != "") { - - p_item->set_text(1, res->get_name()); - } else if (res->get_path() != "" && !res->get_path().begins_with("local://")) { - p_item->set_text(1, res->get_path().get_file()); - } else { - p_item->set_text(1, "<" + res->get_class() + ">"); - }; - - if (res.is_valid() && res->get_path().is_resource_file()) { - p_item->set_tooltip(1, res->get_path()); - } else if (res.is_valid()) { - p_item->set_tooltip(1, res->get_name() + " (" + res->get_class() + ")"); - } - - if (has_icon(res->get_class(), "EditorIcons")) { - - p_item->set_icon(0, get_icon(res->get_class(), "EditorIcons")); - } else { - - Dictionary d = p_item->get_metadata(0); - int hint = d.has("hint") ? d["hint"].operator int() : -1; - String hint_text = d.has("hint_text") ? d["hint_text"] : ""; - if (hint == PROPERTY_HINT_RESOURCE_TYPE) { - - if (has_icon(hint_text, "EditorIcons")) { - - p_item->set_icon(0, get_icon(hint_text, "EditorIcons")); - - } else { - p_item->set_icon(0, get_icon("Object", "EditorIcons")); - } - } - } - - if (res->is_class("Script")) { - p_item->set_text(1, res->get_path().get_file()); - } else if (!res->is_class("Texture")) { - //texture already previews via itself - EditorResourcePreview::get_singleton()->queue_edited_resource_preview(res, this, "_resource_preview_done", p_item->get_instance_id()); - } - } - - } break; - default: {}; - } -} - -void PropertyEditor::_check_reload_status(const String &p_name, TreeItem *item) { - - bool has_reload = false; - int found = -1; - bool is_disabled = false; - - for (int i = 0; i < item->get_button_count(1); i++) { - - if (item->get_button_id(1, i) == 3) { - found = i; - is_disabled = item->is_button_disabled(1, i); - break; - } - } - - if (_is_instanced_node_with_original_property_different(p_name, item)) { - has_reload = true; - } - - if (obj->call("property_can_revert", p_name).operator bool()) { - - has_reload = true; - } - - if (!has_reload && !obj->get_script().is_null()) { - Ref<Script> scr = obj->get_script(); - Variant orig_value; - if (scr->get_property_default_value(p_name, orig_value)) { - if (orig_value != obj->get(p_name)) { - has_reload = true; - } - } - } - - if (found != -1 && !has_reload) { - - if (!is_disabled) { - item->erase_button(1, found); - if (item->get_cell_mode(1) == TreeItem::CELL_MODE_RANGE && item->get_text(1) == String()) { - item->add_button(1, get_icon("ReloadEmpty", "EditorIcons"), 3, true); - } - } - } else if (found == -1 && has_reload) { - item->add_button(1, get_icon("ReloadSmall", "EditorIcons"), 3); - } else if (found != -1 && has_reload && is_disabled) { - item->erase_button(1, found); - item->add_button(1, get_icon("ReloadSmall", "EditorIcons"), 3); - } -} - -bool PropertyEditor::_is_drop_valid(const Dictionary &p_drag_data, const Dictionary &p_item_data) const { - - Dictionary d = p_item_data; - - if (d.has("type")) { - - int type = d["type"]; - if (type == Variant::OBJECT && d.has("hint") && d.has("hint_text") && int(d["hint"]) == PROPERTY_HINT_RESOURCE_TYPE) { - - String allowed_type = d["hint_text"]; - - Dictionary drag_data = p_drag_data; - if (drag_data.has("type") && String(drag_data["type"]) == "resource") { - Ref<Resource> res = drag_data["resource"]; - for (int i = 0; i < allowed_type.get_slice_count(","); i++) { - String at = allowed_type.get_slice(",", i).strip_edges(); - if (res.is_valid() && ClassDB::is_parent_class(res->get_class(), at)) { - return true; - } - } - } - - if (drag_data.has("type") && String(drag_data["type"]) == "files") { - - Vector<String> files = drag_data["files"]; - - if (files.size() == 1) { - String file = files[0]; - String ftype = EditorFileSystem::get_singleton()->get_file_type(file); - - if (ftype != "") { - - for (int i = 0; i < allowed_type.get_slice_count(","); i++) { - String at = allowed_type.get_slice(",", i).strip_edges(); - if (ClassDB::is_parent_class(ftype, at)) { - return true; - } - } - } - } - } - } - } - - return false; -} -void PropertyEditor::_mark_drop_fields(TreeItem *p_at) { - - if (_is_drop_valid(get_viewport()->gui_get_drag_data(), p_at->get_metadata(0))) - p_at->set_custom_bg_color(1, get_color("accent_color", "Editor"), true); - - if (p_at->get_children()) { - _mark_drop_fields(p_at->get_children()); - } - - if (p_at->get_next()) { - _mark_drop_fields(p_at->get_next()); - } -} - -Variant PropertyEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) { - - TreeItem *item = tree->get_item_at_position(p_point); - if (!item) - return Variant(); - - Dictionary d = item->get_metadata(0); - if (!d.has("name")) - return Variant(); - - int col = tree->get_column_at_position(p_point); - if (col == 0) { - - Dictionary dp; - dp["type"] = "obj_property"; - dp["object"] = obj; - dp["property"] = d["name"]; - dp["value"] = obj->get(d["name"]); - - Label *label = memnew(Label); - label->set_text(d["name"]); - set_drag_preview(label); - return dp; - } - - Variant val = obj->get(d["name"]); - - if (val.get_type() == Variant::OBJECT) { - RES res = val; - if (res.is_valid()) { - - custom_editor->hide_menu(); - return EditorNode::get_singleton()->drag_resource(res, p_from); - } - } - - return Variant(); -} - -bool PropertyEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const { - - TreeItem *item = tree->get_item_at_position(p_point); - if (!item) - return false; - - int col = tree->get_column_at_position(p_point); - if (col != 1) - return false; - - return _is_drop_valid(p_data, item->get_metadata(0)); -} -void PropertyEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) { - - TreeItem *item = tree->get_item_at_position(p_point); - if (!item) - return; - - int col = tree->get_column_at_position(p_point); - if (col != 1) - return; - - if (!_is_drop_valid(p_data, item->get_metadata(0))) - return; - - Dictionary d = item->get_metadata(0); - - if (!d.has("name")) - return; - - String name = d["name"]; - - Dictionary drag_data = p_data; - if (drag_data.has("type") && String(drag_data["type"]) == "resource") { - Ref<Resource> res = drag_data["resource"]; - if (res.is_valid()) { - _edit_set(name, res); - return; - } - } - - if (drag_data.has("type") && String(drag_data["type"]) == "files") { - - Vector<String> files = drag_data["files"]; - - if (files.size() == 1) { - String file = files[0]; - RES res = ResourceLoader::load(file); - if (res.is_valid()) { - _edit_set(name, res); - return; - } - } - } -} - -void PropertyEditor::_clear_drop_fields(TreeItem *p_at) { - - Dictionary d = p_at->get_metadata(0); - - if (d.has("type")) { - - int type = d["type"]; - if (type == Variant::OBJECT) { - p_at->clear_custom_bg_color(1); - } - } - - if (p_at->get_children()) { - _clear_drop_fields(p_at->get_children()); - } - - if (p_at->get_next()) { - _clear_drop_fields(p_at->get_next()); - } -} - -void PropertyEditor::_notification(int p_what) { - - if (p_what == NOTIFICATION_ENTER_TREE) { - - get_tree()->connect("node_removed", this, "_node_removed"); - } - if (p_what == NOTIFICATION_EXIT_TREE) { - - get_tree()->disconnect("node_removed", this, "_node_removed"); - edit(NULL); - } - - if (p_what == NOTIFICATION_DRAG_BEGIN) { - - if (is_visible_in_tree() && tree->get_root()) { - _mark_drop_fields(tree->get_root()); - } - } - - if (p_what == NOTIFICATION_DRAG_END) { - if (is_visible_in_tree() && tree->get_root()) { - _clear_drop_fields(tree->get_root()); - } - } - - if (p_what == NOTIFICATION_PHYSICS_PROCESS) { - - if (refresh_countdown > 0) { - refresh_countdown -= get_physics_process_delta_time(); - if (refresh_countdown <= 0) { - TreeItem *root = tree->get_root(); - _refresh_item(root); - } - } - - changing = true; - - if (update_tree_pending) { - - update_tree(); - update_tree_pending = false; - - } else { - - const String *k = NULL; - while ((k = pending.next(k))) { - - TreeItem *item = find_item(tree->get_root(), *k); - if (!item) - continue; - - _check_reload_status(*k, item); - - Dictionary d = item->get_metadata(0); - set_item_text(item, d["type"], d["name"], d["hint"], d["hint_text"]); - } - } - - pending.clear(); - - changing = false; - } - - if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) { - update_tree(); - } -} - -TreeItem *PropertyEditor::get_parent_node(String p_path, HashMap<String, TreeItem *> &item_paths, TreeItem *root, TreeItem *category) { - - TreeItem *item = NULL; - - if (p_path == "") { - - item = category ? category : root; - } else if (item_paths.has(p_path)) { - - item = item_paths.get(p_path); - } else { - - TreeItem *parent = get_parent_node(p_path.left(p_path.find_last("/")), item_paths, root, NULL); - item = tree->create_item(parent); - - String name = (p_path.find("/") != -1) ? p_path.right(p_path.find_last("/") + 1) : p_path; - item->set_text(0, capitalize_paths ? name.capitalize() : name); - item->set_tooltip(0, p_path); - if (item->get_parent() != root) { - item->set_icon(0, get_icon("Folder", "EditorIcons")); - } - - item->set_editable(0, false); - if (!subsection_selectable) { - item->set_expand_right(0, true); - } - item->set_selectable(0, subsection_selectable); - item->set_editable(1, false); - item->set_selectable(1, subsection_selectable); - - if (use_folding) { - if (!obj->editor_is_section_unfolded(p_path)) { - updating_folding = true; - item->set_collapsed(true); - updating_folding = false; - } - item->set_metadata(0, p_path); - foldable_property_cache.push_back(p_path); - } - - if (item->get_parent() == root) { - - item->set_custom_bg_color(0, get_color("prop_subsection", "Editor")); - item->set_custom_bg_color(1, get_color("prop_subsection", "Editor")); - } - - item_paths[p_path] = item; - } - - return item; -} - -void PropertyEditor::_refresh_item(TreeItem *p_item) { - - if (!p_item) - return; - - String name = p_item->get_metadata(1); - - if (name != String()) { - - _check_reload_status(name, p_item); - Dictionary d = p_item->get_metadata(0); - set_item_text(p_item, d["type"], d["name"], d["hint"], d["hint_text"]); - } - - TreeItem *c = p_item->get_children(); - - while (c) { - - _refresh_item(c); - - c = c->get_next(); - } -} - -void PropertyEditor::refresh() { - - if (refresh_countdown > 0) - return; - refresh_countdown = EditorSettings::get_singleton()->get("docks/property_editor/auto_refresh_interval"); -} - -void PropertyEditor::update_tree() { - - tree->clear(); - foldable_property_cache.clear(); - - if (!obj) - return; - - HashMap<String, TreeItem *> item_path; - - TreeItem *root = tree->create_item(NULL); - tree->set_hide_root(true); - - List<PropertyInfo> plist; - obj->get_property_list(&plist, true); - - bool draw_red = false; - - { - Node *nod = Object::cast_to<Node>(obj); - Node *es = EditorNode::get_singleton()->get_edited_scene(); - if (nod && es != nod && nod->get_owner() != es) { - draw_red = true; - } - } - - Color sscolor = get_color("prop_subsection", "Editor"); - - TreeItem *current_category = NULL; - - String filter = search_box ? search_box->get_text() : ""; - String group; - String group_base; - - for (List<PropertyInfo>::Element *I = plist.front(); I; I = I->next()) { - - PropertyInfo &p = I->get(); - - //make sure the property can be edited - - if (p.usage & PROPERTY_USAGE_GROUP) { - - group = p.name; - group_base = p.hint_string; - - continue; - - } else if (p.usage & PROPERTY_USAGE_CATEGORY) { - - group = ""; - group_base = ""; - - if (!show_categories) - continue; - - List<PropertyInfo>::Element *N = I->next(); - bool valid = true; - //if no properties in category, skip - while (N) { - if (N->get().usage & PROPERTY_USAGE_EDITOR) - break; - if (N->get().usage & PROPERTY_USAGE_CATEGORY) { - valid = false; - break; - } - N = N->next(); - } - if (!valid) - continue; //empty, ignore - TreeItem *sep = tree->create_item(root); - current_category = sep; - String type = p.name; - - if (has_icon(type, "EditorIcons")) - sep->set_icon(0, get_icon(type, "EditorIcons")); - else - sep->set_icon(0, get_icon("Object", "EditorIcons")); - - sep->set_text(0, type); - sep->set_expand_right(0, true); - sep->set_selectable(0, false); - sep->set_selectable(1, false); - sep->set_custom_bg_color(0, get_color("prop_category", "Editor")); - sep->set_custom_bg_color(1, get_color("prop_category", "Editor")); - sep->set_text_align(0, TreeItem::ALIGN_CENTER); - sep->set_disable_folding(true); - - if (use_doc_hints) { - StringName type = p.name; - if (!class_descr_cache.has(type)) { - - String descr; - DocData *dd = EditorHelp::get_doc_data(); - Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(type); - if (E) { - descr = E->get().brief_description; - } - class_descr_cache[type] = descr.word_wrap(80); - } - - sep->set_tooltip(0, TTR("Class:") + " " + p.name + (class_descr_cache[type] == "" ? "" : "\n\n" + class_descr_cache[type])); - } - continue; - - } else if (!(p.usage & PROPERTY_USAGE_EDITOR)) - continue; - - if (hide_script && p.name == "script") - continue; - - String basename = p.name; - if (group != "") { - if (group_base != "") { - if (basename.begins_with(group_base)) { - basename = basename.replace_first(group_base, ""); - } else if (group_base.begins_with(basename)) { - //keep it, this is used pretty often - } else { - group = ""; //no longer using group base, clear - } - } - } - - if (group != "") { - basename = group + "/" + basename; - } - - String name = (basename.find("/") != -1) ? basename.right(basename.find_last("/") + 1) : basename; - - if (capitalize_paths) { - int dot = name.find("."); - if (dot != -1) { - String ov = name.right(dot); - name = name.substr(0, dot); - name = name.camelcase_to_underscore().capitalize(); - name += ov; - - } else { - name = name.camelcase_to_underscore().capitalize(); - } - } - - String path = basename.left(basename.find_last("/")); - - if (use_filter && filter != "") { - - String cat = path; - - if (capitalize_paths) - cat = cat.capitalize(); - - if (!filter.is_subsequence_ofi(cat) && !filter.is_subsequence_ofi(name)) - continue; - } - - TreeItem *parent = get_parent_node(path, item_path, root, current_category); - int level = 0; - if (parent != root) { - level++; - TreeItem *parent_lev = parent; - while (parent_lev->get_parent() != root) { - parent_lev = parent_lev->get_parent(); - level++; - } - } - if (level > 4) - level = 4; - - Color col = sscolor; - col.a = (level / 4.0) * 0.7; - - TreeItem *item = tree->create_item(parent); - - if (level > 0) { - item->set_custom_bg_color(0, col); - } - item->set_editable(0, false); - item->set_selectable(0, property_selectable); - - if (p.usage & PROPERTY_USAGE_CHECKABLE) { - - item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); - item->set_selectable(0, true); - item->set_editable(0, true); - item->set_checked(0, p.usage & PROPERTY_USAGE_CHECKED); - } - - item->set_text(0, name); - item->set_tooltip(0, p.name); - - if (name.find(".") != -1) { - Color textcol = get_color("font_color", "Tree"); - textcol.a *= 0.5; - //override :D - item->set_custom_color(0, textcol); - item->set_custom_color(1, textcol); - - Color iconcol(1, 1, 1, 0.6); - item->set_icon_color(0, iconcol); - item->set_icon_color(1, iconcol); - } - - if (use_doc_hints) { - - StringName classname = obj->get_class_name(); - StringName propname = p.name; - String descr; - bool found = false; - - Map<StringName, Map<StringName, String> >::Element *E = descr_cache.find(classname); - if (E) { - Map<StringName, String>::Element *F = E->get().find(propname); - if (F) { - found = true; - descr = F->get(); - } - } - - if (!found) { - DocData *dd = EditorHelp::get_doc_data(); - Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(classname); - while (E && descr == String()) { - for (int i = 0; i < E->get().properties.size(); i++) { - if (E->get().properties[i].name == propname.operator String()) { - descr = E->get().properties[i].description.strip_edges().word_wrap(80); - break; - } - } - if (!E->get().inherits.empty()) { - E = dd->class_list.find(E->get().inherits); - } else { - break; - } - } - descr_cache[classname][propname] = descr; - } - - item->set_tooltip(0, TTR("Property:") + " " + p.name + (descr == "" ? "" : "\n\n" + descr)); - } - - Dictionary d; - d["name"] = p.name; - d["type"] = (int)p.type; - d["hint"] = (int)p.hint; - d["hint_text"] = p.hint_string; - d["usage"] = (int)p.usage; - - item->set_metadata(0, d); - item->set_metadata(1, p.name); - - if (draw_red) - item->set_custom_color(0, get_color("error_color", "Editor")); - - if (p.name == selected_property) { - - item->select(1); - } - - switch (p.type) { - - case Variant::BOOL: { - - item->set_cell_mode(1, TreeItem::CELL_MODE_CHECK); - item->set_text(1, TTR("On")); - item->set_tooltip(1, obj->get(p.name) ? "True" : "False"); - item->set_checked(1, obj->get(p.name)); - if (show_type_icons) - item->set_icon(0, get_icon("bool", "EditorIcons")); - item->set_editable(1, !read_only); - - } break; - case Variant::REAL: - case Variant::INT: { - - if (p.hint == PROPERTY_HINT_EXP_EASING) { - - item->set_cell_mode(1, TreeItem::CELL_MODE_CUSTOM); - item->set_text(1, String::num(obj->get(p.name), 2)); - item->set_editable(1, !read_only); - if (show_type_icons) - item->set_icon(0, get_icon("Curve", "EditorIcons")); - - break; - } - - if (p.hint == PROPERTY_HINT_LAYERS_2D_PHYSICS || p.hint == PROPERTY_HINT_LAYERS_2D_RENDER || p.hint == PROPERTY_HINT_LAYERS_3D_PHYSICS || p.hint == PROPERTY_HINT_LAYERS_3D_RENDER) { - - item->set_cell_mode(1, TreeItem::CELL_MODE_CUSTOM); - item->set_editable(1, !read_only); - item->set_custom_draw(1, this, "_draw_flags"); - break; - } - - if (p.hint == PROPERTY_HINT_FLAGS) { - - item->set_cell_mode(1, TreeItem::CELL_MODE_CUSTOM); - item->set_editable(1, !read_only); - - Vector<String> values = p.hint_string.split(","); - String flags; - int val = obj->get(p.name); - for (int i = 0; i < values.size(); i++) { - - String v = values[i]; - if (v == "") - continue; - if (!(val & (1 << i))) - continue; - - if (flags != "") - flags += ", "; - flags += v; - } - item->set_text(1, flags); - break; - } - - if (p.hint == PROPERTY_HINT_ENUM) - item->set_cell_mode(1, TreeItem::CELL_MODE_RANGE); - else - item->set_cell_mode(1, TreeItem::CELL_MODE_RANGE_EXPRESSION); - - if (p.hint == PROPERTY_HINT_SPRITE_FRAME || p.hint == PROPERTY_HINT_RANGE || p.hint == PROPERTY_HINT_EXP_RANGE) { - - int c = p.hint_string.get_slice_count(","); - float min = 0, max = 100, step = p.type == Variant::REAL ? .01 : 1; - if (c >= 1) { - - min = p.hint_string.get_slice(",", 0).to_double(); - } - if (c >= 2) { - - max = p.hint_string.get_slice(",", 1).to_double(); - } - - if (p.hint != PROPERTY_HINT_SPRITE_FRAME && c >= 3) { - - step = p.hint_string.get_slice(",", 2).to_double(); - } - - item->set_range_config(1, min, max, step, p.hint == PROPERTY_HINT_EXP_RANGE); - } else if (p.hint == PROPERTY_HINT_ENUM) { - - item->set_text(1, p.hint_string); - if (show_type_icons) - item->set_icon(0, get_icon("Enum", "EditorIcons")); - item->set_range(1, obj->get(p.name)); - item->set_editable(1, !read_only); - break; - } else if (p.hint == PROPERTY_HINT_OBJECT_ID) { - - item->set_cell_mode(1, TreeItem::CELL_MODE_CUSTOM); - - String type = p.hint_string; - if (type == "") - type = "Object"; - - ObjectID id = obj->get(p.name); - if (id != 0) { - item->set_text(1, type + " ID: " + itos(id)); - item->add_button(1, get_icon("EditResource", "EditorIcons")); - } else { - item->set_text(1, TTR("[Empty]")); - } - - if (has_icon(p.hint_string, "EditorIcons")) { - type = p.hint_string; - } else { - type = "Object"; - } - - item->set_icon(0, get_icon(type, "EditorIcons")); - - break; - - } else { - if (p.type == Variant::REAL) { - - item->set_range_config(1, -16777216, 16777216, 0.001); - } else { - - item->set_range_config(1, -2147483647, 2147483647, 1); - } - }; - - if (p.type == Variant::REAL) { - if (show_type_icons) - item->set_icon(0, get_icon("float", "EditorIcons")); - item->set_range(1, obj->get(p.name)); - - } else { - if (show_type_icons) - item->set_icon(0, get_icon("int", "EditorIcons")); - item->set_range(1, obj->get(p.name)); - } - - item->set_editable(1, !read_only); - - } break; - case Variant::STRING: { - - switch (p.hint) { - - case PROPERTY_HINT_DIR: - case PROPERTY_HINT_FILE: - case PROPERTY_HINT_GLOBAL_DIR: - case PROPERTY_HINT_GLOBAL_FILE: { - - item->set_cell_mode(1, TreeItem::CELL_MODE_STRING); - item->set_editable(1, !read_only); - if (show_type_icons) - item->set_icon(0, get_icon("File", "EditorIcons")); - item->set_text(1, obj->get(p.name)); - item->add_button(1, get_icon("Folder", "EditorIcons")); - - } break; - case PROPERTY_HINT_ENUM: { - - item->set_cell_mode(1, TreeItem::CELL_MODE_RANGE); - Vector<String> strings = p.hint_string.split(","); - String current = obj->get(p.name); - int idx = 0; - for (int x = 0; x < strings.size(); x++) { - if (strings[x] == current) { - idx = x; - break; - } - } - item->set_text(1, p.hint_string); - item->set_range(1, idx); - item->set_editable(1, !read_only); - if (show_type_icons) - item->set_icon(0, get_icon("Enum", "EditorIcons")); - - } break; - case PROPERTY_HINT_METHOD_OF_VARIANT_TYPE: ///< a property of a type - case PROPERTY_HINT_METHOD_OF_BASE_TYPE: ///< a method of a base type - case PROPERTY_HINT_METHOD_OF_INSTANCE: ///< a method of an instance - case PROPERTY_HINT_METHOD_OF_SCRIPT: ///< a method of a script & base - case PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE: ///< a property of a type - case PROPERTY_HINT_PROPERTY_OF_BASE_TYPE: ///< a property of a base type - case PROPERTY_HINT_PROPERTY_OF_INSTANCE: ///< a property of an instance - case PROPERTY_HINT_PROPERTY_OF_SCRIPT: ///< a property of a script & base - case PROPERTY_HINT_TYPE_STRING: { - - item->set_cell_mode(1, TreeItem::CELL_MODE_CUSTOM); - item->set_editable(1, !read_only); - if (show_type_icons) - item->set_icon(0, get_icon("String", "EditorIcons")); - item->set_text(1, obj->get(p.name)); - - } break; - - default: { - - item->set_cell_mode(1, TreeItem::CELL_MODE_STRING); - item->set_editable(1, !read_only); - if (show_type_icons) - item->set_icon(0, get_icon("String", "EditorIcons")); - item->set_text(1, obj->get(p.name)); - if (p.hint == PROPERTY_HINT_MULTILINE_TEXT) - item->add_button(1, get_icon("MultiLine", "EditorIcons")); - - } break; - } - - } break; - case Variant::ARRAY: { - - item->set_cell_mode(1, TreeItem::CELL_MODE_CUSTOM); - item->add_button(1, get_icon("EditResource", "EditorIcons")); - - Variant v = obj->get(p.name); - String type_name = "Array"; - String type_name_suffix = ""; - - String hint = p.hint_string; - while (hint.begins_with(itos(Variant::ARRAY) + ":")) { - type_name += "<Array"; - type_name_suffix += ">"; - hint = hint.right(2); - } - if (hint.find(":") >= 0) { - int colon_pos = hint.find(":"); - String hint_string = hint.right(colon_pos + 1); - hint = hint.left(colon_pos); - - PropertyHint property_hint = PROPERTY_HINT_NONE; - - if (hint.find("/") >= 0) { - int slash_pos = hint.find("/"); - property_hint = PropertyHint(hint.right(slash_pos + 1).to_int()); - hint = hint.left(slash_pos); - } - - if (property_hint == PROPERTY_HINT_RESOURCE_TYPE) { - type_name += "<" + hint_string; - } else { - type_name += "<" + Variant::get_type_name(Variant::Type(hint.to_int())); - } - type_name_suffix += ">"; - } - type_name += type_name_suffix; - - if (v.is_array()) - item->set_text(1, type_name + "(" + itos(v.call("size")) + ")"); - else - item->set_text(1, type_name + "()"); - - if (show_type_icons) - item->set_icon(0, get_icon("PoolByteArray", "EditorIcons")); - - } break; - case Variant::DICTIONARY: { - - Variant v = obj->get(p.name); - - item->set_cell_mode(1, TreeItem::CELL_MODE_STRING); - item->set_text(1, String("Dictionary{") + itos(v.call("size")) + "}"); - item->add_button(1, get_icon("EditResource", "EditorIcons")); - - if (show_type_icons) - item->set_icon(0, get_icon("Dictionary", "EditorIcons")); - - } break; - - case Variant::POOL_INT_ARRAY: { - - item->set_cell_mode(1, TreeItem::CELL_MODE_CUSTOM); - item->add_button(1, get_icon("EditResource", "EditorIcons")); - - Variant v = obj->get(p.name); - if (v.is_array()) - item->set_text(1, "IntArray[" + itos(v.call("size")) + "]"); - else - item->set_text(1, "IntArray[]"); - if (show_type_icons) - item->set_icon(0, get_icon("PoolIntArray", "EditorIcons")); - - } break; - case Variant::POOL_REAL_ARRAY: { - - item->set_cell_mode(1, TreeItem::CELL_MODE_CUSTOM); - item->add_button(1, get_icon("EditResource", "EditorIcons")); - - Variant v = obj->get(p.name); - if (v.is_array()) - item->set_text(1, "FloatArray[" + itos(v.call("size")) + "]"); - else - item->set_text(1, "FloatArray[]"); - if (show_type_icons) - item->set_icon(0, get_icon("PoolRealArray", "EditorIcons")); - - } break; - case Variant::POOL_STRING_ARRAY: { - - item->set_cell_mode(1, TreeItem::CELL_MODE_CUSTOM); - item->add_button(1, get_icon("EditResource", "EditorIcons")); - - Variant v = obj->get(p.name); - if (v.is_array()) - item->set_text(1, "String[" + itos(v.call("size")) + "]"); - else - item->set_text(1, "String[]"); - if (show_type_icons) - item->set_icon(0, get_icon("PoolStringArray", "EditorIcons")); - - } break; - case Variant::POOL_BYTE_ARRAY: { - - item->set_cell_mode(1, TreeItem::CELL_MODE_CUSTOM); - item->add_button(1, get_icon("EditResource", "EditorIcons")); - - Variant v = obj->get(p.name); - if (v.is_array()) - item->set_text(1, "Byte[" + itos(v.call("size")) + "]"); - else - item->set_text(1, "Byte[]"); - if (show_type_icons) - item->set_icon(0, get_icon("PoolByteArray", "EditorIcons")); - - } break; - case Variant::POOL_VECTOR2_ARRAY: { - - item->set_cell_mode(1, TreeItem::CELL_MODE_CUSTOM); - item->add_button(1, get_icon("EditResource", "EditorIcons")); - - Variant v = obj->get(p.name); - if (v.is_array()) - item->set_text(1, "Vector2[" + itos(v.call("size")) + "]"); - else - item->set_text(1, "Vector2[]"); - if (show_type_icons) - item->set_icon(0, get_icon("Vector2", "EditorIcons")); - - } break; - case Variant::POOL_VECTOR3_ARRAY: { - - item->set_cell_mode(1, TreeItem::CELL_MODE_CUSTOM); - item->add_button(1, get_icon("EditResource", "EditorIcons")); - - Variant v = obj->get(p.name); - if (v.is_array()) - item->set_text(1, "Vector3[" + itos(v.call("size")) + "]"); - else - item->set_text(1, "Vector3[]"); - if (show_type_icons) - item->set_icon(0, get_icon("Vector3", "EditorIcons")); - - } break; - case Variant::POOL_COLOR_ARRAY: { - - item->set_cell_mode(1, TreeItem::CELL_MODE_CUSTOM); - item->add_button(1, get_icon("EditResource", "EditorIcons")); - - Variant v = obj->get(p.name); - if (v.is_array()) - item->set_text(1, "Color[" + itos(v.call("size")) + "]"); - else - item->set_text(1, "Color[]"); - if (show_type_icons) - item->set_icon(0, get_icon("Color", "EditorIcons")); - - } break; - case Variant::VECTOR2: { - - item->set_cell_mode(1, TreeItem::CELL_MODE_CUSTOM); - item->set_editable(1, true); - item->set_text(1, obj->get(p.name)); - if (show_type_icons) - item->set_icon(0, get_icon("Vector2", "EditorIcons")); - - } break; - case Variant::RECT2: { - - item->set_cell_mode(1, TreeItem::CELL_MODE_CUSTOM); - item->set_editable(1, true); - item->set_text(1, obj->get(p.name)); - if (show_type_icons) - item->set_icon(0, get_icon("Rect2", "EditorIcons")); - - } break; - case Variant::VECTOR3: { - - item->set_cell_mode(1, TreeItem::CELL_MODE_CUSTOM); - item->set_editable(1, true); - item->set_text(1, obj->get(p.name)); - if (show_type_icons) - item->set_icon(0, get_icon("Vector3", "EditorIcons")); - - } break; - case Variant::TRANSFORM2D: - case Variant::BASIS: { - - item->set_cell_mode(1, TreeItem::CELL_MODE_CUSTOM); - item->set_editable(1, true); - item->set_text(1, obj->get(p.name)); - - } break; - case Variant::TRANSFORM: { - - item->set_cell_mode(1, TreeItem::CELL_MODE_CUSTOM); - item->set_editable(1, true); - item->set_text(1, obj->get(p.name)); - if (show_type_icons) - item->set_icon(0, get_icon("Transform", "EditorIcons")); - - } break; - case Variant::PLANE: { - - item->set_cell_mode(1, TreeItem::CELL_MODE_CUSTOM); - item->set_editable(1, true); - item->set_text(1, obj->get(p.name)); - if (show_type_icons) - item->set_icon(0, get_icon("Plane", "EditorIcons")); - - } break; - case Variant::AABB: { - - item->set_cell_mode(1, TreeItem::CELL_MODE_CUSTOM); - item->set_editable(1, true); - item->set_text(1, "AABB"); - if (show_type_icons) - item->set_icon(0, get_icon("AABB", "EditorIcons")); - - } break; - - case Variant::QUAT: { - - item->set_cell_mode(1, TreeItem::CELL_MODE_CUSTOM); - item->set_editable(1, true); - item->set_text(1, obj->get(p.name)); - if (show_type_icons) - item->set_icon(0, get_icon("Quat", "EditorIcons")); - - } break; - case Variant::COLOR: { - - item->set_cell_mode(1, TreeItem::CELL_MODE_CUSTOM); - item->set_editable(1, !read_only); - item->set_custom_draw(1, this, "_draw_transparency"); - if (show_type_icons) - item->set_icon(0, get_icon("Color", "EditorIcons")); - - } break; - - case Variant::NODE_PATH: { - - item->set_cell_mode(1, TreeItem::CELL_MODE_STRING); - item->set_editable(1, !read_only); - item->set_text(1, obj->get(p.name)); - item->add_button(1, get_icon("CopyNodePath", "EditorIcons")); - if (show_type_icons) - item->set_icon(0, get_icon("NodePath", "EditorIcons")); - - } break; - case Variant::OBJECT: { - - item->set_cell_mode(1, TreeItem::CELL_MODE_CUSTOM); - item->set_editable(1, !read_only); - String type; - if (p.hint == PROPERTY_HINT_RESOURCE_TYPE) - type = p.hint_string; - - RES res = obj->get(p.name).operator RefPtr(); - if (type.begins_with("RES:") && type != "RES:") { // Remote resources - res = ResourceLoader::load(type.substr(4, type.length())); - } - Ref<EncodedObjectAsID> encoded = obj->get(p.name); //for debugger and remote tools - - if (encoded.is_valid()) { - - item->set_text(1, "Object: " + itos(encoded->get_object_id())); - item->set_icon(1, Ref<Texture>()); - item->set_custom_as_button(1, true); - item->set_editable(1, true); - - } else if (obj->get(p.name).get_type() == Variant::NIL || res.is_null()) { - - item->set_text(1, "<null>"); - item->set_icon(1, Ref<Texture>()); - item->set_custom_as_button(1, false); - - } else if (res.is_valid()) { - - item->set_custom_as_button(1, true); - - if (res->is_class("Texture")) { - int tw = EditorSettings::get_singleton()->get("docks/property_editor/texture_preview_width"); - Vector2 size(res->call("get_width"), res->call("get_height")); - if (size.width < size.height) { - tw = MAX((size.width / size.height) * tw, 1); - } - item->set_icon_max_width(1, tw); - item->set_icon(1, res); - item->set_text(1, ""); - - } else if (res->get_name() != "") { - - item->set_text(1, res->get_name()); - } else if (res->get_path() != "" && !res->get_path().begins_with("local://")) { - item->set_text(1, res->get_path().get_file()); - - } else { - item->set_text(1, "<" + res->get_class() + ">"); - } - - if (has_icon(res->get_class(), "EditorIcons")) { - type = res->get_class(); - } - - if (res.is_valid() && res->get_path().is_resource_file()) { - item->set_tooltip(1, res->get_path()); - } else if (res.is_valid()) { - item->set_tooltip(1, res->get_name() + " (" + res->get_class() + ")"); - } - if (res->is_class("Script")) { - item->set_text(1, res->get_path().get_file()); - } else if (!res->is_class("Texture")) { - //texture already previews via itself - EditorResourcePreview::get_singleton()->queue_edited_resource_preview(res, this, "_resource_preview_done", item->get_instance_id()); - } - } - - if (type != String()) { - if (type.find(",") != -1) - type = type.get_slice(",", 0); - if (has_icon(type, "EditorIcons")) - item->set_icon(0, get_icon(type, "EditorIcons")); - else - item->set_icon(0, get_icon("Object", "EditorIcons")); - } - - } break; - default: {}; - } - - if (keying) { - - if (p.hint == PROPERTY_HINT_SPRITE_FRAME) { - - item->add_button(1, get_icon("KeyNext", "EditorIcons"), 5); - } else { - item->add_button(1, get_icon("Key", "EditorIcons"), 2); - } - } - - bool has_reload = false; - - if (_is_instanced_node_with_original_property_different(p.name, item)) { - item->add_button(1, get_icon("ReloadSmall", "EditorIcons"), 3); - has_reload = true; - } - - if (obj->call("property_can_revert", p.name).operator bool()) { - - item->add_button(1, get_icon("ReloadSmall", "EditorIcons"), 3); - has_reload = true; - } - - if (!has_reload && !obj->get_script().is_null()) { - Ref<Script> scr = obj->get_script(); - Variant orig_value; - if (scr->get_property_default_value(p.name, orig_value)) { - if (orig_value != obj->get(p.name)) { - item->add_button(1, get_icon("ReloadSmall", "EditorIcons"), 3); - has_reload = true; - } - } - } - - if (_might_be_in_instance() && !has_reload && item->get_cell_mode(1) == TreeItem::CELL_MODE_RANGE && item->get_text(1) == String()) { - item->add_button(1, get_icon("ReloadEmpty", "EditorIcons"), 3, true); - } - } -} - -void PropertyEditor::_draw_transparency(Object *t, const Rect2 &p_rect) { - - TreeItem *ti = Object::cast_to<TreeItem>(t); - if (!ti) - return; - - Color color = obj->get(ti->get_metadata(1)); - Ref<Texture> arrow = tree->get_icon("select_option"); - - // make a little space between consecutive color fields - Rect2 area = p_rect; - area.position.y += 1; - area.size.height -= 2; - area.size.width -= arrow->get_size().width + 5; - tree->draw_texture_rect(get_icon("GuiMiniCheckerboard", "EditorIcons"), area, true); - tree->draw_rect(area, color); -} - -void PropertyEditor::_item_folded(Object *item_obj) { - - if (updating_folding) - return; - - TreeItem *item = Object::cast_to<TreeItem>(item_obj); - - obj->editor_set_section_unfold(item->get_metadata(0), !item->is_collapsed()); -} - -void PropertyEditor::_item_selected() { - - TreeItem *item = tree->get_selected(); - ERR_FAIL_COND(!item); - selected_property = item->get_metadata(1); -} - -void PropertyEditor::_item_rmb_edited() { - _custom_editor_request(true); -} - -void PropertyEditor::_edit_set(const String &p_name, const Variant &p_value, bool p_refresh_all, const String &p_changed_field) { - - if (autoclear) { - TreeItem *item = tree->get_selected(); - if (item && item->get_cell_mode(0) == TreeItem::CELL_MODE_CHECK) { - - item->set_checked(0, true); - } - } - - if (!undo_redo || Object::cast_to<ArrayPropertyEdit>(obj) || Object::cast_to<DictionaryPropertyEdit>(obj)) { //kind of hacky - - obj->set(p_name, p_value); - if (p_refresh_all) - _changed_callbacks(obj, ""); - else - _changed_callbacks(obj, p_name); - - emit_signal(_prop_edited, p_name); - - } else if (Object::cast_to<MultiNodeEdit>(obj)) { - - Object::cast_to<MultiNodeEdit>(obj)->set_property_field(p_name, p_value, p_changed_field); - _changed_callbacks(obj, p_name); - emit_signal(_prop_edited, p_name); - } else { - - undo_redo->create_action(TTR("Set") + " " + p_name, UndoRedo::MERGE_ENDS); - undo_redo->add_do_property(obj, p_name, p_value); - undo_redo->add_undo_property(obj, p_name, obj->get(p_name)); - - if (p_refresh_all) { - undo_redo->add_do_method(this, "_changed_callback", obj, ""); - undo_redo->add_undo_method(this, "_changed_callback", obj, ""); - } else { - - undo_redo->add_do_method(this, "_changed_callback", obj, p_name); - undo_redo->add_undo_method(this, "_changed_callback", obj, p_name); - } - - Resource *r = Object::cast_to<Resource>(obj); - if (r) { - if (!r->is_edited() && String(p_name) != "resource/edited") { - undo_redo->add_do_method(r, "set_edited", true); - undo_redo->add_undo_method(r, "set_edited", false); - } - - if (String(p_name) == "resource_local_to_scene") { - bool prev = obj->get(p_name); - bool next = p_value; - if (next) { - undo_redo->add_do_method(this, "setup_local_to_scene"); - } - if (prev) { - undo_redo->add_undo_method(this, "setup_local_to_scene"); - } - } - } - undo_redo->add_do_method(this, "emit_signal", _prop_edited, p_name); - undo_redo->add_undo_method(this, "emit_signal", _prop_edited, p_name); - undo_redo->commit_action(); - } -} - -void PropertyEditor::_item_edited() { - - TreeItem *item = tree->get_edited(); - if (!item) - return; //it all happened too fast.. - - Dictionary d = item->get_metadata(0); - - String name = d["name"]; - - if (tree->get_edited_column() == 0) { - //property checked - if (autoclear) { - if (!item->is_checked(0)) { - obj->set(name, Variant()); - update_property(name); - } else { - Variant::CallError ce; - obj->set(name, Variant::construct(Variant::Type(int(d["type"])), NULL, 0, ce)); - } - } else { - emit_signal("property_toggled", name, item->is_checked(0)); - } - return; - } - - if (autoclear && item->get_cell_mode(0) == TreeItem::CELL_MODE_CHECK && item->get_cell_mode(1) != TreeItem::CELL_MODE_CUSTOM) { - item->set_checked(0, true); - } - - int type = d["type"]; - int hint = d["hint"]; - int usage = d["usage"]; - bool refresh_all = usage & PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED; - - String hint_text = d["hint_text"]; - switch (type) { - - case Variant::NIL: { - - } break; - case Variant::BOOL: { - - item->set_tooltip(1, item->is_checked(1) ? "True" : "False"); - _edit_set(name, item->is_checked(1), refresh_all); - } break; - case Variant::INT: - case Variant::REAL: { - - if (hint == PROPERTY_HINT_LAYERS_2D_PHYSICS || hint == PROPERTY_HINT_LAYERS_2D_RENDER || hint == PROPERTY_HINT_LAYERS_3D_PHYSICS || hint == PROPERTY_HINT_LAYERS_3D_RENDER) - break; - if (hint == PROPERTY_HINT_EXP_EASING) - break; - if (hint == PROPERTY_HINT_FLAGS) - break; - - if (type == Variant::INT) - _edit_set(name, int64_t(round(item->get_range(1))), refresh_all); - else - _edit_set(name, item->get_range(1), refresh_all); - } break; - case Variant::STRING: { - - if (hint == PROPERTY_HINT_ENUM) { - - int idx = item->get_range(1); - - Vector<String> strings = hint_text.split(","); - String txt; - if (idx >= 0 && idx < strings.size()) { - - txt = strings[idx]; - } - - _edit_set(name, txt, refresh_all); - } else { - _edit_set(name, item->get_text(1), refresh_all); - } - } break; - - // math types - case Variant::VECTOR3: { - - } break; - case Variant::PLANE: { - - } break; - case Variant::QUAT: { - - } break; - case Variant::AABB: { - - } break; - case Variant::BASIS: { - - } break; - case Variant::TRANSFORM: { - - } break; - case Variant::COLOR: { - - } break; - - case Variant::NODE_PATH: { - _edit_set(name, NodePath(item->get_text(1)), refresh_all); - - } break; - case Variant::OBJECT: { - if (!item->is_custom_set_as_button(1)) - break; - - Ref<EncodedObjectAsID> encoded = obj->get(name); //for debugger and remote tools - - if (encoded.is_valid()) { - - emit_signal("object_id_selected", encoded->get_object_id()); - } - - RES res = obj->get(name); - if (res.is_valid()) { - emit_signal("resource_selected", res.get_ref_ptr(), name); - } - - } break; - - case Variant::DICTIONARY: { - - } break; - - // arrays - case Variant::POOL_BYTE_ARRAY: { - - } break; - case Variant::POOL_INT_ARRAY: { - - } break; - case Variant::POOL_REAL_ARRAY: { - - } break; - case Variant::POOL_STRING_ARRAY: { - - } break; - case Variant::POOL_VECTOR3_ARRAY: { - - } break; - case Variant::POOL_COLOR_ARRAY: { - - } break; - }; -} - -void PropertyEditor::_resource_edit_request() { - - RES res = custom_editor->get_variant(); - if (res.is_null()) - return; - - String name = custom_editor->get_name(); - - emit_signal("resource_selected", res.get_ref_ptr(), name); -} - -void PropertyEditor::_custom_editor_edited() { - - if (!obj) - return; - - _edit_set(custom_editor->get_name(), custom_editor->get_variant()); -} - -void PropertyEditor::_custom_editor_edited_field(const String &p_field_name) { - - ERR_FAIL_COND(p_field_name == ""); - - if (!obj) - return; - - _edit_set(custom_editor->get_name(), custom_editor->get_variant(), false, p_field_name); -} - -void PropertyEditor::_custom_editor_request(bool p_arrow) { - - TreeItem *item = tree->get_edited(); - ERR_FAIL_COND(!item); - Dictionary d = item->get_metadata(0); - - String name = d["name"]; - Variant::Type type = Variant::NIL; - if (d.has("type")) - type = (Variant::Type)((int)(d["type"])); - - Variant v = obj->get(name); - int hint = d.has("hint") ? d["hint"].operator int() : -1; - String hint_text = d.has("hint_text") ? d["hint_text"] : ""; - Rect2 where = tree->get_custom_popup_rect(); - custom_editor->set_position(where.position); - - if (custom_editor->edit(obj, name, type, v, hint, hint_text)) { - custom_editor->popup(); - } -} - -void PropertyEditor::edit(Object *p_object) { - - if (obj == p_object) - return; - if (obj) { - - obj->remove_change_receptor(this); - } - - obj = p_object; - - evaluator->edit(p_object); - - update_tree(); - - if (obj) { - - obj->add_change_receptor(this); - } -} - -void PropertyEditor::_set_range_def(Object *p_item, String prop, float p_frame) { - - TreeItem *ti = Object::cast_to<TreeItem>(p_item); - ERR_FAIL_COND(!ti); - - ti->call_deferred("set_range", 1, p_frame); - obj->call_deferred("set", prop, p_frame); -} - -void PropertyEditor::_edit_button(Object *p_item, int p_column, int p_button) { - - TreeItem *ti = Object::cast_to<TreeItem>(p_item); - ERR_FAIL_COND(!ti); - - Dictionary d = ti->get_metadata(0); - - if (p_button == 2) { - - if (!d.has("name")) - return; - String prop = d["name"]; - emit_signal("property_keyed", prop, obj->get(prop), false); - } else if (p_button == 5) { - if (!d.has("name")) - return; - String prop = d["name"]; - emit_signal("property_keyed", prop, obj->get(prop), true); - call_deferred("_set_range_def", ti, prop, ti->get_range(p_column) + 1.0); - } else if (p_button == 3) { - - if (!d.has("name")) - return; - - String prop = d["name"]; - - Variant vorig; - - if (_might_be_in_instance() && _get_instanced_node_original_property(prop, vorig)) { - - _edit_set(prop, vorig.duplicate(true)); // Set, making sure to duplicate arrays properly - return; - } - - if (obj->call("property_can_revert", prop).operator bool()) { - Variant rev = obj->call("property_get_revert", prop); - _edit_set(prop, rev); - } - - if (!obj->get_script().is_null()) { - Ref<Script> scr = obj->get_script(); - Variant orig_value; - if (scr->get_property_default_value(prop, orig_value)) { - _edit_set(prop, orig_value); - } - } - - } else { - - Dictionary d = ti->get_metadata(0); - if (!d.has("type")) - return; - if (!d.has("hint")) - return; - if (!d.has("name")) - return; - if (!d.has("hint_text")) - return; - - int t = d["type"]; - int h = d["hint"]; - String n = d["name"]; - String ht = d["hint_text"]; - - if (t == Variant::NODE_PATH) { - - Variant v = obj->get(n); - Rect2 where = tree->get_item_rect(ti, 1); - where.position -= tree->get_scroll(); - where.position += tree->get_global_position() + Point2(where.size.width, 0); - for (int i = ti->get_button_count(p_column) - 1; i >= p_button; i--) - where.position.x -= ti->get_button(p_column, i)->get_width(); - custom_editor->set_position(where.position); - custom_editor->edit(obj, n, (Variant::Type)t, v, h, ht); - custom_editor->popup(); - - } else if (t == Variant::STRING) { - - Variant v = obj->get(n); - custom_editor->edit(obj, n, (Variant::Type)t, v, h, ht); - if (h == PROPERTY_HINT_FILE || h == PROPERTY_HINT_DIR || h == PROPERTY_HINT_GLOBAL_DIR || h == PROPERTY_HINT_GLOBAL_FILE) { - - Rect2 where = tree->get_item_rect(ti, 1); - where.position -= tree->get_scroll(); - where.position += tree->get_global_position() + Point2(where.size.width, 0); - for (int i = ti->get_button_count(p_column) - 1; i >= p_button; i--) - where.position.x -= ti->get_button(p_column, i)->get_width(); - custom_editor->set_position(where.position); - custom_editor->popup(); - } else { - custom_editor->popup_centered_ratio(); - } - - } else if (t == Variant::OBJECT) { - - RES r = obj->get(n); - if (r.is_valid()) { - - emit_signal("resource_selected", r, n); - } - } else if (t == Variant::INT && h == PROPERTY_HINT_OBJECT_ID) { - - emit_signal("object_id_selected", obj->get(n)); - - } else if (t == Variant::ARRAY || t == Variant::POOL_INT_ARRAY || t == Variant::POOL_REAL_ARRAY || t == Variant::POOL_STRING_ARRAY || t == Variant::POOL_VECTOR2_ARRAY || t == Variant::POOL_VECTOR3_ARRAY || t == Variant::POOL_COLOR_ARRAY || t == Variant::POOL_BYTE_ARRAY) { - - Variant v = obj->get(n); - - if (v.get_type() != t) { - Variant::CallError ce; - v = Variant::construct(Variant::Type(t), NULL, 0, ce); - } - - Ref<ArrayPropertyEdit> ape = memnew(ArrayPropertyEdit); - ape->edit(obj, n, ht, Variant::Type(t)); - EditorNode::get_singleton()->push_item(ape.ptr()); - - } else if (t == Variant::DICTIONARY) { - - Variant v = obj->get(n); - - if (v.get_type() != t) { - Variant::CallError ce; - v = Variant::construct(Variant::Type(t), NULL, 0, ce); - } - - Ref<DictionaryPropertyEdit> dpe = memnew(DictionaryPropertyEdit); - dpe->edit(obj, n); - EditorNode::get_singleton()->push_item(dpe.ptr()); - } - } -} - -void PropertyEditor::_node_removed(Node *p_node) { - - if (p_node == obj) { - edit(NULL); - } -} - -void PropertyEditor::set_keying(bool p_active) { - - if (keying == p_active) - return; - - keying = p_active; - update_tree(); -} - -void PropertyEditor::_draw_flags(Object *p_object, const Rect2 &p_rect) { - - TreeItem *ti = Object::cast_to<TreeItem>(p_object); - if (!ti) - return; - - Dictionary d = ti->get_metadata(0); - - if (!d.has("name")) - return; - - uint32_t f = obj->get(d["name"]); - - int bsize = (p_rect.size.height * 80 / 100) / 2; - - int h = bsize * 2 + 1; - int vofs = (p_rect.size.height - h) / 2; - - for (int i = 0; i < 2; i++) { - - Point2 ofs(4, vofs); - if (i == 1) - ofs.y += bsize + 1; - - ofs += p_rect.position; - for (int j = 0; j < 10; j++) { - - Point2 o = ofs + Point2(j * (bsize + 1), 0); - if (j >= 5) - o.x += 1; - - uint32_t idx = i * 10 + j; - bool on = f & (1 << idx); - tree->draw_rect(Rect2(o, Size2(bsize, bsize)), Color(0, 0, 0, on ? 0.8 : 0.3)); - } - } -} - -void PropertyEditor::_filter_changed(const String &p_text) { - - update_tree(); -} - -void PropertyEditor::_resource_preview_done(const String &p_path, const Ref<Texture> &p_preview, Variant p_ud) { - - if (p_preview.is_null()) - return; //don't bother with empty preview - - ObjectID id = p_ud; - Object *obj = ObjectDB::get_instance(id); - - if (!obj) - return; - - TreeItem *ti = Object::cast_to<TreeItem>(obj); - - ERR_FAIL_COND(!ti); - - int tw = EditorSettings::get_singleton()->get("docks/property_editor/texture_preview_width"); - - ti->set_icon(1, p_preview); //should be scaled I think? - ti->set_icon_max_width(1, tw); - ti->set_text(1, ""); -} -void PropertyEditor::_bind_methods() { - - ClassDB::bind_method("_item_edited", &PropertyEditor::_item_edited); - ClassDB::bind_method("_item_selected", &PropertyEditor::_item_selected); - ClassDB::bind_method("_item_rmb_edited", &PropertyEditor::_item_rmb_edited); - ClassDB::bind_method("_item_folded", &PropertyEditor::_item_folded); - ClassDB::bind_method("_custom_editor_request", &PropertyEditor::_custom_editor_request); - ClassDB::bind_method("_custom_editor_edited", &PropertyEditor::_custom_editor_edited); - ClassDB::bind_method("_custom_editor_edited_field", &PropertyEditor::_custom_editor_edited_field, DEFVAL("")); - ClassDB::bind_method("_resource_edit_request", &PropertyEditor::_resource_edit_request); - ClassDB::bind_method("_node_removed", &PropertyEditor::_node_removed); - ClassDB::bind_method("_edit_button", &PropertyEditor::_edit_button); - ClassDB::bind_method("_changed_callback", &PropertyEditor::_changed_callbacks); - ClassDB::bind_method("_draw_flags", &PropertyEditor::_draw_flags); - ClassDB::bind_method("_set_range_def", &PropertyEditor::_set_range_def); - ClassDB::bind_method("_filter_changed", &PropertyEditor::_filter_changed); - ClassDB::bind_method("update_tree", &PropertyEditor::update_tree); - ClassDB::bind_method("_resource_preview_done", &PropertyEditor::_resource_preview_done); - ClassDB::bind_method("refresh", &PropertyEditor::refresh); - ClassDB::bind_method("_draw_transparency", &PropertyEditor::_draw_transparency); - ClassDB::bind_method("edit", &PropertyEditor::edit); - - ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &PropertyEditor::get_drag_data_fw); - ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &PropertyEditor::can_drop_data_fw); - ClassDB::bind_method(D_METHOD("drop_data_fw"), &PropertyEditor::drop_data_fw); - - ADD_SIGNAL(MethodInfo("property_toggled", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::BOOL, "value"))); - ADD_SIGNAL(MethodInfo("resource_selected", PropertyInfo(Variant::OBJECT, "res"), PropertyInfo(Variant::STRING, "prop"))); - ADD_SIGNAL(MethodInfo("object_id_selected", PropertyInfo(Variant::INT, "id"))); - ADD_SIGNAL(MethodInfo("property_keyed", PropertyInfo(Variant::STRING, "property"))); - ADD_SIGNAL(MethodInfo("property_edited", PropertyInfo(Variant::STRING, "property"))); -} - -Tree *PropertyEditor::get_property_tree() { - - return tree; -} - -Label *PropertyEditor::get_top_label() { - - return top_label; -} - -void PropertyEditor::hide_top_label() { - - top_label->hide(); - tree->set_begin(Point2(0, 0)); -} - -String PropertyEditor::get_selected_path() const { - - TreeItem *ti = tree->get_selected(); - if (!ti) - return ""; - - Dictionary d = ti->get_metadata(0); - - if (d.has("name")) - return d["name"]; - else - return ""; -} - -bool PropertyEditor::is_capitalize_paths_enabled() const { - - return capitalize_paths; -} - -void PropertyEditor::set_enable_capitalize_paths(bool p_capitalize) { - - capitalize_paths = p_capitalize; - update_tree_pending = true; -} - -void PropertyEditor::set_autoclear(bool p_enable) { - - autoclear = p_enable; -} - -void PropertyEditor::set_show_categories(bool p_show) { - - show_categories = p_show; - update_tree(); -} - -void PropertyEditor::set_use_filter(bool p_use) { - - if (p_use == use_filter) - return; - - use_filter = p_use; - update_tree(); -} - -void PropertyEditor::register_text_enter(Node *p_line_edit) { - - ERR_FAIL_NULL(p_line_edit); - search_box = Object::cast_to<LineEdit>(p_line_edit); - - if (search_box) - search_box->connect("text_changed", this, "_filter_changed"); -} - -void PropertyEditor::set_property_selectable(bool p_selectable) { - property_selectable = p_selectable; - update_tree(); -} - -void PropertyEditor::set_subsection_selectable(bool p_selectable) { - - if (p_selectable == subsection_selectable) - return; - - subsection_selectable = p_selectable; - update_tree(); -} - -void PropertyEditor::set_use_folding(bool p_enable) { - - use_folding = p_enable; - tree->set_hide_folding(false); -} - -void PropertyEditor::collapse_all_folding() { - if (!obj) - return; - for (List<String>::Element *E = foldable_property_cache.front(); E; E = E->next()) { - obj->editor_set_section_unfold(E->get(), false); - } - update_tree(); -} - -void PropertyEditor::expand_all_folding() { - - if (!obj) - return; - for (List<String>::Element *E = foldable_property_cache.front(); E; E = E->next()) { - obj->editor_set_section_unfold(E->get(), true); - } - update_tree(); -} - -PropertyEditor::PropertyEditor() { - - _prop_edited = "property_edited"; - - hide_script = true; - use_folding = false; - - undo_redo = NULL; - obj = NULL; - search_box = NULL; - changing = false; - update_tree_pending = false; - - top_label = memnew(Label); - top_label->set_text(TTR("Properties:")); - top_label->set_anchor(MARGIN_RIGHT, ANCHOR_END); - top_label->set_begin(Point2(10, 0)); - top_label->set_end(Point2(0, 12)); - - add_child(top_label); - - tree = memnew(Tree); - tree->set_anchor(MARGIN_RIGHT, ANCHOR_END); - tree->set_anchor(MARGIN_BOTTOM, ANCHOR_END); - tree->set_begin(Point2(0, 19)); - tree->set_end(Point2(0, 0)); - - tree->set_columns(2); - tree->set_column_expand(0, true); - tree->set_column_min_width(0, 30); - tree->set_column_expand(1, true); - tree->set_column_min_width(1, 18); - add_child(tree); - - tree->connect("item_edited", this, "_item_edited", varray(), CONNECT_DEFERRED); - tree->connect("item_rmb_edited", this, "_item_rmb_edited"); - tree->connect("cell_selected", this, "_item_selected"); - tree->connect("item_collapsed", this, "_item_folded"); - - tree->set_drag_forwarding(this); - - set_physics_process(true); - - custom_editor = memnew(CustomPropertyEditor); - custom_editor->set_pass_on_modal_close_click(false); - add_child(custom_editor); - - tree->connect("custom_popup_edited", this, "_custom_editor_request"); - tree->connect("button_pressed", this, "_edit_button"); - custom_editor->connect("variant_changed", this, "_custom_editor_edited"); - custom_editor->connect("variant_field_changed", this, "_custom_editor_edited_field"); - custom_editor->connect("resource_edit_request", this, "_resource_edit_request", make_binds(), CONNECT_DEFERRED); - tree->set_hide_folding(true); - - evaluator = memnew(PropertyValueEvaluator); - tree->set_value_evaluator(evaluator); - custom_editor->set_value_evaluator(evaluator); - - capitalize_paths = true; - autoclear = false; - tree->set_column_titles_visible(false); - tree->add_constant_override("button_margin", 0); - - keying = false; - read_only = false; - show_categories = false; - refresh_countdown = 0; - use_doc_hints = false; - updating_folding = true; - use_filter = false; - subsection_selectable = false; - property_selectable = false; - show_type_icons = false; // TODO: need to reimplement it to work with the new inspector -} - -PropertyEditor::~PropertyEditor() { - memdelete(evaluator); -} - -///////////////////////////// - -class SectionedPropertyEditorFilter : public Object { - - GDCLASS(SectionedPropertyEditorFilter, Object); - - Object *edited; - String section; - bool allow_sub; - - bool _set(const StringName &p_name, const Variant &p_value) { - - if (!edited) - return false; - - String name = p_name; - if (section != "") { - name = section + "/" + name; - } - - bool valid; - edited->set(name, p_value, &valid); - return valid; - } - - bool _get(const StringName &p_name, Variant &r_ret) const { - - if (!edited) - return false; - - String name = p_name; - if (section != "") { - name = section + "/" + name; - } - - bool valid = false; - - r_ret = edited->get(name, &valid); - return valid; - } - void _get_property_list(List<PropertyInfo> *p_list) const { - - if (!edited) - return; - - List<PropertyInfo> pinfo; - edited->get_property_list(&pinfo); - for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) { - - PropertyInfo pi = E->get(); - int sp = pi.name.find("/"); - - if (pi.name == "resource_path" || pi.name == "resource_name" || pi.name == "resource_local_to_scene" || pi.name.begins_with("script/")) //skip resource stuff - continue; - - if (sp == -1) { - pi.name = "global/" + pi.name; - } - - if (pi.name.begins_with(section + "/")) { - pi.name = pi.name.replace_first(section + "/", ""); - if (!allow_sub && pi.name.find("/") != -1) - continue; - p_list->push_back(pi); - } - } - } - - bool property_can_revert(const String &p_name) { - - return edited->call("property_can_revert", section + "/" + p_name); - } - - Variant property_get_revert(const String &p_name) { - - return edited->call("property_get_revert", section + "/" + p_name); - } - -protected: - static void _bind_methods() { - - ClassDB::bind_method("property_can_revert", &SectionedPropertyEditorFilter::property_can_revert); - ClassDB::bind_method("property_get_revert", &SectionedPropertyEditorFilter::property_get_revert); - } - -public: - void set_section(const String &p_section, bool p_allow_sub) { - - section = p_section; - allow_sub = p_allow_sub; - _change_notify(); - } - - void set_edited(Object *p_edited) { - edited = p_edited; - _change_notify(); - } - - SectionedPropertyEditorFilter() { - edited = NULL; - } -}; - -void SectionedPropertyEditor::_bind_methods() { - - ClassDB::bind_method("_section_selected", &SectionedPropertyEditor::_section_selected); - ClassDB::bind_method("_search_changed", &SectionedPropertyEditor::_search_changed); - - ClassDB::bind_method("update_category_list", &SectionedPropertyEditor::update_category_list); -} - -void SectionedPropertyEditor::_section_selected() { - - if (!sections->get_selected()) - return; - - filter->set_section(sections->get_selected()->get_metadata(0), sections->get_selected()->get_children() == NULL); -} - -void SectionedPropertyEditor::set_current_section(const String &p_section) { - - if (section_map.has(p_section)) { - section_map[p_section]->select(0); - } -} - -String SectionedPropertyEditor::get_current_section() const { - - if (sections->get_selected()) - return sections->get_selected()->get_metadata(0); - else - return ""; -} - -String SectionedPropertyEditor::get_full_item_path(const String &p_item) { - - String base = get_current_section(); - - if (base != "") - return base + "/" + p_item; - else - return p_item; -} - -void SectionedPropertyEditor::edit(Object *p_object) { - - if (!p_object) { - obj = -1; - sections->clear(); - - filter->set_edited(NULL); - editor->edit(NULL); - - return; - } - - ObjectID id = p_object->get_instance_id(); - - if (obj != id) { - - obj = id; - update_category_list(); - - filter->set_edited(p_object); - editor->edit(filter); - - if (sections->get_root()->get_children()) { - sections->get_root()->get_children()->select(0); - } - } else { - - update_category_list(); - } -} - -void SectionedPropertyEditor::update_category_list() { - - String selected_category = get_current_section(); - sections->clear(); - - Object *o = ObjectDB::get_instance(obj); - - if (!o) - return; - - List<PropertyInfo> pinfo; - o->get_property_list(&pinfo); - - section_map.clear(); - - TreeItem *root = sections->create_item(); - section_map[""] = root; - - for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) { - - PropertyInfo pi = E->get(); - - if (pi.usage & PROPERTY_USAGE_CATEGORY) - continue; - else if (!(pi.usage & PROPERTY_USAGE_EDITOR)) - continue; - - if (pi.name.find(":") != -1 || pi.name == "script" || pi.name == "resource_name" || pi.name == "resource_path" || pi.name == "resource_local_to_scene") - continue; - - if (search_box && search_box->get_text() != String() && pi.name.findn(search_box->get_text()) == -1) - continue; - - int sp = pi.name.find("/"); - if (sp == -1) - pi.name = "Global/" + pi.name; - - Vector<String> sectionarr = pi.name.split("/"); - String metasection; - - int sc = MIN(2, sectionarr.size() - 1); - - for (int i = 0; i < sc; i++) { - - TreeItem *parent = section_map[metasection]; - parent->set_custom_bg_color(0, get_color("prop_subsection", "Editor")); - - if (i > 0) { - metasection += "/" + sectionarr[i]; - } else { - metasection = sectionarr[i]; - } - - if (!section_map.has(metasection)) { - TreeItem *ms = sections->create_item(parent); - section_map[metasection] = ms; - ms->set_text(0, sectionarr[i].capitalize()); - ms->set_metadata(0, metasection); - ms->set_selectable(0, false); - } - - if (i == sc - 1) { - //if it has children, make selectable - section_map[metasection]->set_selectable(0, true); - } - } - } - - if (section_map.has(selected_category)) { - section_map[selected_category]->select(0); - } - - editor->update_tree(); -} - -void SectionedPropertyEditor::register_search_box(LineEdit *p_box) { - - search_box = p_box; - editor->register_text_enter(p_box); - search_box->connect("text_changed", this, "_search_changed"); -} - -void SectionedPropertyEditor::_search_changed(const String &p_what) { - - update_category_list(); -} - -PropertyEditor *SectionedPropertyEditor::get_property_editor() { - - return editor; -} - -SectionedPropertyEditor::SectionedPropertyEditor() { - - obj = -1; - - search_box = NULL; - - add_constant_override("autohide", 1); // Fixes the dragger always showing up - - VBoxContainer *left_vb = memnew(VBoxContainer); - left_vb->set_custom_minimum_size(Size2(170, 0) * EDSCALE); - add_child(left_vb); - - sections = memnew(Tree); - sections->set_v_size_flags(SIZE_EXPAND_FILL); - sections->set_hide_root(true); - - left_vb->add_child(sections, true); - - VBoxContainer *right_vb = memnew(VBoxContainer); - right_vb->set_custom_minimum_size(Size2(300, 0) * EDSCALE); - right_vb->set_h_size_flags(SIZE_EXPAND_FILL); - add_child(right_vb); - - filter = memnew(SectionedPropertyEditorFilter); - editor = memnew(PropertyEditor); - editor->set_v_size_flags(SIZE_EXPAND_FILL); - right_vb->add_child(editor, true); - - editor->get_property_tree()->set_column_titles_visible(false); - - editor->hide_top_label(); - - sections->connect("cell_selected", this, "_section_selected"); -} - -SectionedPropertyEditor::~SectionedPropertyEditor() { - - memdelete(filter); -} - -double PropertyValueEvaluator::eval(const String &p_text) { - - // If range value contains a comma replace it with dot (issue #6028) - const String &p_new_text = p_text.replace(",", "."); - - if (!obj || !script_language) - return _default_eval(p_new_text); - - Ref<Script> script = Ref<Script>(script_language->create_script()); - script->set_source_code(_build_script(p_new_text)); - Error err = script->reload(); - if (err) { - ERR_PRINTS("PropertyValueEvaluator: Error loading script for expression: " + p_new_text); - return _default_eval(p_new_text); - } - - Object dummy; - ScriptInstance *script_instance = script->instance_create(&dummy); - if (!script_instance) - return _default_eval(p_new_text); - - Variant::CallError call_err; - Variant arg = obj; - const Variant *args[] = { &arg }; - double result = script_instance->call("eval", args, 1, call_err); - if (call_err.error == Variant::CallError::CALL_OK) { - return result; - } - ERR_PRINTS("PropertyValueEvaluator: Eval failed, error code: " + itos(call_err.error)); - - return _default_eval(p_new_text); -} - -void PropertyValueEvaluator::edit(Object *p_obj) { - obj = p_obj; -} - -String PropertyValueEvaluator::_build_script(const String &p_text) { - String script_text = "tool\nextends Object\nfunc eval(s):\n\tself = s\n\treturn " + p_text.strip_edges() + "\n"; - return script_text; -} - -PropertyValueEvaluator::PropertyValueEvaluator() { - script_language = NULL; - - for (int i = 0; i < ScriptServer::get_language_count(); i++) { - if (ScriptServer::get_language(i)->get_name() == "GDScript") { - script_language = ScriptServer::get_language(i); - } - } -} - -PropertyValueEvaluator::~PropertyValueEvaluator() { -} diff --git a/editor/property_editor.h b/editor/property_editor.h index 7d8fa22f3f..c74103df3d 100644 --- a/editor/property_editor.h +++ b/editor/property_editor.h @@ -135,6 +135,9 @@ class CustomPropertyEditor : public Popup { void _text_edit_changed(); void _file_selected(String p_file); void _modified(String p_string); + + real_t _parse_real_expression(String text); + void _range_modified(double p_value); void _focus_enter(); void _focus_exit(); @@ -168,204 +171,9 @@ public: void set_read_only(bool p_read_only) { read_only = p_read_only; } - void set_value_evaluator(PropertyValueEvaluator *p_evaluator) { evaluator = p_evaluator; } - bool edit(Object *p_owner, const String &p_name, Variant::Type p_type, const Variant &p_variant, int p_hint, String p_hint_text); CustomPropertyEditor(); }; -class PropertyEditor : public Control { - - GDCLASS(PropertyEditor, Control); - - Tree *tree; - Label *top_label; - LineEdit *search_box; - - PropertyValueEvaluator *evaluator; - - Object *obj; - - StringName _prop_edited; - - bool capitalize_paths; - bool changing; - bool update_tree_pending; - bool autoclear; - bool keying; - bool read_only; - bool show_categories; - bool show_type_icons; - float refresh_countdown; - bool use_doc_hints; - bool use_filter; - bool subsection_selectable; - bool hide_script; - bool use_folding; - bool property_selectable; - bool updating_folding; - - List<String> foldable_property_cache; - HashMap<String, String> pending; - String selected_property; - - Map<StringName, Map<StringName, String> > descr_cache; - Map<StringName, String> class_descr_cache; - - CustomPropertyEditor *custom_editor; - - void _resource_edit_request(); - void _custom_editor_edited(); - void _custom_editor_edited_field(const String &p_field_name); - void _custom_editor_request(bool p_arrow); - - void _item_selected(); - void _item_rmb_edited(); - void _item_edited(); - TreeItem *get_parent_node(String p_path, HashMap<String, TreeItem *> &item_paths, TreeItem *root, TreeItem *category); - - void set_item_text(TreeItem *p_item, int p_type, const String &p_name, int p_hint = PROPERTY_HINT_NONE, const String &p_hint_text = ""); - - TreeItem *find_item(TreeItem *p_item, const String &p_name); - - virtual void _changed_callback(Object *p_changed, const char *p_prop); - virtual void _changed_callbacks(Object *p_changed, const String &p_prop); - - void _check_reload_status(const String &p_name, TreeItem *item); - - void _edit_button(Object *p_item, int p_column, int p_button); - - void _node_removed(Node *p_node); - - friend class ProjectExportDialog; - void _edit_set(const String &p_name, const Variant &p_value, bool p_refresh_all = false, const String &p_changed_field = ""); - void _draw_flags(Object *p_object, const Rect2 &p_rect); - - bool _might_be_in_instance(); - bool _get_instanced_node_original_property(const StringName &p_prop, Variant &value); - bool _is_property_different(const Variant &p_current, const Variant &p_orig, int p_usage = 0); - bool _is_instanced_node_with_original_property_different(const String &p_name, TreeItem *item); - - void _refresh_item(TreeItem *p_item); - void _set_range_def(Object *p_item, String prop, float p_frame); - - void _filter_changed(const String &p_text); - - void _mark_drop_fields(TreeItem *p_at); - void _clear_drop_fields(TreeItem *p_at); - - bool _is_drop_valid(const Dictionary &p_drag_data, const Dictionary &p_item_data) const; - 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; - void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); - - void _resource_preview_done(const String &p_path, const Ref<Texture> &p_preview, Variant p_ud); - void _draw_transparency(Object *t, const Rect2 &p_rect); - void _item_folded(Object *item_obj); - - UndoRedo *undo_redo; - -protected: - void _notification(int p_what); - static void _bind_methods(); - -public: - void set_undo_redo(UndoRedo *p_undo_redo) { undo_redo = p_undo_redo; } - - String get_selected_path() const; - - Tree *get_property_tree(); - Label *get_top_label(); - void hide_top_label(); - void update_tree(); - void update_property(const String &p_prop); - - void refresh(); - - void edit(Object *p_object); - - void set_keying(bool p_active); - void set_read_only(bool p_read_only) { - read_only = p_read_only; - custom_editor->set_read_only(p_read_only); - } - - bool is_capitalize_paths_enabled() const; - void set_enable_capitalize_paths(bool p_capitalize); - void set_autoclear(bool p_enable); - - void set_show_categories(bool p_show); - void set_use_doc_hints(bool p_enable) { use_doc_hints = p_enable; } - void set_hide_script(bool p_hide) { hide_script = p_hide; } - - void set_use_filter(bool p_use); - void register_text_enter(Node *p_line_edit); - - void set_subsection_selectable(bool p_selectable); - void set_property_selectable(bool p_selectable); - - void set_use_folding(bool p_enable); - - void collapse_all_folding(); - void expand_all_folding(); - - PropertyEditor(); - ~PropertyEditor(); -}; - -class SectionedPropertyEditorFilter; - -class SectionedPropertyEditor : public HSplitContainer { - - GDCLASS(SectionedPropertyEditor, HSplitContainer); - - ObjectID obj; - - Tree *sections; - SectionedPropertyEditorFilter *filter; - - Map<String, TreeItem *> section_map; - PropertyEditor *editor; - LineEdit *search_box; - - static void _bind_methods(); - void _section_selected(); - - void _search_changed(const String &p_what); - -public: - void register_search_box(LineEdit *p_box); - PropertyEditor *get_property_editor(); - void edit(Object *p_object); - String get_full_item_path(const String &p_item); - - void set_current_section(const String &p_section); - String get_current_section() const; - - void update_category_list(); - - SectionedPropertyEditor(); - ~SectionedPropertyEditor(); -}; - -class PropertyValueEvaluator : public ValueEvaluator { - GDCLASS(PropertyValueEvaluator, ValueEvaluator); - - Object *obj; - ScriptLanguage *script_language; - String _build_script(const String &p_text); - - _FORCE_INLINE_ double _default_eval(const String &p_text) { - return p_text.to_double(); - } - -public: - void edit(Object *p_obj); - double eval(const String &p_text); - - PropertyValueEvaluator(); - ~PropertyValueEvaluator(); -}; - #endif diff --git a/editor/property_selector.cpp b/editor/property_selector.cpp index dae1bdeeb0..a8c97be936 100644 --- a/editor/property_selector.cpp +++ b/editor/property_selector.cpp @@ -30,8 +30,9 @@ #include "property_selector.h" +#include "core/os/keyboard.h" +#include "editor/editor_node.h" #include "editor_scale.h" -#include "os/keyboard.h" void PropertySelector::_text_changed(const String &p_newtext) { @@ -161,10 +162,8 @@ void PropertySelector::_update_search() { 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"); + icon = EditorNode::get_singleton()->get_class_icon(E->get().name); } category->set_icon(0, icon); continue; @@ -241,10 +240,8 @@ void PropertySelector::_update_search() { 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"); + icon = EditorNode::get_singleton()->get_class_icon(rep); } category->set_icon(0, icon); @@ -397,6 +394,8 @@ void PropertySelector::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { connect("confirmed", this, "_confirmed"); + } else if (p_what == NOTIFICATION_EXIT_TREE) { + disconnect("confirmed", this, "_confirmed"); } } diff --git a/editor/pvrtc_compress.cpp b/editor/pvrtc_compress.cpp index 57a2b0d97f..30e78aa32b 100644 --- a/editor/pvrtc_compress.cpp +++ b/editor/pvrtc_compress.cpp @@ -30,11 +30,11 @@ #include "pvrtc_compress.h" +#include "core/io/resource_loader.h" +#include "core/io/resource_saver.h" +#include "core/os/file_access.h" +#include "core/os/os.h" #include "editor_settings.h" -#include "io/resource_loader.h" -#include "io/resource_saver.h" -#include "os/file_access.h" -#include "os/os.h" #include "scene/resources/texture.h" static void (*_base_image_compress_pvrtc2_func)(Image *) = NULL; @@ -115,11 +115,6 @@ static void _compress_pvrtc4(Image *p_image) { _compress_image(Image::COMPRESS_PVRTC4, p_image); } -static void _compress_etc(Image *p_image) { - - _compress_image(Image::COMPRESS_ETC, p_image); -} - void _pvrtc_register_compressors() { _base_image_compress_pvrtc2_func = Image::_image_compress_pvrtc2_func; @@ -127,5 +122,4 @@ void _pvrtc_register_compressors() { Image::_image_compress_pvrtc2_func = _compress_pvrtc2; Image::_image_compress_pvrtc4_func = _compress_pvrtc4; - //Image::_image_compress_etc_func=_compress_etc; //use the built in one for ETC } diff --git a/editor/pvrtc_compress.h b/editor/pvrtc_compress.h index cdd2f0d242..0396837623 100644 --- a/editor/pvrtc_compress.h +++ b/editor/pvrtc_compress.h @@ -31,7 +31,7 @@ #ifndef PVRTC_COMPRESS_H #define PVRTC_COMPRESS_H -#include "image.h" +#include "core/image.h" void _pvrtc_register_compressors(); diff --git a/editor/quick_open.cpp b/editor/quick_open.cpp index d2101f1e00..8dacc3c142 100644 --- a/editor/quick_open.cpp +++ b/editor/quick_open.cpp @@ -30,9 +30,9 @@ #include "quick_open.h" -#include "os/keyboard.h" +#include "core/os/keyboard.h" -void EditorQuickOpen::popup(const StringName &p_base, bool p_enable_multi, bool p_add_dirs, bool p_dontclear) { +void EditorQuickOpen::popup_dialog(const StringName &p_base, bool p_enable_multi, bool p_add_dirs, bool p_dontclear) { add_directories = p_add_dirs; popup_centered_ratio(0.6); @@ -261,6 +261,8 @@ void EditorQuickOpen::_notification(int p_what) { search_box->set_right_icon(get_icon("Search", "EditorIcons")); search_box->set_clear_button_enabled(true); + } else if (p_what == NOTIFICATION_EXIT_TREE) { + disconnect("confirmed", this, "_confirmed"); } } diff --git a/editor/quick_open.h b/editor/quick_open.h index ecc6af0c53..5451d5a08c 100644 --- a/editor/quick_open.h +++ b/editor/quick_open.h @@ -31,8 +31,8 @@ #ifndef EDITOR_QUICK_OPEN_H #define EDITOR_QUICK_OPEN_H +#include "core/pair.h" #include "editor_file_system.h" -#include "pair.h" #include "scene/gui/dialogs.h" #include "scene/gui/tree.h" class EditorQuickOpen : public ConfirmationDialog { @@ -66,7 +66,7 @@ public: String get_selected() const; Vector<String> get_selected_files() const; - void popup(const StringName &p_base, bool p_enable_multi = false, bool p_add_dirs = false, bool p_dontclear = false); + void popup_dialog(const StringName &p_base, bool p_enable_multi = false, bool p_add_dirs = false, bool p_dontclear = false); EditorQuickOpen(); }; diff --git a/editor/rename_dialog.cpp b/editor/rename_dialog.cpp index d617089142..47e1ae0dab 100644 --- a/editor/rename_dialog.cpp +++ b/editor/rename_dialog.cpp @@ -30,12 +30,12 @@ #include "rename_dialog.h" +#include "core/print_string.h" #include "editor_node.h" #include "editor_settings.h" #include "editor_themes.h" #include "modules/regex/regex.h" #include "plugins/script_editor_plugin.h" -#include "print_string.h" #include "scene/gui/control.h" #include "scene/gui/label.h" #include "scene/gui/tab_container.h" @@ -220,21 +220,21 @@ RenameDialog::RenameDialog(SceneTreeEditor *p_scene_tree_editor, UndoRedo *p_und Label *lbl_count_step = memnew(Label); lbl_count_step->set_text(TTR("Step")); - lbl_count_step->set_tooltip(TTR("Ammount by which counter is incremented for each node")); + lbl_count_step->set_tooltip(TTR("Amount by which counter is incremented for each node")); hbc_count_options->add_child(lbl_count_step); spn_count_step = memnew(SpinBox); - spn_count_step->set_tooltip(TTR("Ammount by which counter is incremented for each node")); + spn_count_step->set_tooltip(TTR("Amount by which counter is incremented for each node")); spn_count_step->set_step(1); hbc_count_options->add_child(spn_count_step); Label *lbl_count_padding = memnew(Label); lbl_count_padding->set_text(TTR("Padding")); - lbl_count_padding->set_tooltip(TTR("Minium number of digits for the counter.\nMissing digits are padded with leading zeros.")); + lbl_count_padding->set_tooltip(TTR("Minimum number of digits for the counter.\nMissing digits are padded with leading zeros.")); hbc_count_options->add_child(lbl_count_padding); spn_count_padding = memnew(SpinBox); - spn_count_padding->set_tooltip(TTR("Minium number of digits for the counter.\nMissing digits are padded with leading zeros.")); + spn_count_padding->set_tooltip(TTR("Minimum number of digits for the counter.\nMissing digits are padded with leading zeros.")); spn_count_padding->set_step(1); hbc_count_options->add_child(spn_count_padding); diff --git a/editor/rename_dialog.h b/editor/rename_dialog.h index c5ebc30c0c..fa558660a4 100644 --- a/editor/rename_dialog.h +++ b/editor/rename_dialog.h @@ -37,8 +37,8 @@ #include "scene/gui/option_button.h" #include "scene/gui/spin_box.h" +#include "core/undo_redo.h" #include "editor/scene_tree_editor.h" -#include "undo_redo.h" /** @author Blazej Floch diff --git a/editor/reparent_dialog.cpp b/editor/reparent_dialog.cpp index da8bfdbbd2..3a6864b052 100644 --- a/editor/reparent_dialog.cpp +++ b/editor/reparent_dialog.cpp @@ -30,7 +30,7 @@ #include "reparent_dialog.h" -#include "print_string.h" +#include "core/print_string.h" #include "scene/gui/box_container.h" #include "scene/gui/label.h" diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 39250ab391..fe438236c9 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -53,6 +53,13 @@ void SceneTreeDock::_nodes_drag_begin() { } } +void SceneTreeDock::_quick_open() { + Vector<String> files = quick_open->get_selected_files(); + for (int i = 0; i < files.size(); i++) { + instance(files[i]); + } +} + void SceneTreeDock::_input(Ref<InputEvent> p_event) { Ref<InputEventMouseButton> mb = p_event; @@ -119,7 +126,6 @@ void SceneTreeDock::instance(const String &p_file) { if (!edited_scene) { current_option = -1; - accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("No parent to instance a child at.")); accept->popup_centered_minsize(); return; @@ -142,7 +148,6 @@ void SceneTreeDock::instance_scenes(const Vector<String> &p_files, Node *p_paren if (!parent || !edited_scene) { - accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("No parent to instance the scenes at.")); accept->popup_centered_minsize(); return; @@ -164,7 +169,6 @@ 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("OK")); accept->set_text(vformat(TTR("Error loading scene from %s"), p_files[i])); accept->popup_centered_minsize(); error = true; @@ -174,7 +178,6 @@ 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("OK")); accept->set_text(vformat(TTR("Error instancing scene from %s"), p_files[i])); accept->popup_centered_minsize(); error = true; @@ -185,7 +188,6 @@ void SceneTreeDock::_perform_instance_scenes(const Vector<String> &p_files, Node if (_cyclical_dependency_exists(edited_scene->get_filename(), instanced_scene)) { - accept->get_ok()->set_text(TTR("Ok")); accept->set_text(vformat(TTR("Cannot instance the scene '%s' because the current scene exists within one of its nodes."), p_files[i])); accept->popup_centered_minsize(); error = true; @@ -224,7 +226,7 @@ void SceneTreeDock::_perform_instance_scenes(const Vector<String> &p_files, Node String new_name = parent->validate_child_name(instanced_scene); ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger(); editor_data->get_undo_redo().add_do_method(sed, "live_debug_instance_node", edited_scene->get_path_to(parent), p_files[i], new_name); - editor_data->get_undo_redo().add_undo_method(sed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)) + "/" + new_name)); + editor_data->get_undo_redo().add_undo_method(sed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)).plus_file(new_name))); } editor_data->get_undo_redo().commit_action(); @@ -233,7 +235,6 @@ 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("OK")); accept->set_text(vformat(TTR("Error loading scene from %s"), p_file)); accept->popup_centered_minsize(); return; @@ -241,7 +242,6 @@ 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("OK")); accept->set_text(vformat(TTR("Error instancing scene from %s"), p_file)); accept->popup_centered_minsize(); return; @@ -326,16 +326,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { break; } - file->set_mode(EditorFileDialog::MODE_OPEN_FILE); - List<String> extensions; - ResourceLoader::get_recognized_extensions_for_type("PackedScene", &extensions); - file->clear_filters(); - for (int i = 0; i < extensions.size(); i++) { - - file->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper()); - } - - file->popup_centered_ratio(); + quick_open->popup_dialog("PackedScene", true); + quick_open->set_title(TTR("Instance Child Scene")); } break; case TOOL_REPLACE: { @@ -354,9 +346,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { if (path == "") { String root_path = editor_data->get_edited_scene_root()->get_filename(); if (root_path == "") { - path = "res://" + selected->get_name(); + path = String("res://").plus_file(selected->get_name()); } else { - path = root_path.get_base_dir() + "/" + selected->get_name(); + path = root_path.get_base_dir().plus_file(selected->get_name()); } } @@ -366,15 +358,12 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { ScriptLanguage *l = ScriptServer::get_language(i); if (l->get_type() == existing->get_class()) { String name = l->get_global_class_name(existing->get_path()); - if (ScriptServer::is_global_class(name)) { - if (EDITOR_GET("interface/editors/derive_script_globals_by_name").operator bool()) { - inherits = editor->get_editor_data().script_class_get_base(name); - } else if (l->can_inherit_from_file()) { - inherits = "\"" + existing->get_path() + "\""; - } - } else { + if (ScriptServer::is_global_class(name) && EDITOR_GET("interface/editors/derive_script_globals_by_name").operator bool()) { + inherits = name; + } else if (l->can_inherit_from_file()) { inherits = "\"" + existing->get_path() + "\""; } + break; } } } @@ -399,9 +388,6 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { const RefPtr empty; editor_data->get_undo_redo().add_do_method(E->get(), "set_script", empty); editor_data->get_undo_redo().add_undo_method(E->get(), "set_script", existing); - - editor_data->get_undo_redo().add_do_method(E->get(), "set_meta", "_editor_icon", get_icon(E->get()->get_class(), "EditorIcons")); - editor_data->get_undo_redo().add_undo_method(E->get(), "set_meta", "_editor_icon", E->get()->get_meta("_editor_icon")); } } @@ -419,7 +405,6 @@ 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("OK")); accept->set_text(TTR("This operation can't be done on the tree root.")); accept->popup_centered_minsize(); break; @@ -480,7 +465,6 @@ 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("OK")); accept->set_text(TTR("This operation can't be done on the tree root.")); accept->popup_centered_minsize(); break; @@ -533,7 +517,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger(); editor_data->get_undo_redo().add_do_method(sed, "live_debug_duplicate_node", edited_scene->get_path_to(node), dup->get_name()); - editor_data->get_undo_redo().add_undo_method(sed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)) + "/" + dup->get_name())); + editor_data->get_undo_redo().add_undo_method(sed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)).plus_file(dup->get_name()))); } editor_data->get_undo_redo().commit_action(); @@ -550,7 +534,6 @@ 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("OK")); accept->set_text(TTR("This operation can't be done on the tree root.")); accept->popup_centered_minsize(); break; @@ -581,15 +564,26 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { return; editor_data->get_undo_redo().create_action("Make node as Root"); - _node_replace_owner(root, node, node, MODE_DO); editor_data->get_undo_redo().add_do_method(node->get_parent(), "remove_child", node); + editor_data->get_undo_redo().add_do_method(root->get_parent(), "remove_child", root); + editor_data->get_undo_redo().add_do_method(node, "add_child", root); editor_data->get_undo_redo().add_do_method(editor, "set_edited_scene", node); editor_data->get_undo_redo().add_do_method(node, "set_filename", root->get_filename()); + editor_data->get_undo_redo().add_do_method(root, "set_filename", String()); + editor_data->get_undo_redo().add_do_method(node, "set_owner", (Object *)NULL); + editor_data->get_undo_redo().add_do_method(root, "set_owner", node); + _node_replace_owner(root, root, node, MODE_DO); + editor_data->get_undo_redo().add_undo_method(root, "set_filename", root->get_filename()); editor_data->get_undo_redo().add_undo_method(node, "set_filename", String()); + editor_data->get_undo_redo().add_undo_method(node, "remove_child", root); editor_data->get_undo_redo().add_undo_method(editor, "set_edited_scene", root); editor_data->get_undo_redo().add_undo_method(node->get_parent(), "add_child", node); - _node_replace_owner(root, node, root, MODE_UNDO); + editor_data->get_undo_redo().add_undo_method(root, "set_owner", (Object *)NULL); + editor_data->get_undo_redo().add_undo_method(node, "set_owner", root); + + _node_replace_owner(root, root, root, MODE_UNDO); + editor_data->get_undo_redo().add_do_method(scene_tree, "update_tree"); editor_data->get_undo_redo().add_undo_method(scene_tree, "update_tree"); editor_data->get_undo_redo().add_undo_reference(root); @@ -637,7 +631,6 @@ 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("OK")); accept->set_text(TTR("This operation can't be done without a scene.")); accept->popup_centered_minsize(); break; @@ -646,7 +639,6 @@ 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("OK")); accept->set_text(TTR("This operation requires a single selected node.")); accept->popup_centered_minsize(); break; @@ -655,14 +647,12 @@ 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("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("OK")); accept->set_text(TTR("This operation can't be done on instanced scenes.")); accept->popup_centered_minsize(); break; @@ -700,6 +690,13 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } } } break; + case TOOL_OPEN_DOCUMENTATION: { + List<Node *> selection = editor_selection->get_selected_node_list(); + for (int i = 0; i < selection.size(); i++) { + ScriptEditor::get_singleton()->goto_help("class_name:" + selection[i]->get_class()); + } + EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT); + } break; case TOOL_SCENE_EDITABLE_CHILDREN: { List<Node *> selection = editor_selection->get_selected_node_list(); List<Node *>::Element *e = selection.front(); @@ -707,18 +704,13 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { Node *node = e->get(); if (node) { bool editable = EditorNode::get_singleton()->get_edited_scene()->is_editable_instance(node); - int editable_item_idx = menu->get_item_idx_from_text(TTR("Editable Children")); - int placeholder_item_idx = menu->get_item_idx_from_text(TTR("Load As Placeholder")); - editable = !editable; - EditorNode::get_singleton()->get_edited_scene()->set_editable_instance(node, editable); - - menu->set_item_checked(editable_item_idx, editable); if (editable) { - node->set_scene_instance_load_placeholder(false); - menu->set_item_checked(placeholder_item_idx, false); + editable_instance_remove_dialog->set_text(TTR("Disabling \"editable_instance\" will cause all properties of the node to be reverted to their default.")); + editable_instance_remove_dialog->popup_centered_minsize(); + break; } - scene_tree->update_tree(); + _toggle_editable_children(); } } } break; @@ -806,7 +798,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { case TOOL_CREATE_USER_INTERFACE: case TOOL_CREATE_FAVORITE: { - Node *new_node; + Node *new_node = NULL; if (TOOL_CREATE_FAVORITE == p_tool) { String name = selected_favorite_root.get_slicec(' ', 0); @@ -844,6 +836,10 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { editor_data->get_undo_redo().add_undo_method(editor, "set_edited_scene", (Object *)NULL); editor_data->get_undo_redo().commit_action(); + editor->edit_node(new_node); + editor_selection->clear(); + editor_selection->add_node(new_node); + } break; default: { @@ -987,24 +983,22 @@ void SceneTreeDock::_notification(int p_what) { void SceneTreeDock::_node_replace_owner(Node *p_base, Node *p_node, Node *p_root, ReplaceOwnerMode p_mode) { - if (p_base != p_node) { - if (p_node->get_owner() == p_base) { - UndoRedo *undo_redo = &editor_data->get_undo_redo(); - switch (p_mode) { - case MODE_BIDI: { - undo_redo->add_do_method(p_node, "set_owner", p_root); - undo_redo->add_undo_method(p_node, "set_owner", p_base); + if (p_node->get_owner() == p_base && p_node != p_root) { + UndoRedo *undo_redo = &editor_data->get_undo_redo(); + switch (p_mode) { + case MODE_BIDI: { + undo_redo->add_do_method(p_node, "set_owner", p_root); + undo_redo->add_undo_method(p_node, "set_owner", p_base); - } break; - case MODE_DO: { - undo_redo->add_do_method(p_node, "set_owner", p_root); + } break; + case MODE_DO: { + undo_redo->add_do_method(p_node, "set_owner", p_root); - } break; - case MODE_UNDO: { - undo_redo->add_undo_method(p_node, "set_owner", p_root); + } break; + case MODE_UNDO: { + undo_redo->add_undo_method(p_node, "set_owner", p_root); - } break; - } + } break; } } @@ -1304,15 +1298,20 @@ bool SceneTreeDock::_validate_no_foreign() { if (E->get() != edited_scene && E->get()->get_owner() != edited_scene) { - accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("Can't operate on nodes from a foreign scene!")); accept->popup_centered_minsize(); return false; } + // When edited_scene inherits from another one the root Node will be the parent Scene, + // we don't want to consider that Node a foreign one otherwise we would not be able to + // delete it + if (edited_scene->get_scene_inherited_state().is_valid() && edited_scene == E->get()) { + continue; + } + if (edited_scene->get_scene_inherited_state().is_valid() && edited_scene->get_scene_inherited_state()->find_node_by_path(edited_scene->get_path_to(E->get())) >= 0) { - accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("Can't operate on nodes the current scene inherits from!")); accept->popup_centered_minsize(); return false; @@ -1424,7 +1423,7 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V } editor_data->get_undo_redo().add_do_method(sed, "live_debug_reparent_node", edited_scene->get_path_to(node), edited_scene->get_path_to(new_parent), new_name, p_position_in_parent + inc); - editor_data->get_undo_redo().add_undo_method(sed, "live_debug_reparent_node", NodePath(String(edited_scene->get_path_to(new_parent)) + "/" + new_name), edited_scene->get_path_to(node->get_parent()), node->get_name(), node->get_index()); + editor_data->get_undo_redo().add_undo_method(sed, "live_debug_reparent_node", NodePath(String(edited_scene->get_path_to(new_parent)).plus_file(new_name)), edited_scene->get_path_to(node->get_parent()), node->get_name(), node->get_index()); if (p_keep_global_xform) { if (Object::cast_to<Node2D>(node)) @@ -1496,24 +1495,39 @@ void SceneTreeDock::_script_created(Ref<Script> p_script) { Ref<Script> existing = E->get()->get_script(); editor_data->get_undo_redo().add_do_method(E->get(), "set_script", p_script.get_ref_ptr()); editor_data->get_undo_redo().add_undo_method(E->get(), "set_script", existing); - - String icon_path; - String name = p_script->get_language()->get_global_class_name(p_script->get_path(), NULL, &icon_path); - if (ScriptServer::is_global_class(name)) { - RES icon = ResourceLoader::load(icon_path); - editor_data->get_undo_redo().add_do_method(E->get(), "set_meta", "_editor_icon", icon); - String existing_name = existing.is_valid() ? existing->get_language()->get_global_class_name(existing->get_path()) : String(); - if (existing.is_null() || !ScriptServer::is_global_class(existing_name)) { - editor_data->get_undo_redo().add_undo_method(E->get(), "set_meta", "_editor_icon", get_icon(E->get()->get_class(), "EditorIcons")); - } else { - editor_data->get_undo_redo().add_undo_method(E->get(), "set_meta", "_editor_icon", editor_data->script_class_get_icon_path(existing_name)); - } - } } editor_data->get_undo_redo().commit_action(); editor->push_item(p_script.operator->()); + _update_script_button(); +} + +void SceneTreeDock::_toggle_editable_children() { + List<Node *> selection = editor_selection->get_selected_node_list(); + List<Node *>::Element *e = selection.front(); + if (e) { + Node *node = e->get(); + if (node) { + bool editable = EditorNode::get_singleton()->get_edited_scene()->is_editable_instance(node); + + int editable_item_idx = menu->get_item_idx_from_text(TTR("Editable Children")); + int placeholder_item_idx = menu->get_item_idx_from_text(TTR("Load As Placeholder")); + editable = !editable; + + EditorNode::get_singleton()->get_edited_scene()->set_editable_instance(node, editable); + + menu->set_item_checked(editable_item_idx, editable); + if (editable) { + node->set_scene_instance_load_placeholder(false); + menu->set_item_checked(placeholder_item_idx, false); + } + + SpatialEditor::get_singleton()->update_all_gizmos(node); + + scene_tree->update_tree(); + } + } } void SceneTreeDock::_delete_confirm() { @@ -1597,14 +1611,22 @@ void SceneTreeDock::_delete_confirm() { // Fixes the EditorHistory from still offering deleted notes EditorHistory *editor_history = EditorNode::get_singleton()->get_editor_history(); editor_history->cleanup_history(); - EditorNode::get_singleton()->call("_prepare_history"); + EditorNode::get_singleton()->get_inspector_dock()->call("_prepare_history"); } void SceneTreeDock::_update_script_button() { if (EditorNode::get_singleton()->get_editor_selection()->get_selection().size() == 1) { - button_create_script->show(); + Node *n = EditorNode::get_singleton()->get_editor_selection()->get_selected_node_list()[0]; + if (n->get_script().is_null()) { + button_create_script->show(); + button_clear_script->hide(); + } else { + button_create_script->hide(); + button_clear_script->show(); + } } else { - button_create_script->hide(); + button_create_script->show(); + button_clear_script->hide(); } } @@ -1614,6 +1636,10 @@ void SceneTreeDock::_selection_changed() { if (selection_size > 1) { //automatically turn on multi-edit _tool_selected(TOOL_MULTI_EDIT); + } else if (selection_size == 1) { + editor->push_item(EditorNode::get_singleton()->get_editor_selection()->get_selected_node_list()[0]); + } else { + editor->push_item(NULL); } _update_script_button(); } @@ -1656,7 +1682,7 @@ void SceneTreeDock::_create() { String new_name = parent->validate_child_name(child); ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger(); editor_data->get_undo_redo().add_do_method(sed, "live_debug_create_node", edited_scene->get_path_to(parent), child->get_class(), new_name); - editor_data->get_undo_redo().add_undo_method(sed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)) + "/" + new_name)); + editor_data->get_undo_redo().add_undo_method(sed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)).plus_file(new_name))); } else { @@ -1805,12 +1831,17 @@ 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("OK")); accept->set_text(TTR("This operation requires a single selected node.")); accept->popup_centered_minsize(); return; } + if (EditorNode::get_singleton()->is_scene_open(p_file)) { + accept->set_text(TTR("Can't overwrite scene that is still open!")); + accept->popup_centered_minsize(); + return; + } + Node *base = selection.front()->get(); Map<Node *, Node *> reown; @@ -1823,7 +1854,6 @@ void SceneTreeDock::_new_scene_from(String p_file) { memdelete(copy); if (err != OK) { - 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; @@ -1835,14 +1865,12 @@ void SceneTreeDock::_new_scene_from(String p_file) { err = ResourceSaver::save(p_file, sdata, flg); if (err != OK) { - 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("OK")); accept->set_text(TTR("Error duplicating scene to save it.")); accept->popup_centered_minsize(); return; @@ -1877,32 +1905,6 @@ static bool _has_visible_children(Node *p_node) { return false; } -static Node *_find_last_visible(Node *p_node) { - - Node *last = NULL; - - bool collapsed = p_node->is_displayed_folded(); - - if (!collapsed) { - for (int i = 0; i < p_node->get_child_count(); i++) { - if (_is_node_visible(p_node->get_child(i))) { - last = p_node->get_child(i); - } - } - } - - if (last) { - Node *lastc = _find_last_visible(last); - if (lastc) - last = lastc; - - } else { - last = p_node; - } - - return last; -} - void SceneTreeDock::_normalize_drop(Node *&to_node, int &to_pos, int p_type) { to_pos = -1; @@ -2017,12 +2019,7 @@ void SceneTreeDock::_add_children_to_popup(Object *p_obj, int p_depth) { if (!obj) continue; - Ref<Texture> icon; - - if (has_icon(obj->get_class(), "EditorIcons")) - icon = get_icon(obj->get_class(), "EditorIcons"); - else - icon = get_icon("Object", "EditorIcons"); + Ref<Texture> icon = EditorNode::get_singleton()->get_object_icon(obj); if (menu->get_item_count() == 0) { menu->add_submenu_item(TTR("Sub-Resources"), "Sub-Resources"); @@ -2056,6 +2053,7 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { menu->clear(); + Ref<Script> existing_script; if (selection.size() == 1) { Node *selected = selection[0]; @@ -2070,16 +2068,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(); + existing_script = selected->get_script(); + } + if (!existing_script.is_valid()) { menu->add_icon_shortcut(get_icon("ScriptCreate", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/attach_script"), TOOL_ATTACH_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(); + } + if (selection.size() > 1 || existing_script.is_valid()) { + menu->add_icon_shortcut(get_icon("ScriptRemove", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/clear_script"), TOOL_CLEAR_SCRIPT); + menu->add_icon_shortcut(get_icon("ScriptExtend", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/extend_script"), TOOL_ATTACH_SCRIPT); + } + + menu->add_separator(); + if (selection.size() == 1) { 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); + 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); @@ -2115,10 +2120,6 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { menu->set_item_checked(menu->get_item_idx_from_text(TTR("Load As Placeholder")), placeholder); } } - } else { - 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); } if (selection.size() > 1) { @@ -2127,6 +2128,9 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { menu->add_icon_shortcut(get_icon("Rename", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/batch_rename"), TOOL_BATCH_RENAME); } menu->add_separator(); + menu->add_icon_item(get_icon("Help", "EditorIcons"), TTR("Open documentation"), TOOL_OPEN_DOCUMENTATION); + + menu->add_separator(); menu->add_icon_shortcut(get_icon("Remove", "EditorIcons"), ED_SHORTCUT("scene_tree/delete", TTR("Delete Node(s)"), KEY_DELETE), TOOL_ERASE); menu->set_size(Size2(1, 1)); menu->set_position(p_menu_pos); @@ -2252,7 +2256,7 @@ void SceneTreeDock::_update_create_root_dialog() { String name = l.get_slicec(' ', 0); if (ScriptServer::is_global_class(name)) name = ScriptServer::get_global_class_base(name); - button->set_icon(get_icon(name, "EditorIcons")); + button->set_icon(EditorNode::get_singleton()->get_class_icon(name)); button->connect("pressed", this, "_favorite_root_selected", make_binds(l)); } } @@ -2292,12 +2296,14 @@ void SceneTreeDock::_bind_methods() { ClassDB::bind_method(D_METHOD("_input"), &SceneTreeDock::_input); ClassDB::bind_method(D_METHOD("_nodes_drag_begin"), &SceneTreeDock::_nodes_drag_begin); ClassDB::bind_method(D_METHOD("_delete_confirm"), &SceneTreeDock::_delete_confirm); + ClassDB::bind_method(D_METHOD("_toggle_editable_children"), &SceneTreeDock::_toggle_editable_children); ClassDB::bind_method(D_METHOD("_node_prerenamed"), &SceneTreeDock::_node_prerenamed); ClassDB::bind_method(D_METHOD("_import_subscene"), &SceneTreeDock::_import_subscene); ClassDB::bind_method(D_METHOD("_selection_changed"), &SceneTreeDock::_selection_changed); ClassDB::bind_method(D_METHOD("_new_scene_from"), &SceneTreeDock::_new_scene_from); ClassDB::bind_method(D_METHOD("_nodes_dragged"), &SceneTreeDock::_nodes_dragged); ClassDB::bind_method(D_METHOD("_files_dropped"), &SceneTreeDock::_files_dropped); + ClassDB::bind_method(D_METHOD("_quick_open"), &SceneTreeDock::_quick_open); ClassDB::bind_method(D_METHOD("_script_dropped"), &SceneTreeDock::_script_dropped); ClassDB::bind_method(D_METHOD("_tree_rmb"), &SceneTreeDock::_tree_rmb); ClassDB::bind_method(D_METHOD("_filter_changed"), &SceneTreeDock::_filter_changed); @@ -2334,6 +2340,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel ED_SHORTCUT("scene_tree/instance_scene", TTR("Instance Child Scene")); ED_SHORTCUT("scene_tree/change_node_type", TTR("Change Type")); ED_SHORTCUT("scene_tree/attach_script", TTR("Attach Script")); + ED_SHORTCUT("scene_tree/extend_script", TTR("Extend Script")); ED_SHORTCUT("scene_tree/clear_script", TTR("Clear Script")); ED_SHORTCUT("scene_tree/move_up", TTR("Move Up"), KEY_MASK_CMD | KEY_UP); ED_SHORTCUT("scene_tree/move_down", TTR("Move Down"), KEY_MASK_CMD | KEY_DOWN); @@ -2365,6 +2372,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel filter->set_h_size_flags(SIZE_EXPAND_FILL); filter->set_placeholder(TTR("Filter nodes")); filter_hbc->add_child(filter); + filter->add_constant_override("minimum_spaces", 0); filter->connect("text_changed", this, "_filter_changed"); tb = memnew(ToolButton); @@ -2433,7 +2441,6 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel add_child(create_dialog); create_dialog->connect("create", this, "_create"); create_dialog->connect("favorites_updated", this, "_update_create_root_dialog"); - EditorFileSystem::get_singleton()->connect("script_classes_updated", create_dialog, "_save_and_update_favorite_list"); rename_dialog = memnew(RenameDialog(scene_tree, &editor_data->get_undo_redo())); add_child(rename_dialog); @@ -2449,15 +2456,19 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel accept = memnew(AcceptDialog); add_child(accept); - file = memnew(EditorFileDialog); - add_child(file); - file->connect("file_selected", this, "instance"); + quick_open = memnew(EditorQuickOpen); + add_child(quick_open); + quick_open->connect("quick_open", this, "_quick_open"); set_process_unhandled_key_input(true); delete_dialog = memnew(ConfirmationDialog); add_child(delete_dialog); delete_dialog->connect("confirmed", this, "_delete_confirm"); + editable_instance_remove_dialog = memnew(ConfirmationDialog); + add_child(editable_instance_remove_dialog); + editable_instance_remove_dialog->connect("confirmed", this, "_toggle_editable_children"); + import_subscene_dialog = memnew(EditorSubScene); add_child(import_subscene_dialog); import_subscene_dialog->connect("subscene_selected", this, "_import_subscene"); diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index 34a7c98d11..3939f4f361 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -36,6 +36,7 @@ #include "editor/editor_data.h" #include "editor/editor_sub_scene.h" #include "editor/groups_editor.h" +#include "editor/quick_open.h" #include "editor/rename_dialog.h" #include "editor/reparent_dialog.h" #include "editor/script_create_dialog.h" @@ -75,6 +76,7 @@ class SceneTreeDock : public VBoxContainer { TOOL_ERASE, TOOL_COPY_NODE_PATH, TOOL_BUTTON_MAX, + TOOL_OPEN_DOCUMENTATION, TOOL_SCENE_EDITABLE_CHILDREN, TOOL_SCENE_USE_PLACEHOLDER, TOOL_SCENE_MAKE_LOCAL, @@ -121,9 +123,10 @@ class SceneTreeDock : public VBoxContainer { ScriptCreateDialog *script_create_dialog; AcceptDialog *accept; ConfirmationDialog *delete_dialog; + ConfirmationDialog *editable_instance_remove_dialog; ReparentDialog *reparent_dialog; - EditorFileDialog *file; + EditorQuickOpen *quick_open; EditorSubScene *import_subscene_dialog; EditorFileDialog *new_scene_from_dialog; @@ -169,6 +172,8 @@ class SceneTreeDock : public VBoxContainer { void _delete_confirm(); + void _toggle_editable_children(); + void _node_prerenamed(Node *p_node, const String &p_new_name); void _nodes_drag_begin(); @@ -190,6 +195,7 @@ class SceneTreeDock : public VBoxContainer { void _nodes_dragged(Array p_nodes, NodePath p_to, int p_type); void _files_dropped(Vector<String> p_files, NodePath p_to, int p_type); void _script_dropped(String p_file, NodePath p_to); + void _quick_open(); void _tree_rmb(const Vector2 &p_menu_pos); diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp index a916ae23f6..6614e24df7 100644 --- a/editor/scene_tree_editor.cpp +++ b/editor/scene_tree_editor.cpp @@ -30,11 +30,11 @@ #include "scene_tree_editor.h" +#include "core/message_queue.h" +#include "core/print_string.h" #include "editor/plugins/animation_player_editor_plugin.h" #include "editor/plugins/canvas_item_editor_plugin.h" #include "editor_node.h" -#include "message_queue.h" -#include "print_string.h" #include "scene/gui/label.h" #include "scene/main/viewport.h" #include "scene/resources/packed_scene.h" @@ -73,7 +73,7 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_i undo_redo->create_action(TTR("Toggle Visible")); _toggle_visible(n); List<Node *> selection = editor_selection->get_selected_node_list(); - if (selection.size() > 1) { + if (selection.size() > 1 && selection.find(n) != NULL) { for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { Node *nv = E->get(); ERR_FAIL_COND(!nv); @@ -186,11 +186,7 @@ bool SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { item->set_collapsed(true); } - Ref<Texture> icon; - if (p_node->has_meta("_editor_icon")) - icon = p_node->get_meta("_editor_icon"); - else - icon = get_icon((has_icon(p_node->get_class(), "EditorIcons") ? p_node->get_class() : String("Object")), "EditorIcons"); + Ref<Texture> icon = EditorNode::get_singleton()->get_object_icon(p_node, "Node"); item->set_icon(0, icon); item->set_metadata(0, p_node->get_path()); @@ -669,6 +665,13 @@ void SceneTreeEditor::_renamed() { Node *n = get_node(np); ERR_FAIL_COND(!n); + // Empty node names are not allowed, so resets it to previous text and show warning + if (which->get_text(0).strip_edges().empty()) { + which->set_text(0, n->get_name()); + EditorNode::get_singleton()->show_warning(TTR("No name provided")); + return; + } + String new_name = which->get_text(0); if (!Node::_validate_node_name(new_name)) { diff --git a/editor/scene_tree_editor.h b/editor/scene_tree_editor.h index c4f63f5736..e575fb986a 100644 --- a/editor/scene_tree_editor.h +++ b/editor/scene_tree_editor.h @@ -31,12 +31,12 @@ #ifndef SCENE_TREE_EDITOR_H #define SCENE_TREE_EDITOR_H +#include "core/undo_redo.h" #include "editor_data.h" #include "editor_settings.h" #include "scene/gui/button.h" #include "scene/gui/dialogs.h" #include "scene/gui/tree.h" -#include "undo_redo.h" /** @author Juan Linietsky <reduzio@gmail.com> */ diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp index 24c4ba4cb7..df704706af 100644 --- a/editor/script_create_dialog.cpp +++ b/editor/script_create_dialog.cpp @@ -30,13 +30,13 @@ #include "script_create_dialog.h" +#include "core/io/resource_saver.h" +#include "core/os/file_access.h" +#include "core/project_settings.h" +#include "core/script_language.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor_file_system.h" -#include "io/resource_saver.h" -#include "os/file_access.h" -#include "project_settings.h" -#include "script_language.h" void ScriptCreateDialog::_notification(int p_what) { @@ -50,12 +50,17 @@ void ScriptCreateDialog::_notification(int p_what) { } } -void ScriptCreateDialog::config(const String &p_base_name, const String &p_base_path) { +bool ScriptCreateDialog::_can_be_built_in() { + return (supports_built_in && built_in_enabled); +} + +void ScriptCreateDialog::config(const String &p_base_name, const String &p_base_path, bool p_built_in_enabled) { class_name->set_text(""); class_name->deselect(); parent_name->set_text(p_base_name); parent_name->deselect(); + if (p_base_path != "") { initial_bp = p_base_path.get_basename(); file_path->set_text(initial_bp + "." + ScriptServer::get_language(language_menu->get_selected())->get_extension()); @@ -65,6 +70,8 @@ void ScriptCreateDialog::config(const String &p_base_name, const String &p_base_ } file_path->deselect(); + built_in_enabled = p_built_in_enabled; + _lang_changed(current_language); _class_name_changed(""); _path_changed(file_path->get_text()); @@ -163,7 +170,6 @@ void ScriptCreateDialog::_create_new() { if (script_template != "") { scr = ResourceLoader::load(script_template); if (scr.is_null()) { - alert->get_ok()->set_text(TTR("OK")); alert->set_text(vformat(TTR("Error loading template '%s'"), script_template)); alert->popup_centered(); return; @@ -200,7 +206,6 @@ void ScriptCreateDialog::_load_exist() { String path = file_path->get_text(); RES p_script = ResourceLoader::load(path, "Script"); if (p_script.is_null()) { - alert->get_ok()->set_text(TTR("OK")); alert->set_text(vformat(TTR("Error loading script from %s"), path)); alert->popup_centered(); return; @@ -359,7 +364,7 @@ void ScriptCreateDialog::_path_changed(const String &p_path) { is_path_valid = false; is_new_script_created = true; - String p = p_path; + String p = p_path.strip_edges(); if (p == "") { _msg_path_valid(false, TTR("Path is empty")); @@ -367,6 +372,12 @@ void ScriptCreateDialog::_path_changed(const String &p_path) { return; } + if (p.get_file().get_basename() == "") { + _msg_path_valid(false, TTR("Filename is empty")); + _update_dialog(); + return; + } + p = ProjectSettings::get_singleton()->localize_path(p); if (!p.begins_with("res://")) { _msg_path_valid(false, TTR("Path is not local")); @@ -539,7 +550,7 @@ void ScriptCreateDialog::_update_dialog() { } } - if (!supports_built_in) + if (!_can_be_built_in()) internal->set_pressed(false); /* Is Script created or loaded from existing file */ @@ -548,14 +559,14 @@ void ScriptCreateDialog::_update_dialog() { get_ok()->set_text(TTR("Create")); parent_name->set_editable(true); parent_browse_button->set_disabled(false); - internal->set_disabled(!supports_built_in); + internal->set_disabled(!_can_be_built_in()); _msg_path_valid(true, TTR("Built-in script (into scene file)")); } else if (is_new_script_created) { // New Script Created get_ok()->set_text(TTR("Create")); parent_name->set_editable(true); parent_browse_button->set_disabled(false); - internal->set_disabled(!supports_built_in); + internal->set_disabled(!_can_be_built_in()); if (is_path_valid) { _msg_path_valid(true, TTR("Create new script file")); } @@ -564,7 +575,7 @@ void ScriptCreateDialog::_update_dialog() { get_ok()->set_text(TTR("Load")); parent_name->set_editable(false); parent_browse_button->set_disabled(true); - internal->set_disabled(!supports_built_in); + internal->set_disabled(!_can_be_built_in()); if (is_path_valid) { _msg_path_valid(true, TTR("Load existing script file")); } @@ -583,7 +594,7 @@ void ScriptCreateDialog::_bind_methods() { ClassDB::bind_method("_path_entered", &ScriptCreateDialog::_path_entered); ClassDB::bind_method("_template_changed", &ScriptCreateDialog::_template_changed); - ClassDB::bind_method(D_METHOD("config", "inherits", "path"), &ScriptCreateDialog::config); + ClassDB::bind_method(D_METHOD("config", "inherits", "path", "built_in_enabled"), &ScriptCreateDialog::config, DEFVAL(true)); ADD_SIGNAL(MethodInfo("script_created", PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script"))); } @@ -788,6 +799,7 @@ ScriptCreateDialog::ScriptCreateDialog() { has_named_classes = false; supports_built_in = false; can_inherit_from_file = false; + built_in_enabled = true; is_built_in = false; is_new_script_created = true; diff --git a/editor/script_create_dialog.h b/editor/script_create_dialog.h index 1ad4a1b7a1..e0bf336b56 100644 --- a/editor/script_create_dialog.h +++ b/editor/script_create_dialog.h @@ -69,11 +69,13 @@ class ScriptCreateDialog : public ConfirmationDialog { bool is_parent_name_valid; bool is_class_name_valid; bool is_built_in; + bool built_in_enabled; int current_language; bool re_check_path; String script_template; Vector<String> template_list; + bool _can_be_built_in(); void _path_changed(const String &p_path = String()); void _path_entered(const String &p_path = String()); void _lang_changed(int l = 0); @@ -96,8 +98,7 @@ protected: static void _bind_methods(); public: - void config(const String &p_base_name, const String &p_base_path); - + void config(const String &p_base_name, const String &p_base_path, bool p_built_in_enabled = true); ScriptCreateDialog(); }; diff --git a/editor/script_editor_debugger.cpp b/editor/script_editor_debugger.cpp index 9db53fe5f5..ab3e3b9a49 100644 --- a/editor/script_editor_debugger.cpp +++ b/editor/script_editor_debugger.cpp @@ -30,11 +30,13 @@ #include "script_editor_debugger.h" +#include "core/io/marshalls.h" +#include "core/project_settings.h" +#include "core/ustring.h" #include "editor_node.h" #include "editor_profiler.h" #include "editor_settings.h" #include "main/performance.h" -#include "project_settings.h" #include "property_editor.h" #include "scene/gui/dialogs.h" #include "scene/gui/label.h" @@ -47,7 +49,6 @@ #include "scene/gui/texture_button.h" #include "scene/gui/tree.h" #include "scene/resources/packed_scene.h" -#include "ustring.h" class ScriptEditorDebuggerVariables : public Object { @@ -354,7 +355,7 @@ void ScriptEditorDebugger::_video_mem_request() { Size2 ScriptEditorDebugger::get_minimum_size() const { Size2 ms = Control::get_minimum_size(); - ms.y = MAX(ms.y, 250); + ms.y = MAX(ms.y, 250 * EDSCALE); return ms; } void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_data) { @@ -396,7 +397,6 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da dobreak->set_disabled(false); docontinue->set_disabled(true); emit_signal("breaked", false, false, Variant()); - //tabs->set_current_tab(0); profiler->set_enabled(true); profiler->disable_seeking(); inspector->edit(NULL); @@ -429,8 +429,9 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da ObjectID id = ObjectID(p_data[i + 3]); it->set_text(0, p_data[i + 1]); - if (has_icon(p_data[i + 2], "EditorIcons")) - it->set_icon(0, get_icon(p_data[i + 2], "EditorIcons")); + Ref<Texture> icon = EditorNode::get_singleton()->get_class_icon(p_data[i + 2], ""); + if (icon.is_valid()) + it->set_icon(0, icon); it->set_metadata(0, id); if (id == inspected_object_id) { @@ -492,10 +493,22 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da pinfo.usage = PropertyUsageFlags(int(prop[4])); Variant var = prop[5]; - String hint_string = pinfo.hint_string; - if (hint_string.begins_with("RES:") && hint_string != "RES:") { - String path = hint_string.substr(4, hint_string.length()); - var = ResourceLoader::load(path); + if (pinfo.type == Variant::OBJECT) { + if (var.is_zero()) { + var = RES(); + } else if (var.get_type() == Variant::STRING) { + var = ResourceLoader::load(var); + + if (pinfo.hint_string == "Script") + debugObj->set_script(var); + } else if (var.get_type() == Variant::OBJECT) { + if (((Object *)var)->is_class("EncodedObjectAsID")) { + var = Object::cast_to<EncodedObjectAsID>(var)->get_object_id(); + pinfo.type = var.get_type(); + pinfo.hint = PROPERTY_HINT_OBJECT_ID; + pinfo.hint_string = "Object"; + } + } } if (is_new_object) { @@ -573,14 +586,10 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da PropertyHint h = PROPERTY_HINT_NONE; String hs = String(); - if (n.begins_with("*")) { - - n = n.substr(1, n.length()); + if (v.get_type() == Variant::OBJECT) { + v = Object::cast_to<EncodedObjectAsID>(v)->get_object_id(); h = PROPERTY_HINT_OBJECT_ID; - String s = v; - s = s.replace("[", ""); - hs = s.get_slice(":", 0); - v = s.get_slice(":", 1).to_int(); + hs = "Object"; } variables->add_property("Locals/" + n, v, h, hs); @@ -596,14 +605,10 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da PropertyHint h = PROPERTY_HINT_NONE; String hs = String(); - if (n.begins_with("*")) { - - n = n.substr(1, n.length()); + if (v.get_type() == Variant::OBJECT) { + v = Object::cast_to<EncodedObjectAsID>(v)->get_object_id(); h = PROPERTY_HINT_OBJECT_ID; - String s = v; - s = s.replace("[", ""); - hs = s.get_slice(":", 0); - v = s.get_slice(":", 1).to_int(); + hs = "Object"; } variables->add_property("Members/" + n, v, h, hs); @@ -619,14 +624,10 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da PropertyHint h = PROPERTY_HINT_NONE; String hs = String(); - if (n.begins_with("*")) { - - n = n.substr(1, n.length()); + if (v.get_type() == Variant::OBJECT) { + v = Object::cast_to<EncodedObjectAsID>(v)->get_object_id(); h = PROPERTY_HINT_OBJECT_ID; - String s = v; - s = s.replace("[", ""); - hs = s.get_slice(":", 0); - v = s.get_slice(":", 1).to_int(); + hs = "Object"; } variables->add_property("Globals/" + n, v, h, hs); @@ -712,27 +713,66 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da bool warning = err[9]; bool e; String time = String("%d:%02d:%02d:%04d").sprintf(vals, &e); - String txt = time + " - " + (err[8].is_zero() ? String(err[7]) : String(err[8])); + String txt = err[8].is_zero() ? String(err[7]) : String(err[8]); + + TreeItem *r = error_tree->get_root(); + if (!r) { + r = error_tree->create_item(); + } - String tooltip = TTR("Type:") + String(warning ? TTR("Warning") : TTR("Error")); - tooltip += "\n" + TTR("Description:") + " " + String(err[8]); - tooltip += "\n" + TTR("Time:") + " " + time; - tooltip += "\nC " + TTR("Error:") + " " + String(err[7]); - tooltip += "\nC " + TTR("Source:") + " " + String(err[5]) + ":" + String(err[6]); - tooltip += "\nC " + TTR("Function:") + " " + String(err[4]); + TreeItem *error = error_tree->create_item(r); + error->set_collapsed(true); - error_list->add_item(txt, EditorNode::get_singleton()->get_gui_base()->get_icon(warning ? "Warning" : "Error", "EditorIcons")); - error_list->set_item_tooltip(error_list->get_item_count() - 1, tooltip); + error->set_icon(0, get_icon(warning ? "Warning" : "Error", "EditorIcons")); + error->set_text(0, time); + error->set_text_align(0, TreeItem::ALIGN_LEFT); - int scc = p_data[1]; + error->set_text(1, txt); - Array stack; - stack.resize(scc); - for (int i = 0; i < scc; i++) { - stack[i] = p_data[2 + i]; + String source(err[5]); + bool source_is_project_file = source.begins_with("res://"); + if (source_is_project_file) + txt = source.get_file() + ":" + String(err[6]); + else + txt = source + ":" + String(err[6]); + + String method = err[4]; + if (method.length() > 0) + txt += " @ " + method + "()"; + + TreeItem *c_info = error_tree->create_item(error); + c_info->set_text(0, "<" + TTR(source_is_project_file ? "Source" : "C Source") + ">"); + c_info->set_text(1, txt); + c_info->set_text_align(0, TreeItem::ALIGN_LEFT); + + if (source_is_project_file) { + Array meta; + meta.push_back(source); + meta.push_back(err[6]); + error->set_metadata(0, meta); + c_info->set_metadata(0, meta); } - error_list->set_item_metadata(error_list->get_item_count() - 1, stack); + int scc = p_data[1]; + + for (int i = 0; i < scc; i += 3) { + String script = p_data[2 + i]; + String method = p_data[3 + i]; + int line = p_data[4 + i]; + TreeItem *stack_trace = error_tree->create_item(error); + + Array meta; + meta.push_back(script); + meta.push_back(line); + stack_trace->set_metadata(0, meta); + + if (i == 0) { + stack_trace->set_text(0, "<" + TTR("Stack Trace") + ">"); + stack_trace->set_text_align(0, TreeItem::ALIGN_LEFT); + error->set_metadata(0, meta); + } + stack_trace->set_text(1, script.get_file() + ":" + itos(line) + " @ " + method + "()"); + } if (warning) warning_count++; @@ -970,8 +1010,8 @@ void ScriptEditorDebugger::_notification(int p_what) { //scene_tree_refresh->set_icon( get_icon("Reload","EditorIcons")); le_set->connect("pressed", this, "_live_edit_set"); le_clear->connect("pressed", this, "_live_edit_clear"); - error_list->connect("item_selected", this, "_error_selected"); - error_stack->connect("item_selected", this, "_error_stack_selected"); + error_tree->connect("item_selected", this, "_error_selected"); + error_tree->connect("item_activated", this, "_error_activated"); vmem_refresh->set_icon(get_icon("Reload", "EditorIcons")); reason->add_color_override("font_color", get_color("error_color", "Editor")); @@ -1017,19 +1057,19 @@ void ScriptEditorDebugger::_notification(int p_what) { if (error_count != last_error_count || warning_count != last_warning_count) { if (error_count == 0 && warning_count == 0) { - error_split->set_name(TTR("Errors")); + error_tree->set_name(TTR("Errors")); debugger_button->set_text(TTR("Debugger")); debugger_button->set_icon(Ref<Texture>()); - tabs->set_tab_icon(error_split->get_index(), Ref<Texture>()); + tabs->set_tab_icon(error_tree->get_index(), Ref<Texture>()); } else { - error_split->set_name(TTR("Errors") + " (" + itos(error_count + warning_count) + ")"); + error_tree->set_name(TTR("Errors") + " (" + itos(error_count + warning_count) + ")"); debugger_button->set_text(TTR("Debugger") + " (" + itos(error_count + warning_count) + ")"); if (error_count == 0) { debugger_button->set_icon(get_icon("Warning", "EditorIcons")); - tabs->set_tab_icon(error_split->get_index(), get_icon("Warning", "EditorIcons")); + tabs->set_tab_icon(error_tree->get_index(), get_icon("Warning", "EditorIcons")); } else { debugger_button->set_icon(get_icon("Error", "EditorIcons")); - tabs->set_tab_icon(error_split->get_index(), get_icon("Error", "EditorIcons")); + tabs->set_tab_icon(error_tree->get_index(), get_icon("Error", "EditorIcons")); } } last_error_count = error_count; @@ -1060,8 +1100,7 @@ void ScriptEditorDebugger::_notification(int p_what) { inspect_scene_tree->clear(); le_set->set_disabled(true); le_clear->set_disabled(false); - error_list->clear(); - error_stack->clear(); + error_tree->clear(); error_count = 0; warning_count = 0; profiler_signature.clear(); @@ -1233,7 +1272,7 @@ void ScriptEditorDebugger::stop() { breaked = false; server->stop(); - + _clear_remote_objects(); ppeer->set_stream_peer(Ref<StreamPeer>()); if (connection.is_valid()) { @@ -1261,9 +1300,6 @@ void ScriptEditorDebugger::stop() { EditorNode::get_singleton()->get_scene_tree_dock()->hide_remote_tree(); EditorNode::get_singleton()->get_scene_tree_dock()->hide_tab_buttons(); - Node *node = editor->get_scene_tree_dock()->get_tree_editor()->get_selected(); - editor->push_item(node); - if (hide_on_stop) { if (is_visible_in_tree()) EditorNode::get_singleton()->hide_bottom_panel(); @@ -1659,45 +1695,52 @@ void ScriptEditorDebugger::reload_scripts() { } } -void ScriptEditorDebugger::_error_selected(int p_idx) { +void ScriptEditorDebugger::_error_activated() { + TreeItem *selected = error_tree->get_selected(); + + TreeItem *ci = selected->get_children(); + if (ci) { + selected->set_collapsed(!selected->is_collapsed()); + } +} + +void ScriptEditorDebugger::_error_selected() { + TreeItem *selected = error_tree->get_selected(); - error_stack->clear(); - Array st = error_list->get_item_metadata(p_idx); - for (int i = 0; i < st.size(); i += 3) { + Array meta = selected->get_metadata(0); - String script = st[i]; - String func = st[i + 1]; - int line = st[i + 2]; - Array md; - md.push_back(st[i]); - md.push_back(st[i + 1]); - md.push_back(st[i + 2]); + if (meta.size() == 0) { + return; + } - String str = func; - String tooltip_str = TTR("Function:") + " " + func; - if (script.length() > 0) { - str += " in " + script.get_file(); - tooltip_str = TTR("File:") + " " + script + "\n" + tooltip_str; - if (line > 0) { - str += ":line " + itos(line); - tooltip_str += "\n" + TTR("Line:") + " " + itos(line); - } - } + Ref<Script> s = ResourceLoader::load(meta[0]); + emit_signal("goto_script_line", s, int(meta[1]) - 1); +} + +void ScriptEditorDebugger::_expand_errors_list() { + + TreeItem *root = error_tree->get_root(); + if (!root) + return; - error_stack->add_item(str); - error_stack->set_item_metadata(error_stack->get_item_count() - 1, md); - error_stack->set_item_tooltip(error_stack->get_item_count() - 1, tooltip_str); + TreeItem *item = root->get_children(); + while (item) { + item->set_collapsed(false); + item = item->get_next(); } } -void ScriptEditorDebugger::_error_stack_selected(int p_idx) { +void ScriptEditorDebugger::_collapse_errors_list() { - Array arr = error_stack->get_item_metadata(p_idx); - if (arr.size() != 3) + TreeItem *root = error_tree->get_root(); + if (!root) return; - Ref<Script> s = ResourceLoader::load(arr[0]); - emit_signal("goto_script_line", s, int(arr[2]) - 1); + TreeItem *item = root->get_children(); + while (item) { + item->set_collapsed(true); + item = item->get_next(); + } } void ScriptEditorDebugger::set_hide_on_stop(bool p_hide) { @@ -1754,27 +1797,24 @@ void ScriptEditorDebugger::_clear_remote_objects() { void ScriptEditorDebugger::_clear_errors_list() { - error_list->clear(); + error_tree->clear(); error_count = 0; warning_count = 0; _notification(NOTIFICATION_PROCESS); } // Right click on specific file(s) or folder(s). -void ScriptEditorDebugger::_error_list_item_rmb_selected(int p_item, const Vector2 &p_pos) { +void ScriptEditorDebugger::_error_tree_item_rmb_selected(const Vector2 &p_pos) { item_menu->clear(); item_menu->set_size(Size2(1, 1)); - // Allow specific actions only on one item. - bool single_item_selected = error_list->get_selected_items().size() == 1; - - if (single_item_selected) { + if (error_tree->is_anything_selected()) { item_menu->add_icon_item(get_icon("ActionCopy", "EditorIcons"), TTR("Copy Error"), ITEM_MENU_COPY_ERROR); } if (item_menu->get_item_count() > 0) { - item_menu->set_position(error_list->get_global_position() + p_pos); + item_menu->set_position(error_tree->get_global_position() + p_pos); item_menu->popup(); } } @@ -1784,10 +1824,30 @@ void ScriptEditorDebugger::_item_menu_id_pressed(int p_option) { switch (p_option) { case ITEM_MENU_COPY_ERROR: { - String title = error_list->get_item_text(error_list->get_current()); - String desc = error_list->get_item_tooltip(error_list->get_current()); + TreeItem *ti = error_tree->get_selected(); + while (ti->get_parent() != error_tree->get_root()) + ti = ti->get_parent(); + + String type; + + if (ti->get_icon(0) == get_icon("Warning", "EditorIcons")) { + type = "W "; + } else if (ti->get_icon(0) == get_icon("Error", "EditorIcons")) { + type = "E "; + } + + String text = ti->get_text(0) + " "; + int rpad_len = text.length(); + + text = type + text + ti->get_text(1) + "\n"; + TreeItem *ci = ti->get_children(); + while (ci) { + text += " " + ci->get_text(0).rpad(rpad_len) + ci->get_text(1) + "\n"; + ci = ci->get_next(); + } + + OS::get_singleton()->set_clipboard(text); - OS::get_singleton()->set_clipboard(title + "\n----------\n" + desc); } break; case ITEM_MENU_SAVE_REMOTE_NODE: { @@ -1826,12 +1886,14 @@ void ScriptEditorDebugger::_bind_methods() { ClassDB::bind_method(D_METHOD("_live_edit_clear"), &ScriptEditorDebugger::_live_edit_clear); ClassDB::bind_method(D_METHOD("_error_selected"), &ScriptEditorDebugger::_error_selected); - ClassDB::bind_method(D_METHOD("_error_stack_selected"), &ScriptEditorDebugger::_error_stack_selected); + ClassDB::bind_method(D_METHOD("_error_activated"), &ScriptEditorDebugger::_error_activated); + ClassDB::bind_method(D_METHOD("_expand_errors_list"), &ScriptEditorDebugger::_expand_errors_list); + ClassDB::bind_method(D_METHOD("_collapse_errors_list"), &ScriptEditorDebugger::_collapse_errors_list); ClassDB::bind_method(D_METHOD("_profiler_activate"), &ScriptEditorDebugger::_profiler_activate); ClassDB::bind_method(D_METHOD("_profiler_seeked"), &ScriptEditorDebugger::_profiler_seeked); ClassDB::bind_method(D_METHOD("_clear_errors_list"), &ScriptEditorDebugger::_clear_errors_list); - ClassDB::bind_method(D_METHOD("_error_list_item_rmb_selected"), &ScriptEditorDebugger::_error_list_item_rmb_selected); + ClassDB::bind_method(D_METHOD("_error_tree_item_rmb_selected"), &ScriptEditorDebugger::_error_tree_item_rmb_selected); ClassDB::bind_method(D_METHOD("_item_menu_id_pressed"), &ScriptEditorDebugger::_item_menu_id_pressed); ClassDB::bind_method(D_METHOD("_paused"), &ScriptEditorDebugger::_paused); @@ -1946,16 +2008,14 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { stack_dump->connect("cell_selected", this, "_stack_dump_frame_selected"); sc->add_child(stack_dump); - inspector = memnew(PropertyEditor); + inspector = memnew(EditorInspector); inspector->set_h_size_flags(SIZE_EXPAND_FILL); - inspector->hide_top_label(); - inspector->get_property_tree()->set_column_title(0, TTR("Variable")); inspector->set_enable_capitalize_paths(false); inspector->set_read_only(true); inspector->connect("object_id_selected", this, "_scene_tree_property_select_object"); sc->add_child(inspector); - server = TCP_Server::create_ref(); + server.instance(); pending_in_queue = 0; @@ -1967,44 +2027,52 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { } { //errors - - error_split = memnew(HSplitContainer); VBoxContainer *errvb = memnew(VBoxContainer); + errvb->set_name(TTR("Errors")); + HBoxContainer *errhb = memnew(HBoxContainer); - errvb->set_h_size_flags(SIZE_EXPAND_FILL); - Label *velb = memnew(Label(TTR("Errors:"))); - velb->set_h_size_flags(SIZE_EXPAND_FILL); - errhb->add_child(velb); + errvb->add_child(errhb); + + Button *expand_all = memnew(Button); + expand_all->set_text(TTR("Expand All")); + expand_all->connect("pressed", this, "_expand_errors_list"); + errhb->add_child(expand_all); + + Button *collapse_all = memnew(Button); + collapse_all->set_text(TTR("Collapse All")); + collapse_all->connect("pressed", this, "_collapse_errors_list"); + errhb->add_child(collapse_all); + + Control *space = memnew(Control); + space->set_h_size_flags(SIZE_EXPAND_FILL); + errhb->add_child(space); clearbutton = memnew(Button); clearbutton->set_text(TTR("Clear")); + clearbutton->set_h_size_flags(0); clearbutton->connect("pressed", this, "_clear_errors_list"); errhb->add_child(clearbutton); - errvb->add_child(errhb); - error_list = memnew(ItemList); - error_list->set_v_size_flags(SIZE_EXPAND_FILL); - error_list->set_h_size_flags(SIZE_EXPAND_FILL); - error_list->connect("item_rmb_selected", this, "_error_list_item_rmb_selected"); - error_list->set_allow_rmb_select(true); - error_list->set_autoscroll_to_bottom(true); + error_tree = memnew(Tree); + error_tree->set_columns(2); - item_menu = memnew(PopupMenu); - item_menu->connect("id_pressed", this, "_item_menu_id_pressed"); - error_list->add_child(item_menu); + error_tree->set_column_expand(0, false); + error_tree->set_column_min_width(0, 140); - errvb->add_child(error_list); + error_tree->set_column_expand(1, true); - error_split->add_child(errvb); + error_tree->set_select_mode(Tree::SELECT_ROW); + error_tree->set_hide_root(true); + error_tree->set_v_size_flags(SIZE_EXPAND_FILL); + error_tree->set_allow_rmb_select(true); + error_tree->connect("item_rmb_selected", this, "_error_tree_item_rmb_selected"); + errvb->add_child(error_tree); - errvb = memnew(VBoxContainer); - errvb->set_h_size_flags(SIZE_EXPAND_FILL); - error_stack = memnew(ItemList); - errvb->add_margin_child(TTR("Stack Trace (if applicable):"), error_stack, true); - error_split->add_child(errvb); + item_menu = memnew(PopupMenu); + item_menu->connect("id_pressed", this, "_item_menu_id_pressed"); + error_tree->add_child(item_menu); - error_split->set_name(TTR("Errors")); - tabs->add_child(error_split); + tabs->add_child(errvb); } { // remote scene tree @@ -2180,7 +2248,6 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { ScriptEditorDebugger::~ScriptEditorDebugger() { - //inspector->edit(NULL); memdelete(variables); ppeer->set_stream_peer(Ref<StreamPeer>()); diff --git a/editor/script_editor_debugger.h b/editor/script_editor_debugger.h index ce705aa35b..cebf6d785e 100644 --- a/editor/script_editor_debugger.h +++ b/editor/script_editor_debugger.h @@ -33,12 +33,12 @@ #include "core/io/packet_peer.h" #include "core/io/tcp_server.h" -#include "property_editor.h" +#include "editor/editor_inspector.h" +#include "editor/property_editor.h" #include "scene/gui/box_container.h" #include "scene/gui/button.h" class Tree; -class PropertyEditor; class EditorNode; class ScriptEditorDebuggerVariables; class LineEdit; @@ -88,8 +88,7 @@ class ScriptEditorDebugger : public Control { Set<ObjectID> unfold_cache; HSplitContainer *error_split; - ItemList *error_list; - ItemList *error_stack; + Tree *error_tree; Tree *inspect_scene_tree; Button *clearbutton; PopupMenu *item_menu; @@ -130,7 +129,7 @@ class ScriptEditorDebugger : public Control { LineEdit *vmem_total; Tree *stack_dump; - PropertyEditor *inspector; + EditorInspector *inspector; Ref<TCP_Server> server; Ref<StreamPeerTCP> connection; @@ -179,8 +178,11 @@ class ScriptEditorDebugger : public Control { void _method_changed(Object *p_base, const StringName &p_name, VARIANT_ARG_DECLARE); void _property_changed(Object *p_base, const StringName &p_property, const Variant &p_value); - void _error_selected(int p_idx); - void _error_stack_selected(int p_idx); + void _error_activated(); + void _error_selected(); + + void _expand_errors_list(); + void _collapse_errors_list(); void _profiler_activate(bool p_enable); void _profiler_seeked(); @@ -191,7 +193,7 @@ class ScriptEditorDebugger : public Control { void _clear_remote_objects(); void _clear_errors_list(); - void _error_list_item_rmb_selected(int p_item, const Vector2 &p_pos); + void _error_tree_item_rmb_selected(const Vector2 &p_pos); void _item_menu_id_pressed(int p_option); protected: diff --git a/editor/settings_config_dialog.cpp b/editor/settings_config_dialog.cpp index 4ebba73cb3..537c9ac6b8 100644 --- a/editor/settings_config_dialog.cpp +++ b/editor/settings_config_dialog.cpp @@ -30,11 +30,11 @@ #include "settings_config_dialog.h" +#include "core/os/keyboard.h" +#include "core/project_settings.h" #include "editor_file_system.h" #include "editor_node.h" #include "editor_settings.h" -#include "os/keyboard.h" -#include "project_settings.h" #include "scene/gui/margin_container.h" #include "script_editor_debugger.h" @@ -56,11 +56,7 @@ void EditorSettingsDialog::_settings_property_edited(const String &p_name) { String full_name = inspector->get_full_item_path(p_name); - // Small usability workaround to update the text color settings when the - // color theme is changed - if (full_name == "text_editor/theme/color_theme") { - inspector->get_inspector()->update_tree(); - } else if (full_name == "interface/theme/accent_color" || full_name == "interface/theme/base_color" || full_name == "interface/theme/contrast") { + if (full_name == "interface/theme/accent_color" || full_name == "interface/theme/base_color" || full_name == "interface/theme/contrast") { EditorSettings::get_singleton()->set_manually("interface/theme/preset", "Custom"); // set preset to Custom } else if (full_name.begins_with("text_editor/highlighting")) { EditorSettings::get_singleton()->set_manually("text_editor/theme/color_theme", "Custom"); @@ -365,7 +361,7 @@ void EditorSettingsDialog::_tabs_tab_changed(int p_tab) { void EditorSettingsDialog::_focus_current_search_box() { Control *tab = tabs->get_current_tab_control(); - LineEdit *current_search_box; + LineEdit *current_search_box = NULL; if (tab == tab_general) current_search_box = search_box; else if (tab == tab_shortcuts) diff --git a/editor/spatial_editor_gizmos.cpp b/editor/spatial_editor_gizmos.cpp index 64638cdb1e..881f20cecb 100644 --- a/editor/spatial_editor_gizmos.cpp +++ b/editor/spatial_editor_gizmos.cpp @@ -30,12 +30,32 @@ #include "spatial_editor_gizmos.h" -#include "geometry.h" -#include "quick_hull.h" -#include "scene/3d/camera.h" +#include "core/math/geometry.h" +#include "core/math/quick_hull.h" +#include "scene/3d/audio_stream_player_3d.h" +#include "scene/3d/baked_lightmap.h" +#include "scene/3d/collision_polygon.h" +#include "scene/3d/collision_shape.h" +#include "scene/3d/gi_probe.h" +#include "scene/3d/light.h" +#include "scene/3d/listener.h" +#include "scene/3d/mesh_instance.h" +#include "scene/3d/navigation_mesh.h" +#include "scene/3d/particles.h" +#include "scene/3d/physics_joint.h" +#include "scene/3d/portal.h" +#include "scene/3d/position_3d.h" +#include "scene/3d/ray_cast.h" +#include "scene/3d/reflection_probe.h" +#include "scene/3d/room_instance.h" #include "scene/3d/soft_body.h" +#include "scene/3d/spring_arm.h" +#include "scene/3d/sprite_3d.h" +#include "scene/3d/vehicle_body.h" +#include "scene/3d/visibility_notifier.h" #include "scene/resources/box_shape.h" #include "scene/resources/capsule_shape.h" +#include "scene/resources/concave_polygon_shape.h" #include "scene/resources/convex_polygon_shape.h" #include "scene/resources/cylinder_shape.h" #include "scene/resources/plane_shape.h" @@ -44,17 +64,8 @@ #include "scene/resources/sphere_shape.h" #include "scene/resources/surface_tool.h" -// Keep small children away from this file. -// It's so ugly it will eat them alive - -// 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(); -} bool EditorSpatialGizmo::is_editable() const { ERR_FAIL_COND_V(!spatial_node, false); @@ -704,6 +715,7 @@ EditorSpatialGizmo::EditorSpatialGizmo() { instanced = false; spatial_node = NULL; gizmo_plugin = NULL; + selectable_icon_size = -1.0f; } EditorSpatialGizmo::~EditorSpatialGizmo() { @@ -898,7 +910,6 @@ void LightSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { } p_gizmo->add_lines(lines, material); - p_gizmo->add_collision_segments(lines); p_gizmo->add_unscaled_billboard(icon, 0.05); } @@ -929,8 +940,6 @@ void LightSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { } p_gizmo->add_lines(points, material, true); - p_gizmo->add_collision_segments(points); - p_gizmo->add_unscaled_billboard(icon, 0.05); Vector<Vector3> handles; @@ -972,38 +981,14 @@ void LightSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { p_gizmo->add_lines(points, material); + float ra = 16 * Math_PI * 2.0 / 64.0; + Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * w; + Vector<Vector3> handles; handles.push_back(Vector3(0, 0, -r)); - - 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)) * w; - Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * w; - - collision_segments.push_back(Vector3(a.x, a.y, -d)); - collision_segments.push_back(Vector3(b.x, b.y, -d)); - - if (i % 16 == 0) { - - collision_segments.push_back(Vector3(a.x, a.y, -d)); - collision_segments.push_back(Vector3()); - } - - if (i == 16) { - - handles.push_back(Vector3(a.x, a.y, -d)); - } - } - - collision_segments.push_back(Vector3(0, 0, -r)); - collision_segments.push_back(Vector3()); + handles.push_back(Vector3(a.x, a.y, -d)); p_gizmo->add_handles(handles, get_material("handles")); - p_gizmo->add_collision_segments(collision_segments); p_gizmo->add_unscaled_billboard(icon, 0.05); } } @@ -1139,7 +1124,6 @@ void AudioStreamPlayer3DSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) } p_gizmo->add_lines(points, material); - p_gizmo->add_collision_segments(points); Vector<Vector3> handles; float ha = Math::deg2rad(player->get_emission_angle()); @@ -1334,7 +1318,6 @@ void CameraSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { } 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")); @@ -1377,7 +1360,6 @@ void CameraSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { } p_gizmo->add_lines(lines, material); - p_gizmo->add_collision_segments(lines); } } @@ -1722,8 +1704,16 @@ void PhysicalBoneSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { return; Skeleton *sk(physical_bone->find_skeleton_parent()); + if (!sk) + return; + PhysicalBone *pb(sk->get_physical_bone(physical_bone->get_bone_id())); + if (!pb) + return; + PhysicalBone *pbp(sk->get_physical_bone_parent(physical_bone->get_bone_id())); + if (!pbp) + return; Vector<Vector3> points; @@ -2105,12 +2095,10 @@ void SoftBodySpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { Vector<Vector3> points; soft_body->get_mesh()->generate_debug_mesh_indices(points); - soft_body->get_mesh()->clear_cache(); Ref<Material> material = get_material("shape_material", p_gizmo); 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); } @@ -2427,7 +2415,6 @@ void ParticlesGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { Ref<Material> icon = get_material("particles_icon", 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("particles_solid_material", p_gizmo); @@ -2612,7 +2599,6 @@ void ReflectionProbeGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { } p_gizmo->add_unscaled_billboard(icon, 0.05); - p_gizmo->add_collision_segments(lines); p_gizmo->add_handles(handles, get_material("handles")); } @@ -2727,7 +2713,6 @@ void GIProbeGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { } p_gizmo->add_lines(lines, material); - p_gizmo->add_collision_segments(lines); lines.clear(); @@ -2897,7 +2882,6 @@ void BakedIndirectLightGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { } p_gizmo->add_lines(lines, material); - p_gizmo->add_collision_segments(lines); Vector<Vector3> handles; @@ -3470,10 +3454,9 @@ void CollisionShapeSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { if (points.size() > 3) { - QuickHull qh; Vector<Vector3> varr = Variant(points); Geometry::MeshData md; - Error err = qh.build(varr, md); + Error err = QuickHull::build(varr, md); if (err == OK) { Vector<Vector3> points; points.resize(md.edges.size() * 2); @@ -3488,6 +3471,14 @@ void CollisionShapeSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { } } + if (Object::cast_to<ConcavePolygonShape>(*s)) { + + Ref<ConcavePolygonShape> cs = s; + Ref<ArrayMesh> mesh = cs->get_debug_mesh()->duplicate(); + mesh->surface_set_material(0, material); + p_gizmo->add_mesh(mesh); + } + if (Object::cast_to<RayShape>(*s)) { Ref<RayShape> rs = s; @@ -4209,21 +4200,16 @@ void JointSpatialGizmoPlugin::CreateGeneric6DOFJointGizmo( float cs = 0.25; for (int ax = 0; ax < 3; ax++) { - /*r_points.push_back(p_offset.translated(Vector3(+cs,0,0)).origin); - r_points.push_back(p_offset.translated(Vector3(-cs,0,0)).origin); - r_points.push_back(p_offset.translated(Vector3(0,+cs,0)).origin); - r_points.push_back(p_offset.translated(Vector3(0,-cs,0)).origin); - r_points.push_back(p_offset.translated(Vector3(0,0,+cs*2)).origin); - r_points.push_back(p_offset.translated(Vector3(0,0,-cs*2)).origin); */ - - float ll; - float ul; - float lll; - float lul; - - int a1, a2, a3; - bool enable_ang; - bool enable_lin; + float ll = 0; + float ul = 0; + float lll = 0; + float lul = 0; + + int a1 = 0; + int a2 = 0; + int a3 = 0; + bool enable_ang = false; + bool enable_lin = false; switch (ax) { case 0: diff --git a/editor/spatial_editor_gizmos.h b/editor/spatial_editor_gizmos.h index 6f29e9d999..371e3bc689 100644 --- a/editor/spatial_editor_gizmos.h +++ b/editor/spatial_editor_gizmos.h @@ -32,27 +32,7 @@ #define SPATIAL_EDITOR_GIZMOS_H #include "editor/plugins/spatial_editor_plugin.h" -#include "scene/3d/audio_stream_player_3d.h" -#include "scene/3d/baked_lightmap.h" #include "scene/3d/camera.h" -#include "scene/3d/collision_polygon.h" -#include "scene/3d/collision_shape.h" -#include "scene/3d/gi_probe.h" -#include "scene/3d/light.h" -#include "scene/3d/listener.h" -#include "scene/3d/mesh_instance.h" -#include "scene/3d/navigation_mesh.h" -#include "scene/3d/particles.h" -#include "scene/3d/physics_joint.h" -#include "scene/3d/portal.h" -#include "scene/3d/position_3d.h" -#include "scene/3d/ray_cast.h" -#include "scene/3d/reflection_probe.h" -#include "scene/3d/room_instance.h" -#include "scene/3d/spring_arm.h" -#include "scene/3d/sprite_3d.h" -#include "scene/3d/vehicle_body.h" -#include "scene/3d/visibility_notifier.h" class Camera; diff --git a/editor/translations/extract.py b/editor/translations/extract.py index 0dee1819dd..ebb032fd6f 100755 --- a/editor/translations/extract.py +++ b/editor/translations/extract.py @@ -88,7 +88,7 @@ def process_file(f, fname): unique_str.append(msg) unique_loc[msg] = [location] elif (not location in unique_loc[msg]): - # Add additional location to previous occurence too + # Add additional location to previous occurrence too msg_pos = main_po.find('\nmsgid "' + msg + '"') if (msg_pos == -1): print("Someone apparently thought writing Python was as easy as GDScript. Ping Akien.") diff --git a/gles_builders.py b/gles_builders.py index b5a2b24aa3..f9fd6d3fa2 100644 --- a/gles_builders.py +++ b/gles_builders.py @@ -59,21 +59,18 @@ def include_file_in_legacygl_header(filename, header_data, depth): 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: + if include_file_in_legacygl_header(included_file, header_data, depth + 1) is 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: + if include_file_in_legacygl_header(included_file, header_data, depth + 1) is 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: 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_")] @@ -232,7 +229,11 @@ def build_legacygl_header(filename, include, class_suffix, output_attribs, gles2 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#ifdef DEBUG_ENABLED\n ") + fd.write("\t#define _FU if (get_uniform(p_uniform)<0) return; if (!is_version_valid()) return; ERR_FAIL_COND( get_active()!=this ); \n\n ") + fd.write("\t#else\n ") + fd.write("\t#define _FU if (get_uniform(p_uniform)<0) return; \n\n ") + fd.write("\t#endif\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") diff --git a/main/SCsub b/main/SCsub index 9af102600e..e7fe6ab4e1 100644 --- a/main/SCsub +++ b/main/SCsub @@ -1,6 +1,7 @@ #!/usr/bin/env python Import('env') + from platform_methods import run_in_subprocess import main_builders @@ -15,8 +16,6 @@ env.CommandNoCache("#main/default_controller_mappings.gen.cpp", controller_datab 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", run_in_subprocess(main_builders.make_splash)) diff --git a/main/gamecontrollerdb.txt b/main/gamecontrollerdb.txt index 80687bd037..2bbee0283a 100644 --- a/main/gamecontrollerdb.txt +++ b/main/gamecontrollerdb.txt @@ -1,10 +1,12 @@ -# Game Controller DB for SDL in 2.0.6 format +# Game Controller DB for SDL in 2.0.6+ format # Source: https://github.com/gabomdq/SDL_GameControllerDB # Windows 03000000fa2d00000100000000000000,3DRUDDER,leftx:a0,lefty:a1,rightx:a5,righty:a2,platform:Windows, 03000000022000000090000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000203800000900000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000060000000000000,8Bitdo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000061000000000000,8Bitdo SF30 Pro Wireless,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000102800000900000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, 03000000a00500003232000000000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows, 030000008f0e00001200000000000000,Acme,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows, @@ -14,7 +16,8 @@ 0300000066f700000500000000000000,BrutalLegendTest,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows, 03000000d81d00000b00000000000000,BUFFALO BSGP1601 Series ,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Windows, 03000000e82000006058000000000000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, -030000005e0400008e02000000000000,Controller (XBOX 360 For Windows),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000005e040000a102000000000000,Controller (Xbox 360 Wireless Receiver for Windows),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000005e0400008e02000000000000,Controller (XBOX 360 For Windows),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000260900008888000000000000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a4,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Windows, 03000000a306000022f6000000000000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows, 03000000791d00000103000000000000,Dual Box WII,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, @@ -77,7 +80,7 @@ 030000001008000001e5000000000000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Windows, 03000000bd12000015d0000000000000,Nintendo Retrolink USB Super SNES Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows, 030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, -030000004b120000014d000000000000,NYKO AIRFLO,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a3,leftstick:a0,lefttrigger:b6,leftx:h0.6,lefty:h0.12,rightshoulder:b5,rightstick:a2,righttrigger:b7,rightx:h0.9,righty:h0.4,start:b9,x:b2,y:b3,platform:Windows, +030000004b120000014d000000000000,NYKO AIRFLO,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a3,leftstick:a0,lefttrigger:b6,rightshoulder:b5,rightstick:a2,righttrigger:b7,start:b9,x:b2,y:b3,platform:Windows, 03000000362800000100000000000000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,x:b1,y:b2,platform:Windows, 03000000120c0000f60e000000000000,P4 Wired Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows, 030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, @@ -147,9 +150,11 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, # Mac OS X 03000000022000000090000001000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00000190000001000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000102800000900000000000000,8Bitdo SFC30 GamePad Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000a00500003232000009010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X, 030000008305000031b0000000000000,Cideko AK08b,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000260900008888000088020000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Mac OS X, 03000000a306000022f6000001030000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X, @@ -167,6 +172,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000008f0e00001330000011010000,HuiJia SNES Controller,a:b4,b:b2,back:b16,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b12,rightshoulder:b14,start:b18,x:b6,y:b0,platform:Mac OS X, 03000000830500006020000000010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X, 03000000830500006020000000000000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X, +030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Mac OS X, 030000006d04000016c2000000020000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000006d04000016c2000000030000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000006d04000016c2000014040000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, @@ -183,6 +189,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000001008000001e5000006010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Mac OS X, 030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, 030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Mac OS X, +03000000d62000006dca000000010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X, 030000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X, 030000004c050000a00b000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, @@ -195,6 +202,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000790000001100000000000000,Retrolink Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a3,lefty:a4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X, 03000000790000001100000006010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X, 030000006b140000010d000000010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000c6240000fefa000000000000,Rock Candy Gamepad for PS3,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 03000000811700007e05000000000000,Sega Saturn,a:b2,b:b4,dpdown:b16,dpleft:b15,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,leftx:a0,lefty:a2,rightshoulder:b9,righttrigger:a4,start:b13,x:b0,y:b6,platform:Mac OS X, 03000000b40400000a01000000000000,Sega Saturn USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Mac OS X, 030000003512000021ab000000000000,SFC30 Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, @@ -229,11 +237,13 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000022000000090000011010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00002038000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +05000000c82d00000061000000010000,8Bitdo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d00000190000011010000,8Bitdo NES30 Pro 8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000102800000900000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00003028000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, 05000000a00500003232000001000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux, 05000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux, +030000006f0e00003901000000430000,Afterglow Prismatic Wired Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006f0e00003901000020060000,Afterglow Wired Controller for Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000100000008200000011010000,Akishop Customs PS360+ v1.66,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 05000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux, @@ -250,6 +260,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000006f0e00001304000000010000,Generic X-Box pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:a0,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:a3,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006f0e00001f01000000010000,Generic X-Box pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000f0250000c183000010010000,Goodbetterbest Ltd USB Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +0300000079000000d418000000010000,GPD Win 2 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000280400000140000000010000,Gravis GamePad Pro USB ,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 030000008f0e00000610000000010000,GreenAsia Electronics 4Axes 12Keys GamePad ,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Linux, 030000008f0e00000300000010010000,GreenAsia Inc. USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, @@ -274,6 +285,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 050000006964726f69643a636f6e0000,idroid:con,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000b50700001503000010010000,impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux, 03000000fd0500000030000000010000,InterAct GoPad I-73000 (Fighting Game Layout),a:b3,b:b4,back:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b7,x:b0,y:b1,platform:Linux, +0500000049190000020400001b010000,Ipega PG-9069 - Bluetooth Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b161,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 030000006e0500000320000010010000,JC-U3613M - DirectInput Mode,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Linux, 03000000300f00001001000010010000,Jess Tech Dual Analog Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux, 03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, @@ -301,10 +313,13 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000ad1b000016f0000090040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000380700001888000010010000,MadCatz PC USB Wired Stick 8818,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000380700003888000010010000,MadCatz PC USB Wired Stick 8838,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:a0,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +0300000079000000d218000011010000,MAGIC-NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000790000004418000010010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux, 03000000780000000600000010010000,Microntek USB Joystick,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux, +030000005e0400000e00000000010000,Microsoft SideWinder,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux, 030000005e0400008e02000004010000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e0400008e02000062230000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000e302000003020000,Microsoft X-Box One Elite pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000d102000001010000,Microsoft X-Box One pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000d102000003020000,Microsoft X-Box One pad v2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e0400008502000000010000,Microsoft X-Box pad (Japan),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux, @@ -323,6 +338,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 05000000362800000100000003010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux, 03000000ff1100003133000010010000,PC Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 030000006f0e00006401000001010000,PDP Battlefield One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000d62000006dca000011010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000ff1100004133000010010000,PS2 Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux, 03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000004c0500006802000010010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, @@ -367,6 +383,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000a306000018f5000010010000,Saitek PLC Saitek P3200 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux, 03000000c01600008704000011010000,Serial/Keyboard/Mouse/Joystick,a:b12,b:b10,back:b4,dpdown:b2,dpleft:b3,dpright:b1,dpup:b0,leftshoulder:b9,leftstick:b14,lefttrigger:b6,leftx:a1,lefty:a0,rightshoulder:b8,rightstick:b15,righttrigger:b7,rightx:a2,righty:a3,start:b5,x:b13,y:b11,platform:Linux, 03000000f025000021c1000010010000,ShanWan Gioteck PS3 Wired Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +03000000632500007505000010010000,SHANWAN PS3/PC Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 03000000250900000500000000010000,Sony PS2 pad with SmartJoy adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux, 030000005e0400008e02000073050000,Speedlink TORID Wireless Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, @@ -398,6 +415,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000005e040000a102000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000a102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 0000000058626f782033363020576900,Xbox 360 Wireless Controller,a:b0,b:b1,back:b14,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b7,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Linux, +030000005e040000a102000014010000,Xbox 360 Wireless Receiver (XBOX),a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 0000000058626f782047616d65706100,Xbox Gamepad (userspace driver),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, 050000005e040000e002000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 050000005e040000fd02000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, @@ -420,4 +438,4 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, # iOS 4d466947616d65706164010000000000,MFi Extended Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:iOS, 4d466947616d65706164020000000000,MFi Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b6,x:b2,y:b3,platform:iOS, -05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:iOS,
\ No newline at end of file +05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:iOS, diff --git a/main/gamecontrollerdb_204.txt b/main/gamecontrollerdb_204.txt index 7f3068d1b1..7fbe925b25 100644 --- a/main/gamecontrollerdb_204.txt +++ b/main/gamecontrollerdb_204.txt @@ -58,9 +58,9 @@ d81d1000000000000000504944564944,iBUFFALO BSGP1204P Series,a:b2,b:b1,back:b8,dpd 2509e803000000000000504944564944,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows, 79000018000000000000504944564944,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 8f0e0d31000000000000504944564944,Multilaser JS071 USB,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -100801e5000000000000504944564944,NEXT Classic USB Game Controller,a:b0,b:b1,back:b8,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b9,platform:Windows, +100801e5000000000000504944564944,NEXT Classic USB Game Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, bd1215d0000000000000504944564944,Nintendo Retrolink USB Super SNES Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows, -4b12014d000000000000504944564944,NYKO AIRFLO,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a3,leftstick:a0,lefttrigger:b6,leftx:h0.6,lefty:h0.12,rightshoulder:b5,rightstick:a2,righttrigger:b7,rightx:h0.9,righty:h0.4,start:b9,x:b2,y:b3,platform:Windows, +4b12014d000000000000504944564944,NYKO AIRFLO,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a3,leftstick:a0,lefttrigger:b6,rightshoulder:b5,rightstick:a2,righttrigger:b7,start:b9,x:b2,y:b3,platform:Windows, 36280100000000000000504944564944,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b15,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,start:b14,x:b1,y:b2,platform:Windows, 4d6963726f736f66742050432d6a6f79,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a5,righty:a4,x:b1,y:b2,platform:Windows, 120cf60e000000000000504944564944,P4 Wired Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows, @@ -196,7 +196,7 @@ bd1200000000000015d0000000000000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,lef 030000005e0400008502000000010000,Microsoft X-Box pad (Japan),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux, 030000005e0400008902000021010000,Microsoft X-Box pad v2 (US),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux, 05000000d6200000ad0d000001000000,Moga Pro,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux, -030000001008000001e5000010010000,NEXT Classic USB Game Controller,a:b0,b:b1,back:b8,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b9,platform:Linux, +030000001008000001e5000010010000,NEXT Classic USB Game Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 050000007e0500003003000001000000,Nintendo Wii Remote Pro Controller,a:b1,b:b0,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 05000000010000000100000003000000,Nintendo Wiimote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 03000000550900001072000011010000,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, @@ -266,4 +266,4 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, # iOS 4d466947616d65706164010000000000,MFi Extended Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:iOS, -4d466947616d65706164020000000000,MFi Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b6,x:b2,y:b3,platform:iOS,
\ No newline at end of file +4d466947616d65706164020000000000,MFi Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b6,x:b2,y:b3,platform:iOS, diff --git a/main/gamecontrollerdb_205.txt b/main/gamecontrollerdb_205.txt index 4c3eae623c..55c45eb148 100644 --- a/main/gamecontrollerdb_205.txt +++ b/main/gamecontrollerdb_205.txt @@ -2,332 +2,332 @@ # Source: https://github.com/gabomdq/SDL_GameControllerDB # Windows -xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, -ffff0000000000000000504944564944,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +03000000022000000090000000000000,8Bitdo NES30 PRO USB,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000203800000900000000000000,8Bitdo NES30 PRO Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, +03000000102800000900000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, +10280900000000000000504944564944,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, +030000008f0e00001200000000000000,Acme,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows, +03000000341a00003608000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +341a3608000000000000504944564944,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000c01100001352000000000000,Battalife Joystick,a:b6,b:b7,back:b2,leftshoulder:b0,leftx:a0,lefty:a1,rightshoulder:b1,start:b3,x:b4,y:b5,platform:Windows, +030000006b1400000055000000000000,bigben ps3padstreetnew,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +0300000066f700000500000000000000,BrutalLegendTest,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows, +03000000d81d00000b00000000000000,BUFFALO BSGP1601 Series ,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Windows, e8206058000000000000504944564944,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, -88880803000000000000504944564944,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows, +030000005e0400008e02000000000000,Controller (XBOX 360 For Windows),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000004f04000023b3000000000000,Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000341a00000108000000000000,EXEQ RF USB Gamepad 8206,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +030000000d0f00008500000000000000,Fighting Commander 2016 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00005e00000000000000,Fighting Commander 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:a3,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00005f00000000000000,Fighting Commander 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00008400000000000000,Fighting Commander 5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00008700000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00008800000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows, +030000000d0f00002700000000000000,FIGHTING STICK V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 78696e70757403000000000000000000,Fightstick TES,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Windows, -6d0419c2000000000000504944564944,Logitech F710 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -6d0418c2000000000000504944564944,Logitech F510 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -6d0416c2000000000000504944564944,Generic DirectInput Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -4d6963726f736f66742050432d6a6f79,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a5,righty:a4,x:b1,y:b2,platform:Windows, -4c05c405000000000000504944564944,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -4c056802000000000000504944564944,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Windows, -341a3608000000000000504944564944,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -25090500000000000000504944564944,PS3 DualShock,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,platform:Windows, -10280900000000000000504944564944,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, +03000000790000000600000000000000,G-Shark GS-GP702,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows, +03000000260900002625000000000000,Gamecube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,lefttrigger:a4,leftx:a0,lefty:a1,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Windows, +030000008f0e00000d31000000000000,GAMEPAD 3 TURBO,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000280400000140000000000000,GamePad Pro USB,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 03000000ffff00000000000000000000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, -03000000ff1100003133000000000000,SVEN X-PAD,a:b2,b:b3,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a4,start:b5,x:b0,y:b1,platform:Windows, -03000000fa1900000706000000000000,Team 5,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, -03000000e30500009605000000000000,PS to USB convert cable,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, -03000000d90400000200000000000000,TwinShock PS2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, -03000000d81d00001000000000000000,iBUFFALO BSGP1204P Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, -03000000d81d00000f00000000000000,iBUFFALO BSGP1204 Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, -03000000d81d00000b00000000000000,BUFFALO BSGP1601 Series ,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Windows, +ffff0000000000000000504944564944,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +030000006d04000016c2000000000000,Generic DirectInput Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +6d0416c2000000000000504944564944,Generic DirectInput Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000451300000010000000000000,Generic USB Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +03000000341a00000302000000000000,Hama Scorpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00004900000000000000,Hatsune Miku Sho Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000d81400000862000000000000,HitBox Edition Cthulhu+,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows, -03000000d62000006dca000000000000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000c01100001352000000000000,Battalife Joystick,a:b6,b:b7,back:b2,leftshoulder:b0,leftx:a0,lefty:a1,rightshoulder:b1,start:b3,x:b4,y:b5,platform:Windows, -03000000bd12000015d0000000000000,Nintendo Retrolink USB Super SNES Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows, -03000000b50700001403000000000000,IMPACT BLACK,a:b2,b:b3,back:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, -03000000b50700001203000000000000,Techmobility X6-38V,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, -03000000a306000023f6000000000000,Saitek Cyborg V.1 Game pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows, -03000000a30600001af5000000000000,Saitek Cyborg,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows, -03000000a30600000cff000000000000,Saitek P2500,a:b2,b:b3,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b9,rightx:a2,righty:a3,start:b4,x:b0,y:b1,platform:Windows, -03000000a30600000c04000000000000,Saitek P2900,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows, -03000000a30600000b04000000000000,Saitek P990,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows, -030000009b2800000500000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows, -030000008f0e00007530000000000000,PS (R) Gamepad,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b1,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -030000008f0e00001200000000000000,Acme,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows, -030000008f0e00001030000000000000,Mayflash USB Adapter for original Sega Saturn controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,rightshoulder:b2,righttrigger:b7,start:b9,x:b3,y:b4,platform:Windows, -030000008f0e00000d31000000000000,GAMEPAD 3 TURBO,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -030000008f0e00000800000000000000,SpeedLink Strike FX Wireless,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, -030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, -03000000888800000803000000000000,PS3,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Windows, +030000000d0f00004000000000000000,Hori Fighting Stick Mini 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00006e00000000000000,HORIPAD 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00004d00000000000000,HORIPAD3 A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000250900000017000000000000,HRAP2 on PS/SS/N64 Joypad to USB BOX,a:b2,b:b1,back:b9,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b8,x:b3,y:b0,platform:Windows, +03000000d81d00000f00000000000000,iBUFFALO BSGP1204 Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +03000000d81d00001000000000000000,iBUFFALO BSGP1204P Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000830500006020000000000000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Windows, -030000008305000031b0000000000000,MaxfireBlaze3,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, -03000000790000004318000000000000,Mayflash GameCube Controller Adapter,a:b1,b:b2,back:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b0,leftshoulder:b4,leftstick:b0,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b0,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows, -03000000790000001b18000000000000,Venom Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, -03000000790000001100000000000000,Sega Saturn Gamepad,a:b1,b:b2,leftshoulder:b6,lefttrigger:b3,leftx:a0,lefty:a4,rightshoulder:b7,righttrigger:b0,start:b8,x:b4,y:b5,platform:Windows, -03000000790000000600000000000000,G-Shark GS-GP702,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows, -03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000786901006e70000000000000,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +03000000b50700001403000000000000,IMPACT BLACK,a:b2,b:b3,back:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, 030000006f0e00002401000000000000,INJUSTICE FightStick for PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, -030000006f0e00001e01000000000000,Rock Candy Gamepad for PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000491900000204000000000000,Ipega PG-9023,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 030000006d04000019c2000000000000,Logitech Cordless RumblePad 2 USB,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -030000006d04000018c2000000000000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -030000006d04000016c2000000000000,Generic DirectInput Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006d04000011c2000000000000,Logitech Cordless Wingman,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b5,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b2,righttrigger:b7,rightx:a3,righty:a4,x:b4,platform:Windows, -030000006b1400000055000000000000,bigben ps3padstreetnew,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, -0300000066f700000500000000000000,BrutalLegendTest,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows, -03000000666600000488000000000000,TigerGame PS/PS2 Game Controller Adapter,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, -03000000632500002305000000000000,USB Vibration Joystick (BM),a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, -030000005e0400008e02000000000000,Controller (XBOX 360 For Windows),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, -030000004f04000023b3000000000000,Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -030000004f04000015b3000000000000,Thrustmaster Dual Analog 2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows, -030000004f04000004b3000000000000,Thrustmaster Firestorm Dual Power 3,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows, -030000004f04000003d0000000000000,run'n'drive,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,leftshoulder:a3,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:a4,rightstick:b11,righttrigger:b5,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Windows, -030000004c050000cc09000000000000,Sony DualShock 4,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -030000004c050000a00b000000000000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Windows, -030000004b120000014d000000000000,NYKO AIRFLO,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a3,leftstick:a0,lefttrigger:b6,leftx:h0.6,lefty:h0.12,rightshoulder:b5,rightstick:a2,righttrigger:b7,rightx:h0.9,righty:h0.4,start:b9,x:b2,y:b3,platform:Windows, -03000000491900000204000000000000,Ipega PG-9023,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, -03000000451300000010000000000000,Generic USB Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, -03000000380700008532000000000000,Madcatz Arcade Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000380700008483000000000000,Mad Catz FightStick TE S+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b6,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +6d0418c2000000000000504944564944,Logitech F510 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +6d0419c2000000000000504944564944,Logitech F710 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000006d04000018c2000000000000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000380700005032000000000000,Mad Catz FightPad PRO PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000380700005082000000000000,Mad Catz FightPad PRO PS4,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000380700008433000000000000,Mad Catz FightStick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, -03000000380700008184000000000000,Mad Catz FightStick TE2+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000380700008483000000000000,Mad Catz FightStick TE S+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b6,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000380700008134000000000000,Mad Catz FightStick TE2+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000380700008084000000000000,Mad Catz TE2 PS4 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000380700008081000000000000,MADCATZ SFV Arcade FightStick Alpha PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000380700008184000000000000,Mad Catz FightStick TE2+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000380700008034000000000000,Mad Catz TE2 PS3 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000380700006652000000000000,UnKnown,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows, -03000000380700005082000000000000,Mad Catz FightPad PRO PS4,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000380700005032000000000000,Mad Catz FightPad PRO PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000380700008084000000000000,Mad Catz TE2 PS4 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000380700008532000000000000,Madcatz Arcade Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000380700003888000000000000,Madcatz Arcade Fightstick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000380700001888000000000000,MadCatz SFIV FightStick PS3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +03000000380700008081000000000000,MADCATZ SFV Arcade FightStick Alpha PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000008305000031b0000000000000,MaxfireBlaze3,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +03000000250900000128000000000000,Mayflash Arcade Stick,a:b1,b:b2,back:b8,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b5,y:b6,platform:Windows, +03000000790000004318000000000000,Mayflash GameCube Controller Adapter,a:b1,b:b2,back:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b0,leftshoulder:b4,leftstick:b0,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b0,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows, +030000008f0e00001030000000000000,Mayflash USB Adapter for original Sega Saturn controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,rightshoulder:b2,righttrigger:b7,start:b9,x:b3,y:b4,platform:Windows, +0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows, +03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000001008000001e5000000000000,NEXT Classic USB Game Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +03000000bd12000015d0000000000000,Nintendo Retrolink USB Super SNES Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows, +030000004b120000014d000000000000,NYKO AIRFLO,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a3,leftstick:a0,lefttrigger:b6,rightshoulder:b5,rightstick:a2,righttrigger:b7,start:b9,x:b2,y:b3,platform:Windows, 03000000362800000100000000000000,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b15,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,start:b14,x:b1,y:b2,platform:Windows, -03000000341a00003608000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000341a00000302000000000000,Hama Scorpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -03000000341a00000208000000000000,SL-6555-SBK,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:-a4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a3,righty:a2,start:b7,x:b2,y:b3,platform:Windows, -03000000341a00000108000000000000,EXEQ RF USB Gamepad 8206,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, -03000000341a00000104000000000000,QanBa Joystick Q4RAF,a:b5,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b1,y:b2,platform:Windows, -03000000321500000003000000000000,Razer Hydra,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +4d6963726f736f66742050432d6a6f79,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a5,righty:a4,x:b1,y:b2,platform:Windows, +03000000120c0000f60e000000000000,P4 Wired Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows, +030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, +03000000d62000006dca000000000000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000008f0e00007530000000000000,PS (R) Gamepad,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b1,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000e30500009605000000000000,PS to USB convert cable,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, +03000000100800000100000000000000,PS1 USB,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, +03000000100800000300000000000000,PS2 USB,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,platform:Windows, +03000000888800000803000000000000,PS3,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Windows, +030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Windows, +4c056802000000000000504944564944,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Windows, +88880803000000000000504944564944,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows, +03000000250900000500000000000000,PS3 DualShock,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,platform:Windows, +25090500000000000000504944564944,PS3 DualShock,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,platform:Windows, +03000000100000008200000000000000,PS360+ v1.66,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:h0.4,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +4c05c405000000000000504944564944,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000300f00000011000000000000,QanBa Arcade JoyStick 1008,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b10,x:b0,y:b3,platform:Windows, 03000000300f00001611000000000000,QanBa Arcade JoyStick 4018,a:b1,b:b2,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows, +03000000222c00000020000000000000,QANBA DRONE ARCADE JOYSTICK,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,x:b0,y:b3,platform:Windows, 03000000300f00001210000000000000,QanBa Joystick Plus,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows, -03000000300f00001201000000000000,Saitek Dual Analog Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, -03000000300f00001101000000000000,saitek rumble pad,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, -03000000300f00001001000000000000,Saitek P480 Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, -03000000300f00000011000000000000,QanBa Arcade JoyStick 1008,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b10,x:b0,y:b3,platform:Windows, -03000000280400000140000000000000,GamePad Pro USB,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, -03000000260900002625000000000000,Gamecube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,lefttrigger:a4,leftx:a0,lefty:a1,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Windows, -0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows, -03000000250900000500000000000000,PS3 DualShock,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,platform:Windows, -03000000250900000128000000000000,Mayflash Arcade Stick,a:b1,b:b2,back:b8,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b5,y:b6,platform:Windows, -03000000250900000017000000000000,HRAP2 on PS/SS/N64 Joypad to USB BOX,a:b2,b:b1,back:b9,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b8,x:b3,y:b0,platform:Windows, +03000000341a00000104000000000000,QanBa Joystick Q4RAF,a:b5,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b1,y:b2,platform:Windows, 03000000222c00000223000000000000,Qanba Obsidian Arcade Joystick PS3 Mode,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000222c00000023000000000000,Qanba Obsidian Arcade Joystick PS4 Mode,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -03000000222c00000020000000000000,QANBA DRONE ARCADE JOYSTICK,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,x:b0,y:b3,platform:Windows, -03000000203800000900000000000000,8Bitdo NES30 PRO Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, -03000000120c0000f60e000000000000,P4 Wired Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows, -03000000102800000900000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, -03000000100800000300000000000000,PS2 USB,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,platform:Windows, -030000001008000001e5000000000000,NEXT Classic USB Game Controller,a:b0,b:b1,back:b8,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b9,platform:Windows, -03000000100800000100000000000000,PS1 USB,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, -03000000100000008200000000000000,PS360+ v1.66,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:h0.4,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00008b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +03000000321500000003000000000000,Razer Hydra,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000000d0f00001100000000000000,REAL ARCADE PRO.3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00006a00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00006b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00008a00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00008800000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows, -030000000d0f00008700000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00008500000000000000,Fighting Commander 2016 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00008400000000000000,Fighting Commander 5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00008b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00007000000000000000,REAL ARCADE PRO.4 VLX,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00006e00000000000000,HORIPAD 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00006b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00006a00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00005f00000000000000,Fighting Commander 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00005e00000000000000,Fighting Commander 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:a3,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00005c00000000000000,Real Arcade Pro.V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00005b00000000000000,Real Arcade Pro.V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00004d00000000000000,HORIPAD3 A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00004900000000000000,Hatsune Miku Sho Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00004000000000000000,Hori Fighting Stick Mini 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00002700000000000000,FIGHTING STICK V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00002200000000000000,REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, -030000000d0f00001100000000000000,REAL ARCADE PRO.3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, -03000000022000000090000000000000,8Bitdo NES30 PRO USB,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, -0300000000f00000f100000000000000,RetroUSB.com Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows, +030000000d0f00005b00000000000000,Real Arcade Pro.V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00005c00000000000000,Real Arcade Pro.V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 0300000000f000000300000000000000,RetroUSB.com RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows, +0300000000f00000f100000000000000,RetroUSB.com Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows, +030000006f0e00001e01000000000000,Rock Candy Gamepad for PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000004f04000003d0000000000000,run'n'drive,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,leftshoulder:a3,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:a4,rightstick:b11,righttrigger:b5,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000a30600001af5000000000000,Saitek Cyborg,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows, +03000000a306000023f6000000000000,Saitek Cyborg V.1 Game pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows, +03000000300f00001201000000000000,Saitek Dual Analog Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, +03000000a30600000cff000000000000,Saitek P2500,a:b2,b:b3,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b9,rightx:a2,righty:a3,start:b4,x:b0,y:b1,platform:Windows, +03000000a30600000c04000000000000,Saitek P2900,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows, +03000000300f00001001000000000000,Saitek P480 Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, +03000000a30600000b04000000000000,Saitek P990,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows, +03000000300f00001101000000000000,saitek rumble pad,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, 0300000000050000289b000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows, +030000009b2800000500000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows, +03000000790000001100000000000000,Sega Saturn Gamepad,a:b1,b:b2,leftshoulder:b6,lefttrigger:b3,leftx:a0,lefty:a4,rightshoulder:b7,righttrigger:b0,start:b8,x:b4,y:b5,platform:Windows, +03000000341a00000208000000000000,SL-6555-SBK,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:-a4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a3,righty:a2,start:b7,x:b2,y:b3,platform:Windows, +030000004c050000cc09000000000000,Sony DualShock 4,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000004c050000a00b000000000000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +030000008f0e00000800000000000000,SpeedLink Strike FX Wireless,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +03000000ff1100003133000000000000,SVEN X-PAD,a:b2,b:b3,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a4,start:b5,x:b0,y:b1,platform:Windows, +03000000fa1900000706000000000000,Team 5,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +03000000b50700001203000000000000,Techmobility X6-38V,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, +030000004f04000015b3000000000000,Thrustmaster Dual Analog 2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows, +030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Windows, +030000004f04000004b3000000000000,Thrustmaster Firestorm Dual Power 3,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows, +03000000666600000488000000000000,TigerGame PS/PS2 Game Controller Adapter,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, +03000000d90400000200000000000000,TwinShock PS2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, +03000000380700006652000000000000,UnKnown,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows, +03000000632500002305000000000000,USB Vibration Joystick (BM),a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, +03000000790000001b18000000000000,Venom Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, +03000000786901006e70000000000000,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, # Mac OS X +03000000102800000900000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, +10280000000000000900000000000000,8Bitdo SFC30 GamePad Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, 830500000000000031b0000000000000,Cideko AK08b,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000790000000600000000000000,G-Shark GP-702,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Mac OS X, +03000000ad1b000001f9000000000000,Gamestop BB-070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, +0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, +030000000d0f00005f00000000000000,HORI Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f00005e00000000000000,HORI Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f00004d00000000000000,HORI Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f00006600000000000000,HORIPAD FPS PLUS 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +03000000830500006020000000000000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X, +030000006d04000016c2000000000000,Logitech F310 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +6d0400000000000016c2000000000000,Logitech F310 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +030000006d04000018c2000000000000,Logitech F510 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +6d0400000000000018c2000000000000,Logitech F510 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +030000006d0400001fc2000000000000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 6d040000000000001fc2000000000000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, +030000006d04000019c2000000000000,Logitech Wireless Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 6d0400000000000019c2000000000000,Logitech Wireless Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, -6d0400000000000018c2000000000000,Logitech F510 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, -6d0400000000000016c2000000000000,Logitech F310 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, -5e040000000000008e02000000000000,X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, -4c05000000000000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, -4c050000000000006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X, -351200000000000021ab000000000000,SFC30 Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, -11010000000000002014000000000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Mac OS X, -11010000000000001714000000000000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Mac OS X, -10280000000000000900000000000000,8Bitdo SFC30 GamePad Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, -050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b8,leftshoulder:b19,leftstick:b23,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b24,righttrigger:b22,rightx:a2,righty:a3,start:b6,x:b18,y:b17,platform:Mac OS X, -050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,back:b7,dpdown:b3,dpleft:b0,dpright:b1,dpup:b2,guide:b8,leftshoulder:b11,lefttrigger:b12,leftx:a0,lefty:a1,start:b6,x:b10,y:b9,platform:Mac OS X, -0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, +0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Mac OS X, +03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X, 03000000d8140000cecf000000000000,MC Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, -03000000bd12000015d0000000000000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X, -03000000b40400000a01000000000000,Sega Saturn USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Mac OS X, -03000000ad1b000001f9000000000000,Gamestop BB-070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Mac OS X, +030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X, +4c050000000000006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X, +030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +4c05000000000000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 030000008916000000fd000000000000,Razer Onza TE,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, -03000000830500006020000000000000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X, -03000000811700007e05000000000000,Sega Saturn,a:b2,b:b4,dpdown:b16,dpleft:b15,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,leftx:a0,lefty:a2,rightshoulder:b9,righttrigger:a4,start:b13,x:b0,y:b6,platform:Mac OS X, 03000000790000001100000000000000,Retrolink Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a3,lefty:a4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X, -03000000790000000600000000000000,G-Shark GP-702,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Mac OS X, -03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X, -030000006d0400001fc2000000000000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, -030000006d04000019c2000000000000,Logitech Wireless Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, -030000006d04000018c2000000000000,Logitech F510 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, -030000006d04000016c2000000000000,Logitech F310 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, -030000005e040000ea02000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, -030000005e040000e002000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X, -030000005e040000dd02000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, -030000005e0400008e02000000000000,X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, -030000004f04000015b3000000000000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Mac OS X, -030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Mac OS X, +03000000811700007e05000000000000,Sega Saturn,a:b2,b:b4,dpdown:b16,dpleft:b15,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,leftx:a0,lefty:a2,rightshoulder:b9,righttrigger:a4,start:b13,x:b0,y:b6,platform:Mac OS X, +03000000b40400000a01000000000000,Sega Saturn USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Mac OS X, +030000003512000021ab000000000000,SFC30 Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, +351200000000000021ab000000000000,SFC30 Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, 030000004c050000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, -030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 030000004c050000a00b000000000000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, -030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X, -030000003512000021ab000000000000,SFC30 Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, -0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Mac OS X, -03000000102800000900000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, +11010000000000002014000000000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Mac OS X, +11010000000000001714000000000000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Mac OS X, +030000004f04000015b3000000000000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Mac OS X, +030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Mac OS X, +03000000bd12000015d0000000000000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X, 03000000100800000100000000000000,Twin USB Joystick,a:b4,b:b2,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b12,leftstick:b20,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b14,rightstick:b22,righttrigger:b10,rightx:a6,righty:a4,start:b18,x:b6,y:b0,platform:Mac OS X, -030000000d0f00006600000000000000,HORIPAD FPS PLUS 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, -030000000d0f00005f00000000000000,HORI Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, -030000000d0f00005e00000000000000,HORI Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, -030000000d0f00004d00000000000000,HORI Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, +050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,back:b7,dpdown:b3,dpleft:b0,dpright:b1,dpup:b2,guide:b8,leftshoulder:b11,lefttrigger:b12,leftx:a0,lefty:a1,start:b6,x:b10,y:b9,platform:Mac OS X, +050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b8,leftshoulder:b19,leftstick:b23,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b24,righttrigger:b22,rightx:a2,righty:a3,start:b6,x:b18,y:b17,platform:Mac OS X, +030000005e0400008e02000000000000,X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, +5e040000000000008e02000000000000,X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, +030000005e040000dd02000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, +030000005e040000e002000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X, +030000005e040000ea02000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, # Linux -xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -06000000adde0000efbe000002010000,Hidromancer Game Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -060000004c0500006802000000010000,PS3 Controller (Bluetooth),a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, -05000000d6200000ad0d000001000000,Moga Pro,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux, +05000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00002038000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, -05000000ac0500003232000001000000,VR-BOX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux, +03000000c82d00000190000011010000,8Bitdo NES30 Pro 8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, +05000000102800000900000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, 05000000a00500003232000001000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux, -050000007e0500003003000001000000,Nintendo Wii Remote Pro Controller,a:b1,b:b0,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, -050000006964726f69643a636f6e0000,idroid:con,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -050000005e040000e002000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -05000000504c415953544154494f4e00,PS3 Controller (Bluetooth),a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, -050000004c050000cc09000000810000,Sony DualShock 4 (CUH-ZCT2U) (Bluetooth),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, -050000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -050000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -050000004c0500006802000000810000,Sony PLAYSTATION(R)3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, -050000004c0500006802000000010000,PS3 Controller (Bluetooth),a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, -0500000047532067616d657061640000,GS gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +030000006f0e00003901000020060000,Afterglow Wired Controller for Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000100000008200000011010000,Akishop Customs PS360+ v1.66,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, +03000000666600006706000000010000,boom PSX to PC Converter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Linux, +03000000e82000006058000001010000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +03000000b40400000a01000000010000,CYPRESS USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux, +03000000790000000600000010010000,DragonRise Inc. Generic USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Linux, +030000006f0e00003001000001010000,EA Sports PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000341a000005f7000010010000,GameCube {HuiJia USB box},a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux, +03000000260900008888000000010000,GameCube {WiseGroup USB box},a:b0,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b1,y:b3,platform:Linux, 0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, -05000000380700006652000025010000,Mad Catz C.T.R.L.R ,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -05000000362800000100000003010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux, -05000000362800000100000002010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux, -050000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, -05000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, -05000000102800000900000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, -05000000010000000100000003000000,Nintendo Wiimote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, -03000000ff1100004133000010010000,GreenAsia Inc.USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, -03000000ff1100003133000010010000,PC Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, -03000000fd0500000030000000010000,InterAct GoPad I-73000 (Fighting Game Layout),a:b3,b:b4,back:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b7,x:b0,y:b1,platform:Linux, +030000006f0e00000104000000010000,Gamestop Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e00001304000000010000,Generic X-Box pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:a0,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:a3,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006f0e00001f01000000010000,Generic X-Box pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000f0250000c183000010010000,Goodbetterbest Ltd USB Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -03000000e82000006058000001010000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, -03000000de280000ff11000001000000,Valve Streaming Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -03000000de280000fc11000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000280400000140000000010000,Gravis GamePad Pro USB ,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, +030000008f0e00000300000010010000,GreenAsia Inc. USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, +030000008f0e00001200000010010000,GreenAsia Inc. USB Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux, +03000000ff1100004133000010010000,GreenAsia Inc.USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, +0500000047532067616d657061640000,GS gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +06000000adde0000efbe000002010000,Hidromancer Game Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000d81400000862000011010000,HitBox (PS3/PC) Analog Mode,a:b1,b:b2,back:b8,guide:b9,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,platform:Linux, -03000000d814000007cd000011010000,Toodles 2008 Chimp PC/PS3,a:b0,b:b1,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux, 03000000c9110000f055000011010000,HJC Game GAMEPAD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, -03000000c82d00000190000011010000,8Bitdo NES30 Pro 8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, -03000000c6240000045d000025010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -03000000c0160000e105000001010000,Xin-Mo Xin-Mo Dual Arcade,a:b4,b:b3,back:b6,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b9,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b1,y:b0,platform:Linux, -03000000c01600008704000011010000,Serial/Keyboard/Mouse/Joystick,a:b12,b:b10,back:b4,dpdown:b2,dpleft:b3,dpright:b1,dpup:b0,leftshoulder:b9,leftstick:b14,lefttrigger:b6,leftx:a1,lefty:a0,rightshoulder:b8,rightstick:b15,righttrigger:b7,rightx:a2,righty:a3,start:b5,x:b13,y:b11,platform:Linux, -03000000bd12000015d0000010010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux, -03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, -03000000b40400000a01000000010000,CYPRESS USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux, -03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux, -03000000ad1b000016f0000090040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000000d0f00000d00000000010000,hori,a:b0,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftx:b4,lefty:b5,rightshoulder:b7,start:b9,x:b1,y:b2,platform:Linux, +030000000d0f00001000000011010000,HORI CO. LTD. FIGHTING STICK 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, +030000000d0f00002200000011010000,HORI CO. LTD. REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -03000000a306000023f6000011010000,Saitek Cyborg V.1 Game Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux, -03000000a306000018f5000010010000,Saitek PLC Saitek P3200 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux, -03000000a30600000c04000011010000,Saitek P2900 Wireless Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b12,x:b0,y:b3,platform:Linux, -03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,platform:Linux, -030000009b2800000300000001010000,raphnet.net 4nes4snes v1.5,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux, -030000008f0e00001200000010010000,GreenAsia Inc. USB Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux, -030000008f0e00000300000010010000,GreenAsia Inc. USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, -030000008916000001fd000024010000,Razer Onza Classic Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000008916000000fd000024010000,Razer Onza Tournament,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000000d0f00006700000001010000,HORIPAD ONE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000830500006020000010010000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux, -03000000790000001100000010010000,RetroLink Saturn Classic Controller,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux, -03000000790000001100000000010000,USB Gamepad1,a:b2,b:b1,back:b8,dpdown:a0,dpleft:a1,dpright:a2,dpup:a4,start:b9,platform:Linux, -03000000790000000600000010010000,DragonRise Inc. Generic USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Linux, -03000000780000000600000010010000,Microntek USB Joystick,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux, -030000006f0e00004601000001010000,Rock Candy Wired Controller for Xbox One,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000006f0e00003901000020060000,Afterglow Wired Controller for Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000006f0e00003001000001010000,EA Sports PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -030000006f0e00001f01000000010000,Generic X-Box pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000006f0e00001e01000011010000,Rock Candy Gamepad for PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -030000006f0e00001304000000010000,Generic X-Box pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:a0,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:a3,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000006f0e00000104000000010000,Gamestop Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000006f0e00000103000000020000,Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +050000006964726f69643a636f6e0000,idroid:con,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000fd0500000030000000010000,InterAct GoPad I-73000 (Fighting Game Layout),a:b3,b:b4,back:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b7,x:b0,y:b1,platform:Linux, 030000006e0500000320000010010000,JC-U3613M - DirectInput Mode,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Linux, -030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000006d0400001ec2000020200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000006d0400001dc2000014400000,Logitech F310 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000006d04000019c2000011010000,Logitech F710 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000300f00001001000010010000,Jess Tech Dual Analog Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux, +03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, +030000006f0e00000103000000020000,Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -030000006d04000018c2000010010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000006d04000016c2000011010000,Logitech F310 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006d0400001dc2000014400000,Logitech F310 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006d0400001ec2000020200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006d04000019c2000011010000,Logitech F710 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006d04000016c2000010010000,Logitech Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006d04000018c2000010010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000006d04000011c2000010010000,Logitech WingMan Cordless RumblePad,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b10,rightx:a3,righty:a4,start:b8,x:b3,y:b4,platform:Linux, -03000000666600006706000000010000,boom PSX to PC Converter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Linux, -03000000666600000488000000010000,Super Joy Box 5 Pro,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux, -030000005e040000dd02000003020000,Microsoft X-Box One pad v2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000005e040000d102000001010000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000005e040000a102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000005e040000a102000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000005e0400009102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000005e0400008e02000073050000,Speedlink TORID Wireless Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000005e0400008e02000062230000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000005e0400008e02000014010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000005e0400008e02000010010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +05000000380700006652000025010000,Mad Catz C.T.R.L.R ,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux, +03000000380700008034000011010000,Mad Catz fightstick (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000380700008084000011010000,Mad Catz fightstick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000380700008433000011010000,Mad Catz FightStick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000380700008483000011010000,Mad Catz FightStick TE S+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000380700001647000010040000,Mad Catz Wired Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000380700003847000090040000,Mad Catz Wired Xbox 360 Controller (SFIV),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +03000000ad1b000016f0000090040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000380700001888000010010000,MadCatz PC USB Wired Stick 8818,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000380700003888000010010000,MadCatz PC USB Wired Stick 8838,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:a0,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000780000000600000010010000,Microntek USB Joystick,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux, 030000005e0400008e02000004010000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000005e0400008902000021010000,Microsoft X-Box pad v2 (US),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux, +030000005e0400008e02000062230000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000dd02000003020000,Microsoft X-Box One pad v2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e0400008502000000010000,Microsoft X-Box pad (Japan),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux, -030000005e0400001907000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000005e0400000202000000010000,Old Xbox pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux, +030000005e0400008902000021010000,Microsoft X-Box pad v2 (US),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux, +05000000d6200000ad0d000001000000,Moga Pro,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux, +030000001008000001e5000010010000,NEXT Classic USB Game Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +050000007e0500003003000001000000,Nintendo Wii Remote Pro Controller,a:b1,b:b0,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +05000000010000000100000003000000,Nintendo Wiimote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 03000000550900001072000011010000,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, -030000004f04000023b3000000010000,Thrustmaster Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -030000004f04000020b3000010010000,Thrustmaster 2 in 1 DT,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux, -030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux, -030000004f04000009d0000000010000,Thrustmaster Run N Drive Wireless PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -030000004f04000008d0000000010000,Thrustmaster Run N Drive Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -030000004f04000000b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Linux, -030000004c050000cc09000011810000,Sony DualShock 4 (CUH-ZCT2U) (USB),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +03000000451300000830000010010000,NYKO CORE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000005e0400000202000000010000,Old Xbox pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux, +05000000362800000100000002010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux, +05000000362800000100000003010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux, +03000000ff1100003133000010010000,PC Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, +03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, +050000004c0500006802000000010000,PS3 Controller (Bluetooth),a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, +05000000504c415953544154494f4e00,PS3 Controller (Bluetooth),a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, +060000004c0500006802000000010000,PS3 Controller (Bluetooth),a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, +030000004c050000c405000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 030000004c050000cc09000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +050000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +050000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000009b2800000300000001010000,raphnet.net 4nes4snes v1.5,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux, +030000008916000001fd000024010000,Razer Onza Classic Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000008916000000fd000024010000,Razer Onza Tournament,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000c6240000045d000025010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000321500000009000011010000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, +050000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, +03000000790000001100000010010000,RetroLink Saturn Classic Controller,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux, +0300000000f000000300000000010000,RetroUSB.com RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux, +0300000000f00000f100000000010000,RetroUSB.com Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux, +030000006f0e00001e01000011010000,Rock Candy Gamepad for PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006f0e00004601000001010000,Rock Candy Wired Controller for Xbox One,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000a306000023f6000011010000,Saitek Cyborg V.1 Game Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux, +03000000a30600000c04000011010000,Saitek P2900 Wireless Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b12,x:b0,y:b3,platform:Linux, +03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,platform:Linux, +03000000a306000018f5000010010000,Saitek PLC Saitek P3200 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux, +03000000c01600008704000011010000,Serial/Keyboard/Mouse/Joystick,a:b12,b:b10,back:b4,dpdown:b2,dpleft:b3,dpright:b1,dpup:b0,leftshoulder:b9,leftstick:b14,lefttrigger:b6,leftx:a1,lefty:a0,rightshoulder:b8,rightstick:b15,righttrigger:b7,rightx:a2,righty:a3,start:b5,x:b13,y:b11,platform:Linux, 030000004c050000c405000011810000,Sony DualShock 4,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, -030000004c050000c405000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +050000004c050000cc09000000810000,Sony DualShock 4 (CUH-ZCT2U) (Bluetooth),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +030000004c050000cc09000011810000,Sony DualShock 4 (CUH-ZCT2U) (USB),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 030000004c050000a00b000011010000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 030000004c0500006802000011810000,Sony PLAYSTATION(R)3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, -030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, -03000000451300000830000010010000,NYKO CORE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -03000000380700008483000011010000,Mad Catz FightStick TE S+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -03000000380700008433000011010000,Mad Catz FightStick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -03000000380700008084000011010000,Mad Catz fightstick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, -03000000380700008034000011010000,Mad Catz fightstick (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -03000000380700003888000010010000,MadCatz PC USB Wired Stick 8838,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:a0,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -03000000380700003847000090040000,Mad Catz Wired Xbox 360 Controller (SFIV),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, -03000000380700001888000010010000,MadCatz PC USB Wired Stick 8818,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -03000000380700001647000010040000,Mad Catz Wired Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, -03000000341a000005f7000010010000,GameCube {HuiJia USB box},a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux, -03000000321500000009000011010000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, -03000000300f00001001000010010000,Jess Tech Dual Analog Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux, -03000000280400000140000000010000,Gravis GamePad Pro USB ,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, -03000000260900008888000000010000,GameCube {WiseGroup USB box},a:b0,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b1,y:b3,platform:Linux, +050000004c0500006802000000810000,Sony PLAYSTATION(R)3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 03000000250900000500000000010000,Sony PS2 pad with SmartJoy adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux, -03000000100800000300000010010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, -030000001008000001e5000010010000,NEXT Classic USB Game Controller,a:b0,b:b1,back:b8,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b9,platform:Linux, +030000005e0400008e02000073050000,Speedlink TORID Wireless Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000de280000fc11000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000666600000488000000010000,Super Joy Box 5 Pro,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux, +030000004f04000020b3000010010000,Thrustmaster 2 in 1 DT,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux, +030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux, +030000004f04000023b3000000010000,Thrustmaster Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000004f04000000b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Linux, +030000004f04000008d0000000010000,Thrustmaster Run N Drive Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000004f04000009d0000000010000,Thrustmaster Run N Drive Wireless PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000bd12000015d0000010010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux, +03000000d814000007cd000011010000,Toodles 2008 Chimp PC/PS3,a:b0,b:b1,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux, 03000000100800000100000010010000,Twin USB PS2 Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, -03000000100000008200000011010000,Akishop Customs PS360+ v1.66,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, -030000000d0f00006700000001010000,HORIPAD ONE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, -030000000d0f00002200000011010000,HORI CO. LTD. REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, -030000000d0f00001000000011010000,HORI CO. LTD. FIGHTING STICK 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, -030000000d0f00000d00000000010000,hori,a:b0,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftx:b4,lefty:b5,rightshoulder:b7,start:b9,x:b1,y:b2,platform:Linux, -0300000000f00000f100000000010000,RetroUSB.com Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux, -0300000000f000000300000000010000,RetroUSB.com RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux, -0000000058626f782047616d65706100,Xbox Gamepad (userspace driver),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, +03000000100800000300000010010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, +03000000790000001100000000010000,USB Gamepad1,a:b2,b:b1,back:b8,dpdown:a0,dpleft:a1,dpright:a2,dpup:a4,start:b9,platform:Linux, +03000000de280000ff11000001000000,Valve Streaming Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +05000000ac0500003232000001000000,VR-BOX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux, +030000005e0400008e02000010010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e0400008e02000014010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e0400001907000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e0400009102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000a102000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000a102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 0000000058626f782033363020576900,Xbox 360 Wireless Controller,a:b0,b:b1,back:b14,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b7,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Linux, +0000000058626f782047616d65706100,Xbox Gamepad (userspace driver),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, +030000005e040000d102000001010000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +050000005e040000e002000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000c0160000e105000001010000,Xin-Mo Xin-Mo Dual Arcade,a:b4,b:b3,back:b6,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b9,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b1,y:b0,platform:Linux, +xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, # Android 4e564944494120436f72706f72617469,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, diff --git a/main/godotcontrollerdb.txt b/main/godotcontrollerdb.txt index 031a761f30..472b01947b 100644 --- a/main/godotcontrollerdb.txt +++ b/main/godotcontrollerdb.txt @@ -1,4 +1,4 @@ -# Game Controller DB for SDL in 2.0.6 format +# Game Controller DB for SDL in 2.0.6+ format # Source: https://github.com/godotengine/godot # Windows @@ -7,32 +7,31 @@ c911f055000000000000504944564944,GAMEPAD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0 __XINPUT_DEVICE__,XInput Gamepad,a:b12,b:b13,x:b14,y:b15,start:b4,back:b5,leftstick:b6,rightstick:b7,leftshoulder:b8,rightshoulder:b9,dpup:b0,dpdown:b1,dpleft:b2,dpright:b3,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,platform:Windows, # Linux -030000005e040000e302000003020000,Microsoft X-Box One Elite pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, -030000006f0e00001302000000010000,Afterglow Gamepad for Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, -05000000362800000100000004010000,OUYA Game Controller,leftx:a0,lefty:a1,dpdown:b9,rightstick:b7,rightshoulder:b5,rightx:a3,start:b16,righty:a4,dpleft:b10,lefttrigger:b12,x:b1,dpup:b8,back:b14,leftstick:b6,leftshoulder:b4,y:b2,a:b0,dpright:b11,righttrigger:b13,b:b3,platform:Windows, -030000005e0400001907000000010000,X360 Wireless Controller,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b10,rightshoulder:b5,rightx:a3,start:b7,righty:a4,dpleft:h0.8,lefttrigger:a2,x:b2,dpup:h0.1,back:b6,leftstick:b9,leftshoulder:b4,y:b3,a:b0,dpright:h0.2,righttrigger:a5,b:b1,platform:Windows, -030000005e0400008e02000001000000,Microsoft X-Box 360 pad,leftstick:b9,leftx:a0,lefty:a1,dpdown:h0.1,rightstick:b10,rightshoulder:b5,rightx:a3,start:b7,righty:a4,dpleft:h0.2,lefttrigger:a2,x:b2,dpup:h0.4,back:b6,leftshoulder:b4,y:b3,a:b0,dpright:h0.8,righttrigger:a5,b:b1,platform:Windows, -03000000fd0500002a26000000010000,3dfx InterAct HammerHead FX,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b5,rightshoulder:b7,rightx:a2,start:b11,righty:a3,dpleft:h0.8,lefttrigger:b8,x:b0,dpup:h0.1,back:b10,leftstick:b2,leftshoulder:b6,y:b1,a:b3,dpright:h0.2,righttrigger:b9,b:b4,platform:Windows, -030000006f0e00002801000011010000,PDP Rock Candy Wireless Controller for PS3,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b11,rightshoulder:b5,rightx:a2,start:b9,righty:a3,dpleft:h0.8,lefttrigger:b6,x:b0,dpup:h0.1,back:b8,leftstick:b10,leftshoulder:b4,y:b3,a:b1,dpright:h0.2,righttrigger:b7,b:b2,platform:Windows, -030000000d0f00004d00000011010000,HORI Gem Pad 3,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,platform:Windows, +030000006f0e00001302000000010000,Afterglow Gamepad for Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +05000000362800000100000004010000,OUYA Game Controller,leftx:a0,lefty:a1,dpdown:b9,rightstick:b7,rightshoulder:b5,rightx:a3,start:b16,righty:a4,dpleft:b10,lefttrigger:b12,x:b1,dpup:b8,back:b14,leftstick:b6,leftshoulder:b4,y:b2,a:b0,dpright:b11,righttrigger:b13,b:b3,platform:Linux, +030000005e0400008e02000001000000,Microsoft X-Box 360 pad,leftstick:b9,leftx:a0,lefty:a1,dpdown:h0.1,rightstick:b10,rightshoulder:b5,rightx:a3,start:b7,righty:a4,dpleft:h0.2,lefttrigger:a2,x:b2,dpup:h0.4,back:b6,leftshoulder:b4,y:b3,a:b0,dpright:h0.8,righttrigger:a5,b:b1,platform:Linux, +03000000fd0500002a26000000010000,3dfx InterAct HammerHead FX,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b5,rightshoulder:b7,rightx:a2,start:b11,righty:a3,dpleft:h0.8,lefttrigger:b8,x:b0,dpup:h0.1,back:b10,leftstick:b2,leftshoulder:b6,y:b1,a:b3,dpright:h0.2,righttrigger:b9,b:b4,platform:Linux, +030000006f0e00002801000011010000,PDP Rock Candy Wireless Controller for PS3,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b11,rightshoulder:b5,rightx:a2,start:b9,righty:a3,dpleft:h0.8,lefttrigger:b6,x:b0,dpup:h0.1,back:b8,leftstick:b10,leftshoulder:b4,y:b3,a:b1,dpright:h0.2,righttrigger:b7,b:b2,platform:Linux, +030000000d0f00004d00000011010000,HORI Gem Pad 3,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,platform:Linux, +030000005e040000ea02000001030000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, # Android -4f5559412047616d6520436f6e74726f,OUYA Game Controller,leftx:a1,lefty:a3,dpdown:b12,rightstick:b8,rightshoulder:b10,rightx:a6,start:b-86,righty:a7,dpleft:b13,lefttrigger:b15,x:b2,dpup:b11,leftstick:b7,leftshoulder:b9,y:b3,a:b0,dpright:b14,righttrigger:b16,b:b1,platform:Windows, -Default Android Gamepad,Default Controller,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b8,rightshoulder:b10,rightx:a2,start:b6,righty:a3,dpleft:h0.8,lefttrigger:a4,x:b2,dpup:h0.1,back:b4,leftstick:b7,leftshoulder:b9,y:b3,a:b0,dpright:h0.2,righttrigger:a5,b:b1,platform:Windows, -532e542e442e20496e74657261637420,3dfx InterAct HammerHead FX,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b25,rightshoulder:b27,rightx:a2,start:b31,righty:a3,dpleft:h0.8,lefttrigger:b28,x:b20,dpup:h0.1,back:b30,leftstick:b22,leftshoulder:b26,y:b21,a:b23,dpright:h0.2,righttrigger:b29,b:b24,platform:Windows, -506572666f726d616e63652044657369,PDP Rock Candy Wireless Controller for PS3,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b6,rightshoulder:b18,rightx:a2,start:b16,righty:a3,dpleft:h0.8,lefttrigger:b9,x:b0,dpup:h0.1,back:h0.2,leftstick:b4,leftshoulder:b3,y:b2,a:b1,dpright:h0.2,righttrigger:b10,b:b17,platform:Windows, -4d6963726f736f667420582d426f7820,Microsoft X-Box 360 pad,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b8,rightshoulder:b10,rightx:a2,start:b6,righty:a3,dpleft:h0.8,lefttrigger:a4,x:b2,dpup:h0.1,leftstick:b7,leftshoulder:b9,y:b3,a:b0,dpright:h0.2,righttrigger:a5,b:b1,platform:Windows, -484f524920434f2e2c4c544420205041,Hori Gem Pad 3,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b6,rightshoulder:b18,rightx:a2,start:b16,righty:a3,dpleft:h0.8,lefttrigger:b9,x:b0,dpup:h0.1,back:b15,leftstick:b4,leftshoulder:b3,y:b2,a:b1,dpright:h0.2,righttrigger:b10,b:b17,platform:Windows, -47656e6572696320582d426f78207061,Logitech F-310,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b8,rightshoulder:b10,rightx:a2,start:b6,righty:a3,dpleft:h0.8,lefttrigger:a5,x:b2,dpup:h0.1,leftstick:b7,leftshoulder:b9,y:b3,a:b0,dpright:h0.2,righttrigger:a4,b:b1,platform:Windows, +4f5559412047616d6520436f6e74726f,OUYA Game Controller,leftx:a1,lefty:a3,dpdown:b12,rightstick:b8,rightshoulder:b10,rightx:a6,start:b-86,righty:a7,dpleft:b13,lefttrigger:b15,x:b2,dpup:b11,leftstick:b7,leftshoulder:b9,y:b3,a:b0,dpright:b14,righttrigger:b16,b:b1,platform:Android, +Default Android Gamepad,Default Controller,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b8,rightshoulder:b10,rightx:a2,start:b6,righty:a3,dpleft:h0.8,lefttrigger:a4,x:b2,dpup:h0.1,back:b4,leftstick:b7,leftshoulder:b9,y:b3,a:b0,dpright:h0.2,righttrigger:a5,b:b1,platform:Android, +532e542e442e20496e74657261637420,3dfx InterAct HammerHead FX,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b25,rightshoulder:b27,rightx:a2,start:b31,righty:a3,dpleft:h0.8,lefttrigger:b28,x:b20,dpup:h0.1,back:b30,leftstick:b22,leftshoulder:b26,y:b21,a:b23,dpright:h0.2,righttrigger:b29,b:b24,platform:Android, +506572666f726d616e63652044657369,PDP Rock Candy Wireless Controller for PS3,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b6,rightshoulder:b18,rightx:a2,start:b16,righty:a3,dpleft:h0.8,lefttrigger:b9,x:b0,dpup:h0.1,back:h0.2,leftstick:b4,leftshoulder:b3,y:b2,a:b1,dpright:h0.2,righttrigger:b10,b:b17,platform:Android, +4d6963726f736f667420582d426f7820,Microsoft X-Box 360 pad,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b8,rightshoulder:b10,rightx:a2,start:b6,righty:a3,dpleft:h0.8,lefttrigger:a4,x:b2,dpup:h0.1,leftstick:b7,leftshoulder:b9,y:b3,a:b0,dpright:h0.2,righttrigger:a5,b:b1,platform:Android, +484f524920434f2e2c4c544420205041,Hori Gem Pad 3,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b6,rightshoulder:b18,rightx:a2,start:b16,righty:a3,dpleft:h0.8,lefttrigger:b9,x:b0,dpup:h0.1,back:b15,leftstick:b4,leftshoulder:b3,y:b2,a:b1,dpright:h0.2,righttrigger:b10,b:b17,platform:Android, +47656e6572696320582d426f78207061,Logitech F-310,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b8,rightshoulder:b10,rightx:a2,start:b6,righty:a3,dpleft:h0.8,lefttrigger:a5,x:b2,dpup:h0.1,leftstick:b7,leftshoulder:b9,y:b3,a:b0,dpright:h0.2,righttrigger:a4,b:b1,platform:Android, # Javascript -Default HTML5 Gamepad, Default Mapping,leftx:a0,lefty:a1,dpdown:b13,rightstick:b11,rightshoulder:b5,rightx:a2,start:b9,righty:a3,dpleft:b14,lefttrigger:a6,x:b2,dpup:b12,back:b8,leftstick:b10,leftshoulder:b4,y:b3,a:b0,dpright:b15,righttrigger:a7,b:b1,platform:Windows, -c2a94d6963726f736f66742058626f78,Wireless X360 Controller,leftx:a0,lefty:a1,dpdown:b14,rightstick:b10,rightshoulder:b5,rightx:a3,start:b7,righty:a4,dpleft:b11,lefttrigger:a2,x:b2,dpup:b13,back:b6,leftstick:b9,leftshoulder:b4,y:b3,a:b0,dpright:b12,righttrigger:a5,b:b1,platform:Windows, -303534632d303563342d576972656c65,PS4 Controller USB/Win,leftx:a0,lefty:a1,dpdown:b15,rightstick:b11,rightshoulder:b5,rightx:a2,start:b9,righty:a5,lefttrigger:a3,x:b0,dpup:b14,dpleft:b16,dpright:b17,back:b8,leftstick:b10,leftshoulder:b4,y:b3,a:b1,righttrigger:b7,b:b2,platform:Windows, -303534632d303563342d536f6e792043,PS4 Controller USB/Linux,leftx:a0,lefty:a1,dpdown:a7,rightstick:b11,rightshoulder:b5,rightx:a2,start:b9,righty:a5,dpleft:a6,lefttrigger:a3,x:b0,dpup:a7,back:b8,leftstick:b10,leftshoulder:b4,y:b3,a:b1,dpright:a6,righttrigger:a4,b:b2,platform:Windows, -303534632d303236382d536f6e792050,PS3 Controller USB/Linux,leftx:a0,lefty:a1,dpdown:b6,rightstick:b2,rightshoulder:b11,rightx:a2,start:b3,righty:a3,dpleft:b7,lefttrigger:b8,x:b15,dpup:b4,back:b0,leftstick:b1,leftshoulder:b10,y:b12,a:b14,dpright:b5,righttrigger:b9,b:b13,platform:Windows, -303435652d303731392d58626f782033,Wireless X360 Controller,leftx:a0,lefty:a1,dpdown:b14,rightstick:b10,rightshoulder:b5,rightx:a3,start:b7,righty:a4,dpleft:b11,lefttrigger:a2,x:b2,dpup:b13,back:b6,leftstick:b9,leftshoulder:b4,y:b3,a:b0,dpright:b12,righttrigger:a5,b:b1,platform:Windows, -303435652d303238652d4d6963726f73,Wired X360 Controller,leftx:a0,lefty:a1,dpdown:a7,rightstick:b10,rightshoulder:b5,rightx:a3,start:b7,righty:a4,dpleft:a6,lefttrigger:a2,x:b2,dpup:a7,back:b6,leftstick:b9,leftshoulder:b4,y:b3,a:b0,dpright:a6,righttrigger:a5,b:b1,platform:Windows, +Default HTML5 Gamepad, Default Mapping,leftx:a0,lefty:a1,dpdown:b13,rightstick:b11,rightshoulder:b5,rightx:a2,start:b9,righty:a3,dpleft:b14,lefttrigger:a6,x:b2,dpup:b12,back:b8,leftstick:b10,leftshoulder:b4,y:b3,a:b0,dpright:b15,righttrigger:a7,b:b1,platform:Javascript, +c2a94d6963726f736f66742058626f78,Wireless X360 Controller,leftx:a0,lefty:a1,dpdown:b14,rightstick:b10,rightshoulder:b5,rightx:a3,start:b7,righty:a4,dpleft:b11,lefttrigger:a2,x:b2,dpup:b13,back:b6,leftstick:b9,leftshoulder:b4,y:b3,a:b0,dpright:b12,righttrigger:a5,b:b1,platform:Javascript, +303534632d303563342d576972656c65,PS4 Controller USB/Win,leftx:a0,lefty:a1,dpdown:b15,rightstick:b11,rightshoulder:b5,rightx:a2,start:b9,righty:a5,lefttrigger:a3,x:b0,dpup:b14,dpleft:b16,dpright:b17,back:b8,leftstick:b10,leftshoulder:b4,y:b3,a:b1,righttrigger:b7,b:b2,platform:Javascript, +303534632d303563342d536f6e792043,PS4 Controller USB/Linux,leftx:a0,lefty:a1,dpdown:a7,rightstick:b11,rightshoulder:b5,rightx:a2,start:b9,righty:a5,dpleft:a6,lefttrigger:a3,x:b0,dpup:a7,back:b8,leftstick:b10,leftshoulder:b4,y:b3,a:b1,dpright:a6,righttrigger:a4,b:b2,platform:Javascript, +303534632d303236382d536f6e792050,PS3 Controller USB/Linux,leftx:a0,lefty:a1,dpdown:b6,rightstick:b2,rightshoulder:b11,rightx:a2,start:b3,righty:a3,dpleft:b7,lefttrigger:b8,x:b15,dpup:b4,back:b0,leftstick:b1,leftshoulder:b10,y:b12,a:b14,dpright:b5,righttrigger:b9,b:b13,platform:Javascript, +303435652d303731392d58626f782033,Wireless X360 Controller,leftx:a0,lefty:a1,dpdown:b14,rightstick:b10,rightshoulder:b5,rightx:a3,start:b7,righty:a4,dpleft:b11,lefttrigger:a2,x:b2,dpup:b13,back:b6,leftstick:b9,leftshoulder:b4,y:b3,a:b0,dpright:b12,righttrigger:a5,b:b1,platform:Javascript, +303435652d303238652d4d6963726f73,Wired X360 Controller,leftx:a0,lefty:a1,dpdown:a7,rightstick:b10,rightshoulder:b5,rightx:a3,start:b7,righty:a4,dpleft:a6,lefttrigger:a2,x:b2,dpup:a7,back:b6,leftstick:b9,leftshoulder:b4,y:b3,a:b0,dpright:a6,righttrigger:a5,b:b1,platform:Javascript, # UWP -__UWP_GAMEPAD__,Xbox Controller,a:b2,b:b3,x:b4,y:b5,start:b0,back:b1,leftstick:b12,rightstick:b13,leftshoulder:b10,rightshoulder:b11,dpup:b6,dpdown:b7,dpleft:b8,dpright:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,platform:Windows,
\ No newline at end of file +__UWP_GAMEPAD__,Xbox Controller,a:b2,b:b3,x:b4,y:b5,start:b0,back:b1,leftstick:b12,rightstick:b13,leftshoulder:b10,rightshoulder:b11,dpup:b6,dpdown:b7,dpleft:b8,dpright:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,platform:UWP, diff --git a/main/input_default.cpp b/main/input_default.cpp index d074e05f43..e8015400af 100644 --- a/main/input_default.cpp +++ b/main/input_default.cpp @@ -30,9 +30,9 @@ #include "input_default.h" -#include "default_controller_mappings.h" -#include "input_map.h" -#include "os/os.h" +#include "core/input_map.h" +#include "core/os/os.h" +#include "main/default_controller_mappings.h" #include "scene/resources/texture.h" #include "servers/visual_server.h" @@ -261,6 +261,13 @@ void InputDefault::parse_input_event(const Ref<InputEvent> &p_event) { void InputDefault::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_emulated) { + // Notes on mouse-touch emulation: + // - Emulated mouse events are parsed, that is, re-routed to this method, so they make the same effects + // as true mouse events. The only difference is the situation is flagged as emulated so they are not + // emulated back to touch events in an endless loop. + // - Emulated touch events are handed right to the main loop (i.e., the SceneTree) because they don't + // require additional handling by this class. + _THREAD_SAFE_METHOD_ Ref<InputEventKey> k = p_event; @@ -316,11 +323,21 @@ void InputDefault::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool } } - if (emulate_mouse_from_touch) { + Ref<InputEventScreenTouch> st = p_event; + + if (st.is_valid()) { + + if (st->is_pressed()) { + SpeedTrack &track = touch_speed_track[st->get_index()]; + track.reset(); + } else { + // Since a pointer index may not occur again (OSs may or may not reuse them), + // imperatively remove it from the map to keep no fossil entries in it + touch_speed_track.erase(st->get_index()); + } - Ref<InputEventScreenTouch> st = p_event; + if (emulate_mouse_from_touch) { - if (st.is_valid()) { bool translate = false; if (st->is_pressed()) { if (mouse_from_touch_index == -1) { @@ -343,18 +360,26 @@ void InputDefault::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool button_event->set_pressed(st->is_pressed()); button_event->set_button_index(BUTTON_LEFT); if (st->is_pressed()) { - button_event->set_button_mask(mouse_button_mask | (1 << BUTTON_LEFT - 1)); + button_event->set_button_mask(mouse_button_mask | (1 << (BUTTON_LEFT - 1))); } else { - button_event->set_button_mask(mouse_button_mask & ~(1 << BUTTON_LEFT - 1)); + button_event->set_button_mask(mouse_button_mask & ~(1 << (BUTTON_LEFT - 1))); } _parse_input_event_impl(button_event, true); } } + } + + Ref<InputEventScreenDrag> sd = p_event; + + if (sd.is_valid()) { + + SpeedTrack &track = touch_speed_track[sd->get_index()]; + track.update(sd->get_relative()); + sd->set_speed(track.speed); - Ref<InputEventScreenDrag> sd = p_event; + if (emulate_mouse_from_touch && sd->get_index() == mouse_from_touch_index) { - if (sd.is_valid() && sd->get_index() == mouse_from_touch_index) { Ref<InputEventMouseMotion> motion_event; motion_event.instance(); @@ -404,6 +429,7 @@ void InputDefault::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool action.physics_frame = Engine::get_singleton()->get_physics_frames(); action.idle_frame = Engine::get_singleton()->get_idle_frames(); action.pressed = p_event->is_action_pressed(E->key()); + action.strength = 0.f; action_state[E->key()] = action; } action_state[E->key()].strength = p_event->get_action_strength(E->key()); @@ -537,6 +563,7 @@ void InputDefault::action_press(const StringName &p_action) { action.physics_frame = Engine::get_singleton()->get_physics_frames(); action.idle_frame = Engine::get_singleton()->get_idle_frames(); action.pressed = true; + action.strength = 0.f; action_state[p_action] = action; } @@ -548,6 +575,7 @@ void InputDefault::action_release(const StringName &p_action) { action.physics_frame = Engine::get_singleton()->get_physics_frames(); action.idle_frame = Engine::get_singleton()->get_idle_frames(); action.pressed = false; + action.strength = 0.f; action_state[p_action] = action; } @@ -576,7 +604,7 @@ void InputDefault::ensure_touch_mouse_raised() { button_event->set_global_position(mouse_pos); button_event->set_pressed(false); button_event->set_button_index(BUTTON_LEFT); - button_event->set_button_mask(mouse_button_mask & ~(1 << BUTTON_LEFT - 1)); + button_event->set_button_mask(mouse_button_mask & ~(1 << (BUTTON_LEFT - 1))); _parse_input_event_impl(button_event, true); } @@ -598,7 +626,13 @@ Input::CursorShape InputDefault::get_default_cursor_shape() { void InputDefault::set_default_cursor_shape(CursorShape p_shape) { default_shape = p_shape; - OS::get_singleton()->set_cursor_shape((OS::CursorShape)p_shape); + // The default shape is set in Viewport::_gui_input_event. To instantly + // see the shape in the viewport we need to trigger a mouse motion event. + Ref<InputEventMouseMotion> mm; + mm.instance(); + mm->set_position(mouse_pos); + mm->set_global_position(mouse_pos); + parse_input_event(mm); } void InputDefault::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { @@ -630,6 +664,7 @@ InputDefault::InputDefault() { emulate_mouse_from_touch = false; mouse_from_touch_index = -1; main_loop = NULL; + default_shape = CURSOR_ARROW; hat_map_default[HAT_UP].type = TYPE_BUTTON; hat_map_default[HAT_UP].index = JOY_DPAD_UP; diff --git a/main/input_default.h b/main/input_default.h index 2e3cae8520..4964b9a83c 100644 --- a/main/input_default.h +++ b/main/input_default.h @@ -31,7 +31,7 @@ #ifndef INPUT_DEFAULT_H #define INPUT_DEFAULT_H -#include "os/input.h" +#include "core/os/input.h" class InputDefault : public Input { @@ -117,9 +117,11 @@ class InputDefault : public Input { }; SpeedTrack mouse_speed_track; + Map<int, SpeedTrack> touch_speed_track; Map<int, Joypad> joy_names; int fallback_mapping; - CursorShape default_shape = CURSOR_ARROW; + + CursorShape default_shape; public: enum HatMask { diff --git a/main/main.cpp b/main/main.cpp index 21851337b7..6310281ff8 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -30,117 +30,144 @@ #include "main.h" -#include "app_icon.gen.h" +#include "core/input_map.h" +#include "core/io/file_access_network.h" +#include "core/io/file_access_pack.h" +#include "core/io/file_access_zip.h" +#include "core/io/image_loader.h" +#include "core/io/ip.h" +#include "core/io/resource_loader.h" +#include "core/io/stream_peer_ssl.h" +#include "core/io/stream_peer_tcp.h" +#include "core/message_queue.h" +#include "core/os/dir_access.h" +#include "core/os/os.h" +#include "core/project_settings.h" #include "core/register_core_types.h" +#include "core/script_debugger_local.h" +#include "core/script_debugger_remote.h" +#include "core/script_language.h" +#include "core/translation.h" +#include "core/version.h" +#include "core/version_hash.gen.h" #include "drivers/register_driver_types.h" -#include "message_queue.h" +#include "main/app_icon.gen.h" +#include "main/input_default.h" +#include "main/performance.h" +#include "main/splash.gen.h" +#include "main/splash_editor.gen.h" +#include "main/tests/test_main.h" +#include "main/timer_sync.h" #include "modules/register_module_types.h" -#include "os/os.h" #include "platform/register_platform_apis.h" -#include "project_settings.h" -#include "scene/register_scene_types.h" -#include "script_debugger_local.h" -#include "script_debugger_remote.h" -#include "servers/register_server_types.h" -#include "splash.gen.h" -#include "splash_editor.gen.h" - -#include "input_map.h" -#include "io/resource_loader.h" #include "scene/main/scene_tree.h" +#include "scene/main/viewport.h" +#include "scene/register_scene_types.h" +#include "scene/resources/packed_scene.h" #include "servers/arvr_server.h" #include "servers/audio_server.h" #include "servers/physics_2d_server.h" #include "servers/physics_server.h" - -#include "io/resource_loader.h" -#include "script_language.h" - -#include "core/io/ip.h" -#include "main/tests/test_main.h" -#include "os/dir_access.h" -#include "scene/main/viewport.h" -#include "scene/resources/packed_scene.h" +#include "servers/register_server_types.h" #ifdef TOOLS_ENABLED #include "editor/doc/doc_data.h" #include "editor/doc/doc_data_class_path.gen.h" #include "editor/editor_node.h" +#include "editor/editor_settings.h" #include "editor/project_manager.h" #endif -#include "io/file_access_network.h" -#include "servers/physics_2d_server.h" - -#include "core/io/file_access_pack.h" -#include "core/io/file_access_zip.h" -#include "core/io/stream_peer_ssl.h" -#include "core/io/stream_peer_tcp.h" -#include "main/input_default.h" -#include "performance.h" -#include "translation.h" -#include "version.h" -#include "version_hash.gen.h" +/* Static members */ -#include "main/timer_sync.h" +// Singletons -static ProjectSettings *globals = NULL; +// Initialized in setup() static Engine *engine = NULL; +static ProjectSettings *globals = NULL; static InputMap *input_map = NULL; -static bool _start_success = false; -static ScriptDebugger *script_debugger = NULL; -AudioServer *audio_server = NULL; -ARVRServer *arvr_server = NULL; -PhysicsServer *physics_server = NULL; -Physics2DServer *physics_2d_server = NULL; - -static MessageQueue *message_queue = NULL; +static TranslationServer *translation_server = NULL; static Performance *performance = NULL; - static PackedData *packed_data = NULL; #ifdef MINIZIP_ENABLED static ZipArchive *zip_packed_data = NULL; #endif static FileAccessNetworkClient *file_access_network_client = NULL; -static TranslationServer *translation_server = NULL; +static ScriptDebugger *script_debugger = NULL; +static MessageQueue *message_queue = NULL; + +// Initialized in setup2() +static AudioServer *audio_server = NULL; +static ARVRServer *arvr_server = NULL; +static PhysicsServer *physics_server = NULL; +static Physics2DServer *physics_2d_server = NULL; +// We error out if setup2() doesn't turn this true +static bool _start_success = false; + +// Drivers + +static int video_driver_idx = -1; +static int audio_driver_idx = -1; + +// Engine config/tools + +static bool editor = false; +static bool project_manager = false; +static String locale; +static bool show_help = false; +static bool auto_quit = false; +static OS::ProcessID allow_focus_steal_pid = 0; +#ifdef TOOLS_ENABLED +static bool auto_build_solutions = false; +#endif + +// Display static OS::VideoMode video_mode; +static int init_screen = -1; +static bool init_fullscreen = false; static bool init_maximized = false; static bool init_windowed = false; -static bool init_fullscreen = false; static bool init_always_on_top = false; static bool init_use_custom_pos = false; +static Vector2 init_custom_pos; +static bool force_lowdpi = false; + +// Debug + +static bool use_debug_profiler = false; #ifdef DEBUG_ENABLED static bool debug_collisions = false; static bool debug_navigation = false; #endif static int frame_delay = 0; -static Vector2 init_custom_pos; -static int video_driver_idx = -1; -static int audio_driver_idx = -1; -static String locale; -static bool use_debug_profiler = false; -static bool force_lowdpi = false; -static int init_screen = -1; -static bool use_vsync = true; -static bool editor = false; -static bool show_help = false; static bool disable_render_loop = false; static int fixed_fps = -1; -static bool auto_build_solutions = false; -static bool auto_quit = false; static bool print_fps = false; -static OS::ProcessID allow_focus_steal_pid = 0; - -static bool project_manager = false; +/* Helper methods */ +// Used by Mono module, should likely be registered in Engine singleton instead +// FIXME: This is also not 100% accurate, `project_manager` is only true when it was requested, +// but not if e.g. we fail to load and project and fallback to the manager. bool Main::is_project_manager() { return project_manager; } -void initialize_physics() { +static String unescape_cmdline(const String &p_str) { + return p_str.replace("%20", " "); +} + +static String get_full_version_string() { + String hash = String(VERSION_HASH); + if (hash.length() != 0) + hash = "." + hash.left(7); + return String(VERSION_FULL_BUILD) + hash; +} +// FIXME: Could maybe be moved to PhysicsServerManager and Physics2DServerManager directly +// to have less code in main.cpp. +void initialize_physics() { /// 3D Physics Server physics_server = PhysicsServerManager::new_server(ProjectSettings::get_singleton()->get(PhysicsServerManager::setting_property_name)); if (!physics_server) { @@ -168,19 +195,6 @@ void finalize_physics() { memdelete(physics_2d_server); } -static String unescape_cmdline(const String &p_str) { - - return p_str.replace("%20", " "); -} - -static String get_full_version_string() { - - String hash = String(VERSION_HASH); - if (hash.length() != 0) - hash = "." + hash.left(7); - return String(VERSION_FULL_BUILD) + hash; -} - //#define DEBUG_INIT #ifdef DEBUG_INIT #define MAIN_PRINT(m_txt) print_line(m_txt) @@ -285,6 +299,32 @@ void Main::print_help(const char *p_binary) { #endif } +/* Engine initialization + * + * Consists of several methods that are called by each platform's specific main(argc, argv). + * To fully understand engine init, one should therefore start from the platform's main and + * see how it calls into the Main class' methods. + * + * The initialization is typically done in 3 steps (with the setup2 step triggered either + * automatically by setup, or manually in the platform's main). + * + * - setup(execpath, argc, argv, p_second_phase) is the main entry point for all platforms, + * responsible for the initialization of all low level singletons and core types, and parsing + * command line arguments to configure things accordingly. + * If p_second_phase is true, it will chain into setup2() (default behaviour). This is + * disabled on some platforms (Android, iOS, UWP) which trigger the second step in their + * own time. + * + * - setup2(p_main_tid_override) registers high level servers and singletons, displays the + * boot splash, then registers higher level types (scene, editor, etc.). + * + * - start() is the last step and that's where command line tools can run, or the main loop + * can be created eventually and the project settings put into action. That's also where + * the editor node is created, if relevant. + * start() does it own argument parsing for a subset of the command line arguments described + * in help, it's a bit messy and should be globalized with the setup() parsing somehow. + */ + Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_phase) { RID_OwnerBase::init_rid(); @@ -689,16 +729,22 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph #ifdef TOOLS_ENABLED editor = false; #else - OS::get_singleton()->print("Error: Could not load game path '%s'.\n", project_path.ascii().get_data()); + String error_msg = "Error: Could not load game data at path '" + project_path + "'. Is the .pck file missing?\n"; + OS::get_singleton()->print(error_msg.ascii().get_data()); + OS::get_singleton()->alert(error_msg); goto error; #endif } GLOBAL_DEF("memory/limits/multithreaded_server/rid_pool_prealloc", 60); + ProjectSettings::get_singleton()->set_custom_property_info("memory/limits/multithreaded_server/rid_pool_prealloc", PropertyInfo(Variant::INT, "memory/limits/multithreaded_server/rid_pool_prealloc", PROPERTY_HINT_RANGE, "0,500,1")); // No negative and limit to 500 due to crashes GLOBAL_DEF("network/limits/debugger_stdout/max_chars_per_second", 2048); + ProjectSettings::get_singleton()->set_custom_property_info("network/limits/debugger_stdout/max_chars_per_second", PropertyInfo(Variant::INT, "network/limits/debugger_stdout/max_chars_per_second", PROPERTY_HINT_RANGE, "0, 4096, 1, or_greater")); GLOBAL_DEF("network/limits/debugger_stdout/max_messages_per_frame", 10); + ProjectSettings::get_singleton()->set_custom_property_info("network/limits/debugger_stdout/max_messages_per_frame", PropertyInfo(Variant::INT, "network/limits/debugger_stdout/max_messages_per_frame", PROPERTY_HINT_RANGE, "0, 20, 1, or_greater")); GLOBAL_DEF("network/limits/debugger_stdout/max_errors_per_frame", 10); + ProjectSettings::get_singleton()->set_custom_property_info("network/limits/debugger_stdout/max_errors_per_frame", PropertyInfo(Variant::INT, "network/limits/debugger_stdout/max_errors_per_frame", PROPERTY_HINT_RANGE, "0, 20, 1, or_greater")); if (debug_mode == "remote") { @@ -764,7 +810,6 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph if (editor) { packed_data->set_disabled(true); globals->set_disable_feature_overrides(true); - StreamPeerSSL::initialize_certs = false; //will be initialized by editor } #endif @@ -772,6 +817,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph GLOBAL_DEF("logging/file_logging/enable_file_logging", false); GLOBAL_DEF("logging/file_logging/log_path", "user://logs/log.txt"); GLOBAL_DEF("logging/file_logging/max_log_files", 10); + ProjectSettings::get_singleton()->set_custom_property_info("logging/file_logging/max_log_files", PropertyInfo(Variant::INT, "logging/file_logging/max_log_files", PROPERTY_HINT_RANGE, "0,20,1,or_greater")); //no negative numbers if (FileAccess::get_create_func(FileAccess::ACCESS_USERDATA) && GLOBAL_GET("logging/file_logging/enable_file_logging")) { String base_path = GLOBAL_GET("logging/file_logging/log_path"); int max_files = GLOBAL_GET("logging/file_logging/max_log_files"); @@ -830,14 +876,21 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph video_driver = GLOBAL_GET("rendering/quality/driver/driver_name"); } + GLOBAL_DEF("rendering/quality/driver/driver_fallback", "Best"); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/driver/driver_fallback", PropertyInfo(Variant::STRING, "rendering/quality/driver/driver_fallback", PROPERTY_HINT_ENUM, "Best,Never")); + GLOBAL_DEF("display/window/size/width", 1024); + ProjectSettings::get_singleton()->set_custom_property_info("display/window/size/width", PropertyInfo(Variant::INT, "display/window/size/width", PROPERTY_HINT_RANGE, "0,7680,or_greater")); // 8K resolution GLOBAL_DEF("display/window/size/height", 600); + ProjectSettings::get_singleton()->set_custom_property_info("display/window/size/height", PropertyInfo(Variant::INT, "display/window/size/height", PROPERTY_HINT_RANGE, "0,4320,or_greater")); // 8K resolution GLOBAL_DEF("display/window/size/resizable", true); GLOBAL_DEF("display/window/size/borderless", false); GLOBAL_DEF("display/window/size/fullscreen", false); GLOBAL_DEF("display/window/size/always_on_top", false); GLOBAL_DEF("display/window/size/test_width", 0); + ProjectSettings::get_singleton()->set_custom_property_info("display/window/size/test_width", PropertyInfo(Variant::INT, "display/window/size/test_width", PROPERTY_HINT_RANGE, "0,7680,or_greater")); // 8K resolution GLOBAL_DEF("display/window/size/test_height", 0); + ProjectSettings::get_singleton()->set_custom_property_info("display/window/size/test_height", PropertyInfo(Variant::INT, "display/window/size/test_height", PROPERTY_HINT_RANGE, "0,4320,or_greater")); // 8K resolution if (use_custom_res) { @@ -955,6 +1008,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph Engine::get_singleton()->set_iterations_per_second(GLOBAL_DEF("physics/common/physics_fps", 60)); Engine::get_singleton()->set_physics_jitter_fix(GLOBAL_DEF("physics/common/physics_jitter_fix", 0.5)); Engine::get_singleton()->set_target_fps(GLOBAL_DEF("debug/settings/fps/force_fps", 0)); + ProjectSettings::get_singleton()->set_custom_property_info("debug/settings/fps/force_fps", PropertyInfo(Variant::INT, "debug/settings/fps/force_fps", PROPERTY_HINT_RANGE, "0,120,1,or_greater")); GLOBAL_DEF("debug/settings/stdout/print_fps", false); @@ -963,10 +1017,12 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph if (frame_delay == 0) { frame_delay = GLOBAL_DEF("application/run/frame_delay_msec", 0); + ProjectSettings::get_singleton()->set_custom_property_info("application/run/frame_delay_msec", PropertyInfo(Variant::INT, "application/run/frame_delay_msec", PROPERTY_HINT_RANGE, "0,100,1,or_greater")); // No negative numbers } OS::get_singleton()->set_low_processor_usage_mode(GLOBAL_DEF("application/run/low_processor_mode", false)); OS::get_singleton()->set_low_processor_usage_mode_sleep_usec(GLOBAL_DEF("application/run/low_processor_mode_sleep_usec", 8000)); + ProjectSettings::get_singleton()->set_custom_property_info("application/run/low_processor_mode_sleep_usec", PropertyInfo(Variant::INT, "application/run/low_processor_mode_sleep_usec", PROPERTY_HINT_RANGE, "0,33200,1,or_greater")); // No negative numbers Engine::get_singleton()->set_frame_delay(frame_delay); @@ -1008,15 +1064,6 @@ error: if (file_access_network_client) memdelete(file_access_network_client); - // Note 1: *zip_packed_data live into *packed_data - // Note 2: PackedData::~PackedData destroy this. - /* -#ifdef MINIZIP_ENABLED - if (zip_packed_data) - memdelete( zip_packed_data ); -#endif -*/ - unregister_core_driver_types(); unregister_core_types(); @@ -1040,6 +1087,7 @@ Error Main::setup2(Thread::ID p_main_tid_override) { if (err != OK) { return err; } + if (init_use_custom_pos) { OS::get_singleton()->set_window_position(init_custom_pos); } @@ -1093,37 +1141,30 @@ Error Main::setup2(Thread::ID p_main_tid_override) { boot_logo_path = boot_logo_path.strip_edges(); if (boot_logo_path != String()) { - print_line("Boot splash path: " + boot_logo_path); boot_logo.instance(); - Error err = boot_logo->load(boot_logo_path); + Error err = ImageLoader::load_image(boot_logo_path, boot_logo); if (err) ERR_PRINTS("Non-existing or invalid boot splash at: " + boot_logo_path + ". Loading default splash."); } + Color boot_bg_color = GLOBAL_DEF("application/boot_splash/bg_color", boot_splash_bg_color); if (boot_logo.is_valid()) { OS::get_singleton()->_msec_splash = OS::get_singleton()->get_ticks_msec(); - Color boot_bg = GLOBAL_DEF("application/boot_splash/bg_color", clear); - VisualServer::get_singleton()->set_boot_image(boot_logo, boot_bg, boot_logo_scale); -#ifndef TOOLS_ENABLED -//no tools, so free the boot logo (no longer needed) -//ProjectSettings::get_singleton()->set("application/boot_logo",Image()); -#endif + VisualServer::get_singleton()->set_boot_image(boot_logo, boot_bg_color, boot_logo_scale); } else { #ifndef NO_DEFAULT_BOOT_LOGO - MAIN_PRINT("Main: Create bootsplash"); #if defined(TOOLS_ENABLED) && !defined(NO_EDITOR_SPLASH) - Ref<Image> splash = (editor || project_manager) ? memnew(Image(boot_splash_editor_png)) : memnew(Image(boot_splash_png)); #else Ref<Image> splash = memnew(Image(boot_splash_png)); #endif MAIN_PRINT("Main: ClearColor"); - VisualServer::get_singleton()->set_default_clear_color(boot_splash_bg_color); + VisualServer::get_singleton()->set_default_clear_color(boot_bg_color); MAIN_PRINT("Main: Image"); - VisualServer::get_singleton()->set_boot_image(splash, boot_splash_bg_color, false); + VisualServer::get_singleton()->set_boot_image(splash, boot_bg_color, false); #endif } @@ -1220,7 +1261,6 @@ Error Main::setup2(Thread::ID p_main_tid_override) { } // everything the main loop needs to know about frame timings - static MainTimerSync main_timer_sync; bool Main::start() { @@ -1605,12 +1645,13 @@ bool Main::start() { sml->set_use_font_oversampling(font_oversampling); } else { + GLOBAL_DEF("display/window/stretch/mode", "disabled"); ProjectSettings::get_singleton()->set_custom_property_info("display/window/stretch/mode", PropertyInfo(Variant::STRING, "display/window/stretch/mode", PROPERTY_HINT_ENUM, "disabled,2d,viewport")); GLOBAL_DEF("display/window/stretch/aspect", "ignore"); ProjectSettings::get_singleton()->set_custom_property_info("display/window/stretch/aspect", PropertyInfo(Variant::STRING, "display/window/stretch/aspect", PROPERTY_HINT_ENUM, "ignore,keep,keep_width,keep_height,expand")); GLOBAL_DEF("display/window/stretch/shrink", 1); - ProjectSettings::get_singleton()->set_custom_property_info("display/window/stretch/shrink", PropertyInfo(Variant::STRING, "display/window/stretch/shrink", PROPERTY_HINT_RANGE, "1,8,1")); + ProjectSettings::get_singleton()->set_custom_property_info("display/window/stretch/shrink", PropertyInfo(Variant::REAL, "display/window/stretch/shrink", PROPERTY_HINT_RANGE, "1,8,1")); sml->set_auto_accept_quit(GLOBAL_DEF("application/config/auto_accept_quit", true)); sml->set_quit_on_go_back(GLOBAL_DEF("application/config/quit_on_go_back", true)); GLOBAL_DEF("gui/common/snap_controls_to_pixels", true); @@ -1664,6 +1705,10 @@ bool Main::start() { } if (!project_manager && !editor) { // game + + // Load SSL Certificates from Project Settings (or builtin) + StreamPeerSSL::load_certs_from_memory(StreamPeerSSL::get_project_cert_array()); + if (game_path != "") { Node *scene = NULL; Ref<PackedScene> scenedata = ResourceLoader::load(local_game_path); @@ -1678,7 +1723,7 @@ bool Main::start() { if (iconpath != "") { Ref<Image> icon; icon.instance(); - if (icon->load(iconpath) == OK) { + if (ImageLoader::load_image(iconpath, icon) == OK) { OS::get_singleton()->set_icon(icon); hasicon = true; } @@ -1689,12 +1734,22 @@ bool Main::start() { #ifdef TOOLS_ENABLED if (project_manager || (script == "" && test == "" && game_path == "" && !editor)) { + Engine::get_singleton()->set_editor_hint(true); ProjectManager *pmanager = memnew(ProjectManager); ProgressDialog *progress_dialog = memnew(ProgressDialog); pmanager->add_child(progress_dialog); sml->get_root()->add_child(pmanager); OS::get_singleton()->set_context(OS::CONTEXT_PROJECTMAN); } + + if (project_manager || editor) { + // Load SSL Certificates from Editor Settings (or builtin) + String certs = EditorSettings::get_singleton()->get_setting("network/ssl/editor_ssl_certificates").operator String(); + if (certs != "") + StreamPeerSSL::load_certs_from_file(certs); + else + StreamPeerSSL::load_certs_from_memory(StreamPeerSSL::get_project_cert_array()); + } #endif } @@ -1708,13 +1763,23 @@ bool Main::start() { return true; } +/* Main iteration + * + * This is the iteration of the engine's game loop, advancing the state of physics, + * rendering and audio. + * It's called directly by the platform's OS::run method, where the loop is created + * and monitored. + * + * The OS implementation can impact its draw step with the Main::force_redraw() method. + */ + uint64_t Main::last_ticks = 0; uint64_t Main::target_ticks = 0; uint32_t Main::frames = 0; uint32_t Main::frame = 0; bool Main::force_redraw_requested = false; -//for performance metrics +// For performance metrics static uint64_t physics_process_max = 0; static uint64_t idle_process_max = 0; @@ -1738,11 +1803,6 @@ bool Main::iteration() { Engine::get_singleton()->_frame_step = step; - /* - if (time_accum+step < frame_slice) - return false; - */ - uint64_t physics_process_ticks = 0; uint64_t idle_process_ticks = 0; @@ -1886,9 +1946,15 @@ bool Main::iteration() { } void Main::force_redraw() { - force_redraw_requested = true; -}; +} + +/* Engine deinitialization + * + * Responsible for freeing all the memory allocated by previous setup steps, + * so that the engine closes cleanly without leaking memory or crashing. + * The order matters as some of those steps are linked with each other. + */ void Main::cleanup() { diff --git a/main/main.h b/main/main.h index c20592bf3b..23a19dddec 100644 --- a/main/main.h +++ b/main/main.h @@ -35,9 +35,9 @@ @author Juan Linietsky <reduzio@gmail.com> */ +#include "core/error_list.h" #include "core/os/thread.h" -#include "error_list.h" -#include "typedefs.h" +#include "core/typedefs.h" class Main { @@ -49,13 +49,16 @@ class Main { static bool force_redraw_requested; public: + static bool is_project_manager(); + static Error setup(const char *execpath, int argc, char *argv[], bool p_second_phase = true); static Error setup2(Thread::ID p_main_tid_override = 0); static bool start(); + static bool iteration(); - static void cleanup(); static void force_redraw(); - static bool is_project_manager(); + + static void cleanup(); }; -#endif +#endif // MAIN_H diff --git a/main/main_builders.py b/main/main_builders.py index 6d45768493..038a7d17f5 100644 --- a/main/main_builders.py +++ b/main/main_builders.py @@ -69,8 +69,8 @@ def make_default_controller_mappings(target, source, env): 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") + g.write("#include \"core/typedefs.h\"\n") + g.write("#include \"main/default_controller_mappings.h\"\n") # ensure mappings have a consistent order platform_mappings = OrderedDict() diff --git a/main/performance.cpp b/main/performance.cpp index 70e0a5f7aa..aab3a8646f 100644 --- a/main/performance.cpp +++ b/main/performance.cpp @@ -29,13 +29,15 @@ /*************************************************************************/ #include "performance.h" -#include "message_queue.h" -#include "os/os.h" + +#include "core/message_queue.h" +#include "core/os/os.h" #include "scene/main/scene_tree.h" #include "servers/audio_server.h" #include "servers/physics_2d_server.h" #include "servers/physics_server.h" #include "servers/visual_server.h" + Performance *Performance::singleton = NULL; void Performance::_bind_methods() { diff --git a/main/performance.h b/main/performance.h index de00df5ff9..41822562c5 100644 --- a/main/performance.h +++ b/main/performance.h @@ -31,7 +31,7 @@ #ifndef PERFORMANCE_H #define PERFORMANCE_H -#include "object.h" +#include "core/object.h" #define PERF_WARN_OFFLINE_FUNCTION #define PERF_WARN_PROCESS_SYNC diff --git a/main/splash_editor.png b/main/splash_editor.png Binary files differindex f003995d6f..d5bc2f1ce6 100644 --- a/main/splash_editor.png +++ b/main/splash_editor.png diff --git a/main/tests/SCsub b/main/tests/SCsub index 26a0819ee8..437d9ed777 100644 --- a/main/tests/SCsub +++ b/main/tests/SCsub @@ -5,9 +5,5 @@ Import('env') env.tests_sources = [] env.add_source_files(env.tests_sources, "*.cpp") -Export('env') - -# SConscript('math/SCsub'); - lib = env.add_library("tests", env.tests_sources) env.Prepend(LIBS=[lib]) diff --git a/drivers/unix/packet_peer_udp_posix.h b/main/tests/test_astar.cpp index 7f72a9bfc9..8b48f075bc 100644 --- a/drivers/unix/packet_peer_udp_posix.h +++ b/main/tests/test_astar.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* packet_peer_udp_posix.h */ +/* test_astar.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,61 +28,90 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef PACKET_PEER_UDP_POSIX_H -#define PACKET_PEER_UDP_POSIX_H +#include "test_astar.h" -#ifdef UNIX_ENABLED +#include "core/math/a_star.h" +#include "core/os/os.h" -#include "io/packet_peer_udp.h" -#include "ring_buffer.h" +#include <stdio.h> -class PacketPeerUDPPosix : public PacketPeerUDP { - - enum { - PACKET_BUFFER_SIZE = 65536 - }; - - RingBuffer<uint8_t> rb; - uint8_t recv_buffer[PACKET_BUFFER_SIZE]; - uint8_t packet_buffer[PACKET_BUFFER_SIZE]; - IP_Address packet_ip; - int packet_port; - int queue_count; - int sockfd; - bool sock_blocking; - IP::Type sock_type; - - IP_Address peer_addr; - int peer_port; - - _FORCE_INLINE_ int _get_socket(); - - static PacketPeerUDP *_create(); - void _set_sock_blocking(bool p_blocking); - virtual Error _poll(bool p_block); +namespace TestAStar { +class ABCX : public AStar { public: - 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); - - virtual int get_max_packet_size() const; - - virtual Error listen(int p_port, const IP_Address &p_bind_address = IP_Address("*"), int p_recv_buffer_size = 65536); - virtual void close(); - virtual Error wait(); - virtual bool is_listening() const; - - virtual IP_Address get_packet_address() const; - virtual int get_packet_port() const; - - virtual void set_dest_address(const IP_Address &p_address, int p_port); - - static void make_default(); + enum { A, + B, + C, + X }; + + ABCX() { + add_point(A, Vector3(0, 0, 0)); + add_point(B, Vector3(1, 0, 0)); + add_point(C, Vector3(0, 1, 0)); + add_point(X, Vector3(0, 0, 1)); + connect_points(A, B); + connect_points(A, C); + connect_points(B, C); + connect_points(X, A); + } + + // Disable heuristic completely + float _compute_cost(int p_from, int p_to) { + if (p_from == A && p_to == C) { + return 1000; + } + return 100; + } +}; - PacketPeerUDPPosix(); - ~PacketPeerUDPPosix(); +bool test_abc() { + ABCX abcx; + PoolVector<int> path = abcx.get_id_path(ABCX::A, ABCX::C); + bool ok = path.size() == 3; + int i = 0; + ok = ok && path[i++] == ABCX::A; + ok = ok && path[i++] == ABCX::B; + ok = ok && path[i++] == ABCX::C; + return ok; +} + +bool test_abcx() { + ABCX abcx; + PoolVector<int> path = abcx.get_id_path(ABCX::X, ABCX::C); + bool ok = path.size() == 4; + int i = 0; + ok = ok && path[i++] == ABCX::X; + ok = ok && path[i++] == ABCX::A; + ok = ok && path[i++] == ABCX::B; + ok = ok && path[i++] == ABCX::C; + return ok; +} + +typedef bool (*TestFunc)(void); + +TestFunc test_funcs[] = { + test_abc, + test_abcx, + NULL }; -#endif // PACKET_PEER_UDP_POSIX_H -#endif +MainLoop *test() { + int count = 0; + int passed = 0; + + while (true) { + if (!test_funcs[count]) + break; + bool pass = test_funcs[count](); + if (pass) + passed++; + OS::get_singleton()->print("\t%s\n", pass ? "PASS" : "FAILED"); + + count++; + } + OS::get_singleton()->print("\n"); + OS::get_singleton()->print("Passed %i of %i tests\n", passed, count); + return NULL; +} + +} // namespace TestAStar diff --git a/editor/editor_initialize_ssl.h b/main/tests/test_astar.h index 71d16b8c53..6458f9efb1 100644 --- a/editor/editor_initialize_ssl.h +++ b/main/tests/test_astar.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* editor_initialize_ssl.h */ +/* test_astar.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,9 +28,14 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef EDITOR_INITIALIZE_SSL_H -#define EDITOR_INITIALIZE_SSL_H +#ifndef TEST_ASTAR_H +#define TEST_ASTAR_H -void editor_initialize_certificates(); +#include "core/os/main_loop.h" -#endif // EDITOR_INITIALIZE_SSL_H +namespace TestAStar { + +MainLoop *test(); +} + +#endif diff --git a/main/tests/test_gdscript.cpp b/main/tests/test_gdscript.cpp index 0a9d03c1b7..4d2fa2a26d 100644 --- a/main/tests/test_gdscript.cpp +++ b/main/tests/test_gdscript.cpp @@ -30,9 +30,9 @@ #include "test_gdscript.h" -#include "os/file_access.h" -#include "os/main_loop.h" -#include "os/os.h" +#include "core/os/file_access.h" +#include "core/os/main_loop.h" +#include "core/os/os.h" #ifdef GDSCRIPT_ENABLED @@ -357,6 +357,9 @@ static void _parser_show_block(const GDScriptParser::BlockNode *p_block, int p_i _parser_show_block(cf_node->body, p_indent + 1); } break; + case GDScriptParser::ControlFlowNode::CF_MATCH: { + // FIXME: Implement + } break; case GDScriptParser::ControlFlowNode::CF_SWITCH: { } break; diff --git a/main/tests/test_gdscript.h b/main/tests/test_gdscript.h index 91e0be1238..0a052c8db5 100644 --- a/main/tests/test_gdscript.h +++ b/main/tests/test_gdscript.h @@ -31,7 +31,7 @@ #ifndef TEST_GDSCRIPT_H #define TEST_GDSCRIPT_H -#include "os/main_loop.h" +#include "core/os/main_loop.h" namespace TestGDScript { diff --git a/main/tests/test_gui.cpp b/main/tests/test_gui.cpp index 305b749717..271353f1dd 100644 --- a/main/tests/test_gui.cpp +++ b/main/tests/test_gui.cpp @@ -32,9 +32,9 @@ #include "test_gui.h" -#include "io/image_loader.h" -#include "os/os.h" -#include "print_string.h" +#include "core/io/image_loader.h" +#include "core/os/os.h" +#include "core/print_string.h" #include "scene/2d/sprite.h" #include "scene/gui/button.h" #include "scene/gui/control.h" @@ -60,8 +60,6 @@ namespace TestGUI { class TestMainLoop : public SceneTree { - Control *control; - public: virtual void request_quit() { @@ -235,7 +233,7 @@ public: richtext->push_meta("http://www.scrollingcapabilities.xz"); richtext->add_text("This allows to test for the scrolling capabilities "); richtext->pop(); - richtext->add_text("of the rich text label for huge text (not like this text will really be huge but, you know).\nAs long as it is so long that it will work nicely for a test/demo, then it's welcomed in my book...\nChanging subject, the day is cloudy today and I'm wondering if I'll get che chance to travel somewhere nice. Sometimes, watching the clouds from satellite images may give a nice insight about how pressure zones in our planet work, althogh it also makes it pretty obvious to see why most weather forecasts get it wrong so often.\nClouds are so difficult to predict!\nBut it's pretty cool how our civilization has adapted to having water falling from the sky each time it rains..."); + richtext->add_text("of the rich text label for huge text (not like this text will really be huge but, you know).\nAs long as it is so long that it will work nicely for a test/demo, then it's welcomed in my book...\nChanging subject, the day is cloudy today and I'm wondering if I'll get che chance to travel somewhere nice. Sometimes, watching the clouds from satellite images may give a nice insight about how pressure zones in our planet work, although it also makes it pretty obvious to see why most weather forecasts get it wrong so often.\nClouds are so difficult to predict!\nBut it's pretty cool how our civilization has adapted to having water falling from the sky each time it rains..."); TabContainer *tabc = memnew(TabContainer); diff --git a/main/tests/test_gui.h b/main/tests/test_gui.h index 3ed9dae522..25dfa3bc2b 100644 --- a/main/tests/test_gui.h +++ b/main/tests/test_gui.h @@ -31,7 +31,7 @@ #ifndef TEST_GUI_H #define TEST_GUI_H -#include "os/main_loop.h" +#include "core/os/main_loop.h" /** @author Juan Linietsky <reduzio@gmail.com> diff --git a/main/tests/test_image.cpp b/main/tests/test_image.cpp index cb87458a93..979e590ab8 100644 --- a/main/tests/test_image.cpp +++ b/main/tests/test_image.cpp @@ -30,10 +30,10 @@ #include "test_image.h" -#include "io/image_loader.h" -#include "math_funcs.h" -#include "os/main_loop.h" -#include "print_string.h" +#include "core/io/image_loader.h" +#include "core/math/math_funcs.h" +#include "core/os/main_loop.h" +#include "core/print_string.h" namespace TestImage { diff --git a/main/tests/test_image.h b/main/tests/test_image.h index d45b4e4e15..381edf7ef9 100644 --- a/main/tests/test_image.h +++ b/main/tests/test_image.h @@ -35,7 +35,7 @@ @author Juan Linietsky <reduzio@gmail.com> */ -#include "os/main_loop.h" +#include "core/os/main_loop.h" namespace TestImage { diff --git a/main/tests/test_io.cpp b/main/tests/test_io.cpp index 4f98955995..4e43ee6a54 100644 --- a/main/tests/test_io.cpp +++ b/main/tests/test_io.cpp @@ -32,16 +32,16 @@ #ifdef MINIZIP_ENABLED +#include "core/io/resource_loader.h" +#include "core/io/resource_saver.h" +#include "core/os/dir_access.h" +#include "core/os/main_loop.h" +#include "core/os/os.h" +#include "core/print_string.h" #include "core/project_settings.h" -#include "io/resource_loader.h" -#include "io/resource_saver.h" -#include "os/dir_access.h" -#include "os/main_loop.h" -#include "os/os.h" -#include "print_string.h" #include "scene/resources/texture.h" -#include "io/file_access_memory.h" +#include "core/io/file_access_memory.h" namespace TestIO { @@ -50,7 +50,7 @@ class TestMainLoop : public MainLoop { bool quit; public: - virtual void input_event(const InputEvent &p_event) { + virtual void input_event(const Ref<InputEvent> &p_event) { } virtual bool idle(float p_time) { return false; diff --git a/main/tests/test_io.h b/main/tests/test_io.h index 76567829e7..ffebd05160 100644 --- a/main/tests/test_io.h +++ b/main/tests/test_io.h @@ -35,7 +35,7 @@ @author Juan Linietsky <reduzio@gmail.com> */ -#include "os/main_loop.h" +#include "core/os/main_loop.h" namespace TestIO { diff --git a/main/tests/test_main.cpp b/main/tests/test_main.cpp index cbc1107acb..714a254371 100644 --- a/main/tests/test_main.cpp +++ b/main/tests/test_main.cpp @@ -28,11 +28,12 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "list.h" -#include "os/main_loop.h" +#include "core/list.h" +#include "core/os/main_loop.h" #ifdef DEBUG_ENABLED +#include "test_astar.h" #include "test_gdscript.h" #include "test_gui.h" #include "test_image.h" @@ -64,6 +65,7 @@ const char **tests_get_names() { "gd_bytecode", "image", "ordered_hash_map", + "astar", NULL }; @@ -149,6 +151,11 @@ MainLoop *test_main(String p_test, const List<String> &p_args) { return TestOrderedHashMap::test(); } + if (p_test == "astar") { + + return TestAStar::test(); + } + return NULL; } diff --git a/main/tests/test_main.h b/main/tests/test_main.h index d319391070..55ab4daeb8 100644 --- a/main/tests/test_main.h +++ b/main/tests/test_main.h @@ -31,8 +31,8 @@ #ifndef TEST_MAIN_H #define TEST_MAIN_H -#include "list.h" -#include "ustring.h" +#include "core/list.h" +#include "core/ustring.h" const char **tests_get_names(); MainLoop *test_main(String p_test, const List<String> &p_args); diff --git a/main/tests/test_math.cpp b/main/tests/test_math.cpp index 1a72416d6a..a48fdbe4e3 100644 --- a/main/tests/test_math.cpp +++ b/main/tests/test_math.cpp @@ -30,22 +30,22 @@ #include "test_math.h" -#include "camera_matrix.h" -#include "math_funcs.h" -#include "matrix3.h" -#include "os/file_access.h" -#include "os/keyboard.h" -#include "os/os.h" -#include "print_string.h" +#include "core/math/camera_matrix.h" +#include "core/math/math_funcs.h" +#include "core/math/matrix3.h" +#include "core/math/transform.h" +#include "core/os/file_access.h" +#include "core/os/keyboard.h" +#include "core/os/os.h" +#include "core/print_string.h" +#include "core/ustring.h" +#include "core/variant.h" +#include "core/vmap.h" #include "scene/main/node.h" #include "scene/resources/texture.h" #include "servers/visual/shader_language.h" -#include "transform.h" -#include "ustring.h" -#include "variant.h" -#include "vmap.h" -#include "method_ptrcall.h" +#include "core/method_ptrcall.h" namespace TestMath { diff --git a/main/tests/test_math.h b/main/tests/test_math.h index 26a33aa164..2d0c6c461f 100644 --- a/main/tests/test_math.h +++ b/main/tests/test_math.h @@ -31,7 +31,7 @@ #ifndef TEST_MATH_H #define TEST_MATH_H -#include "os/main_loop.h" +#include "core/os/main_loop.h" namespace TestMath { diff --git a/main/tests/test_oa_hash_map.cpp b/main/tests/test_oa_hash_map.cpp index 0e34faace7..deaba285cf 100644 --- a/main/tests/test_oa_hash_map.cpp +++ b/main/tests/test_oa_hash_map.cpp @@ -48,7 +48,7 @@ MainLoop *test() { map.set(1337, 21); map.set(42, 11880); - int value; + int value = 0; map.lookup(42, value); OS::get_singleton()->print("capacity %d\n", map.get_capacity()); @@ -92,6 +92,35 @@ MainLoop *test() { } } + // stress test / test for issue #22928 + { + OAHashMap<int, int> map; + int dummy; + const int N = 1000; + uint32_t *keys = new uint32_t[N]; + + Math::seed(0); + + // insert a couple of random keys (with a dummy value, which is ignored) + for (int i = 0; i < N; i++) { + keys[i] = Math::rand(); + map.set(keys[i], dummy); + + if (!map.lookup(keys[i], dummy)) + OS::get_singleton()->print("could not find 0x%X despite it was just inserted!\n", unsigned(keys[i])); + } + + // check whether the keys are still present + for (int i = 0; i < N; i++) { + if (!map.lookup(keys[i], dummy)) { + OS::get_singleton()->print("could not find 0x%X despite it has been inserted previously! (not checking the other keys, breaking...)\n", unsigned(keys[i])); + break; + } + } + + delete[] keys; + } + return NULL; } } // namespace TestOAHashMap diff --git a/main/tests/test_oa_hash_map.h b/main/tests/test_oa_hash_map.h index a63da537d8..677021f933 100644 --- a/main/tests/test_oa_hash_map.h +++ b/main/tests/test_oa_hash_map.h @@ -31,7 +31,7 @@ #ifndef TEST_OA_HASH_MAP_H #define TEST_OA_HASH_MAP_H -#include "os/main_loop.h" +#include "core/os/main_loop.h" namespace TestOAHashMap { diff --git a/main/tests/test_ordered_hash_map.cpp b/main/tests/test_ordered_hash_map.cpp index 668a8788ff..cad52ceedf 100644 --- a/main/tests/test_ordered_hash_map.cpp +++ b/main/tests/test_ordered_hash_map.cpp @@ -28,10 +28,10 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "ordered_hash_map.h" -#include "os/os.h" -#include "pair.h" -#include "vector.h" +#include "core/ordered_hash_map.h" +#include "core/os/os.h" +#include "core/pair.h" +#include "core/vector.h" namespace TestOrderedHashMap { @@ -168,4 +168,4 @@ MainLoop *test() { return NULL; } -} // namespace TestOrderedHashMap
\ No newline at end of file +} // namespace TestOrderedHashMap diff --git a/main/tests/test_physics.cpp b/main/tests/test_physics.cpp index 99c8fce70e..c869b268b0 100644 --- a/main/tests/test_physics.cpp +++ b/main/tests/test_physics.cpp @@ -30,12 +30,12 @@ #include "test_physics.h" -#include "map.h" -#include "math_funcs.h" -#include "os/main_loop.h" -#include "os/os.h" -#include "print_string.h" -#include "quick_hull.h" +#include "core/map.h" +#include "core/math/math_funcs.h" +#include "core/math/quick_hull.h" +#include "core/os/main_loop.h" +#include "core/os/os.h" +#include "core/print_string.h" #include "servers/physics_server.h" #include "servers/visual_server.h" diff --git a/main/tests/test_physics.h b/main/tests/test_physics.h index 4d62f3a0cf..c260bf9fcc 100644 --- a/main/tests/test_physics.h +++ b/main/tests/test_physics.h @@ -35,7 +35,7 @@ @author Juan Linietsky <reduzio@gmail.com> */ -#include "os/main_loop.h" +#include "core/os/main_loop.h" namespace TestPhysics { diff --git a/main/tests/test_physics_2d.cpp b/main/tests/test_physics_2d.cpp index 482a858650..8245ee276e 100644 --- a/main/tests/test_physics_2d.cpp +++ b/main/tests/test_physics_2d.cpp @@ -30,10 +30,10 @@ #include "test_physics_2d.h" -#include "map.h" -#include "os/main_loop.h" -#include "os/os.h" -#include "print_string.h" +#include "core/map.h" +#include "core/os/main_loop.h" +#include "core/os/os.h" +#include "core/print_string.h" #include "scene/resources/texture.h" #include "servers/physics_2d_server.h" #include "servers/visual_server.h" diff --git a/main/tests/test_physics_2d.h b/main/tests/test_physics_2d.h index 1031aa7f71..52669777c0 100644 --- a/main/tests/test_physics_2d.h +++ b/main/tests/test_physics_2d.h @@ -31,7 +31,7 @@ #ifndef TEST_PHYSICS_2D_H #define TEST_PHYSICS_2D_H -#include "os/main_loop.h" +#include "core/os/main_loop.h" namespace TestPhysics2D { diff --git a/main/tests/test_render.cpp b/main/tests/test_render.cpp index 9340e69bc5..ebf6d363be 100644 --- a/main/tests/test_render.cpp +++ b/main/tests/test_render.cpp @@ -30,12 +30,12 @@ #include "test_render.h" -#include "math_funcs.h" -#include "os/keyboard.h" -#include "os/main_loop.h" -#include "os/os.h" -#include "print_string.h" -#include "quick_hull.h" +#include "core/math/math_funcs.h" +#include "core/math/quick_hull.h" +#include "core/os/keyboard.h" +#include "core/os/main_loop.h" +#include "core/os/os.h" +#include "core/print_string.h" #include "servers/visual_server.h" #define OBJECT_COUNT 50 diff --git a/main/tests/test_render.h b/main/tests/test_render.h index 9084b57067..717074021c 100644 --- a/main/tests/test_render.h +++ b/main/tests/test_render.h @@ -35,7 +35,7 @@ @author Juan Linietsky <reduzio@gmail.com> */ -#include "os/main_loop.h" +#include "core/os/main_loop.h" namespace TestRender { diff --git a/main/tests/test_shader_lang.cpp b/main/tests/test_shader_lang.cpp index 7103b436e1..9df5973376 100644 --- a/main/tests/test_shader_lang.cpp +++ b/main/tests/test_shader_lang.cpp @@ -30,11 +30,11 @@ #include "test_shader_lang.h" -#include "os/file_access.h" -#include "os/main_loop.h" -#include "os/os.h" +#include "core/os/file_access.h" +#include "core/os/main_loop.h" +#include "core/os/os.h" -#include "print_string.h" +#include "core/print_string.h" #include "scene/gui/control.h" #include "scene/gui/text_edit.h" #include "servers/visual/shader_language.h" @@ -194,6 +194,9 @@ static String dump_node_code(SL::Node *p_node, int p_level) { code = vnode->name; } break; + case SL::Node::TYPE_VARIABLE_DECLARATION: { + // FIXME: Implement + } break; case SL::Node::TYPE_CONSTANT: { SL::ConstantNode *cnode = (SL::ConstantNode *)p_node; return get_constant_text(cnode->datatype, cnode->values); diff --git a/main/tests/test_shader_lang.h b/main/tests/test_shader_lang.h index 235bf10463..e99858fdc6 100644 --- a/main/tests/test_shader_lang.h +++ b/main/tests/test_shader_lang.h @@ -31,7 +31,7 @@ #ifndef TEST_SHADER_LANG_H #define TEST_SHADER_LANG_H -#include "os/main_loop.h" +#include "core/os/main_loop.h" namespace TestShaderLang { diff --git a/main/tests/test_string.cpp b/main/tests/test_string.cpp index af948556c4..7e19c7bf3e 100644 --- a/main/tests/test_string.cpp +++ b/main/tests/test_string.cpp @@ -28,11 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "ustring.h" +#include "core/ustring.h" #include <wchar.h> -//#include "math_funcs.h" +//#include "core/math/math_funcs.h" #include "core/io/ip_address.h" -#include "os/os.h" +#include "core/os/os.h" #include <stdio.h> #include "test_string.h" @@ -480,7 +480,7 @@ bool test_28() { output = format.sprintf(args, &error); success = (output == String("fish % frog") && !error); OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; //////// INTS @@ -491,7 +491,7 @@ bool test_28() { output = format.sprintf(args, &error); success = (output == String("fish 5 frog") && !error); OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; // Int left padded with zeroes. format = "fish %05d frog"; @@ -500,7 +500,7 @@ bool test_28() { output = format.sprintf(args, &error); success = (output == String("fish 00005 frog") && !error); OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; // Int left padded with spaces. format = "fish %5d frog"; @@ -509,7 +509,7 @@ bool test_28() { output = format.sprintf(args, &error); success = (output == String("fish 5 frog") && !error); OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; // Int right padded with spaces. format = "fish %-5d frog"; @@ -518,7 +518,7 @@ bool test_28() { output = format.sprintf(args, &error); success = (output == String("fish 5 frog") && !error); OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; // Int with sign (positive). format = "fish %+d frog"; @@ -527,7 +527,7 @@ bool test_28() { output = format.sprintf(args, &error); success = (output == String("fish +5 frog") && !error); OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; // Negative int. format = "fish %d frog"; @@ -536,7 +536,7 @@ bool test_28() { output = format.sprintf(args, &error); success = (output == String("fish -5 frog") && !error); OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; // Hex (lower) format = "fish %x frog"; @@ -545,7 +545,7 @@ bool test_28() { output = format.sprintf(args, &error); success = (output == String("fish 2d frog") && !error); OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; // Hex (upper) format = "fish %X frog"; @@ -554,7 +554,7 @@ bool test_28() { output = format.sprintf(args, &error); success = (output == String("fish 2D frog") && !error); OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; // Octal format = "fish %o frog"; @@ -563,7 +563,7 @@ bool test_28() { output = format.sprintf(args, &error); success = (output == String("fish 143 frog") && !error); OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; ////// REALS @@ -574,7 +574,7 @@ bool test_28() { output = format.sprintf(args, &error); success = (output == String("fish 99.990000 frog") && !error); OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; // Real left-padded format = "fish %11f frog"; @@ -583,7 +583,7 @@ bool test_28() { output = format.sprintf(args, &error); success = (output == String("fish 99.990000 frog") && !error); OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; // Real right-padded format = "fish %-11f frog"; @@ -592,7 +592,7 @@ bool test_28() { output = format.sprintf(args, &error); success = (output == String("fish 99.990000 frog") && !error); OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; // Real given int. format = "fish %f frog"; @@ -601,7 +601,7 @@ bool test_28() { output = format.sprintf(args, &error); success = (output == String("fish 99.000000 frog") && !error); OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; // Real with sign (positive). format = "fish %+f frog"; @@ -610,7 +610,7 @@ bool test_28() { output = format.sprintf(args, &error); success = (output == String("fish +99.990000 frog") && !error); OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; // Real with 1 decimals. format = "fish %.1f frog"; @@ -619,7 +619,7 @@ bool test_28() { output = format.sprintf(args, &error); success = (output == String("fish 100.0 frog") && !error); OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; // Real with 12 decimals. format = "fish %.12f frog"; @@ -628,7 +628,7 @@ bool test_28() { output = format.sprintf(args, &error); success = (output == String("fish 99.990000000000 frog") && !error); OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; // Real with no decimals. format = "fish %.f frog"; @@ -637,7 +637,7 @@ bool test_28() { output = format.sprintf(args, &error); success = (output == String("fish 100 frog") && !error); OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; /////// Strings. @@ -648,7 +648,7 @@ bool test_28() { output = format.sprintf(args, &error); success = (output == String("fish cheese frog") && !error); OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; // String left-padded format = "fish %10s frog"; @@ -657,7 +657,7 @@ bool test_28() { output = format.sprintf(args, &error); success = (output == String("fish cheese frog") && !error); OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; // String right-padded format = "fish %-10s frog"; @@ -666,7 +666,7 @@ bool test_28() { output = format.sprintf(args, &error); success = (output == String("fish cheese frog") && !error); OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; ///// Characters @@ -677,7 +677,7 @@ bool test_28() { output = format.sprintf(args, &error); success = (output == String("fish A frog") && !error); OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; // Character as int. format = "fish %c frog"; @@ -686,7 +686,7 @@ bool test_28() { output = format.sprintf(args, &error); success = (output == String("fish A frog") && !error); OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; ///// Dynamic width @@ -698,7 +698,7 @@ bool test_28() { output = format.sprintf(args, &error); success = (output == String("fish cheese frog") && !error); OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; // Int dynamic width format = "fish %*d frog"; @@ -708,7 +708,7 @@ bool test_28() { output = format.sprintf(args, &error); success = (output == String("fish 99 frog") && !error); OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; // Float dynamic width format = "fish %*.*f frog"; @@ -719,7 +719,7 @@ bool test_28() { output = format.sprintf(args, &error); success = (output == String("fish 99.990 frog") && !error); OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; ///// Errors @@ -730,7 +730,7 @@ bool test_28() { output = format.sprintf(args, &error); success = (output == "not enough arguments for format string" && error); OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; // More arguments than formats. format = "fish %s frog"; @@ -740,7 +740,7 @@ bool test_28() { output = format.sprintf(args, &error); success = (output == "not all arguments converted during string formatting" && error); OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; // Incomplete format. format = "fish %10"; @@ -749,7 +749,7 @@ bool test_28() { output = format.sprintf(args, &error); success = (output == "incomplete format" && error); OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; // Bad character in format string format = "fish %&f frog"; @@ -758,7 +758,7 @@ bool test_28() { output = format.sprintf(args, &error); success = (output == "unsupported format character" && error); OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; // Too many decimals. format = "fish %2.2.2f frog"; @@ -767,7 +767,7 @@ bool test_28() { output = format.sprintf(args, &error); success = (output == "too many decimal points in format" && error); OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; // * not a number format = "fish %*f frog"; @@ -777,7 +777,7 @@ bool test_28() { output = format.sprintf(args, &error); success = (output == "* wants number" && error); OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; // Character too long. format = "fish %c frog"; @@ -786,7 +786,7 @@ bool test_28() { output = format.sprintf(args, &error); success = (output == "%c requires number or single-character string" && error); OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; // Character bad type. format = "fish %c frog"; @@ -795,7 +795,7 @@ bool test_28() { output = format.sprintf(args, &error); success = (output == "%c requires number or single-character string" && error); OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; return state; } @@ -819,37 +819,127 @@ bool test_29() { String ip4 = "192.168.0.1"; bool success = ip4.is_valid_ip_address(); OS::get_singleton()->print("Is valid ipv4: %ls, %s\n", ip4.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; ip4 = "192.368.0.1"; success = (!ip4.is_valid_ip_address()); OS::get_singleton()->print("Is invalid ipv4: %ls, %s\n", ip4.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; String ip6 = "2001:0db8:85a3:0000:0000:8a2e:0370:7334"; success = ip6.is_valid_ip_address(); OS::get_singleton()->print("Is valid ipv6: %ls, %s\n", ip6.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; ip6 = "2001:0db8:85j3:0000:0000:8a2e:0370:7334"; success = (!ip6.is_valid_ip_address()); OS::get_singleton()->print("Is invalid ipv6: %ls, %s\n", ip6.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; ip6 = "2001:0db8:85f345:0000:0000:8a2e:0370:7334"; success = (!ip6.is_valid_ip_address()); OS::get_singleton()->print("Is invalid ipv6: %ls, %s\n", ip6.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; ip6 = "2001:0db8::0:8a2e:370:7334"; success = (ip6.is_valid_ip_address()); OS::get_singleton()->print("Is valid ipv6: %ls, %s\n", ip6.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; ip6 = "::ffff:192.168.0.1"; success = (ip6.is_valid_ip_address()); OS::get_singleton()->print("Is valid ipv6: %ls, %s\n", ip6.c_str(), success ? "OK" : "FAIL"); - if (!success) state = false; + state = state && success; + + return state; +}; + +bool test_30() { + bool state = true; + bool success = true; + String input = "bytes2var"; + String output = "Bytes 2 Var"; + success = (input.capitalize() == output); + state = state && success; + OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL"); + + input = "linear2db"; + output = "Linear 2 Db"; + success = (input.capitalize() == output); + state = state && success; + OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL"); + + input = "vector3"; + output = "Vector 3"; + success = (input.capitalize() == output); + state = state && success; + OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL"); + + input = "sha256"; + output = "Sha 256"; + success = (input.capitalize() == output); + state = state && success; + OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL"); + + input = "2db"; + output = "2 Db"; + success = (input.capitalize() == output); + state = state && success; + OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL"); + + input = "PascalCase"; + output = "Pascal Case"; + success = (input.capitalize() == output); + state = state && success; + OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL"); + + input = "PascalPascalCase"; + output = "Pascal Pascal Case"; + success = (input.capitalize() == output); + state = state && success; + OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL"); + + input = "snake_case"; + output = "Snake Case"; + success = (input.capitalize() == output); + state = state && success; + OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL"); + + input = "snake_snake_case"; + output = "Snake Snake Case"; + success = (input.capitalize() == output); + state = state && success; + OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL"); + + input = "sha256sum"; + output = "Sha 256 Sum"; + success = (input.capitalize() == output); + state = state && success; + OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL"); + + input = "cat2dog"; + output = "Cat 2 Dog"; + success = (input.capitalize() == output); + state = state && success; + OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL"); + + input = "function(name)"; + output = "Function(name)"; + success = (input.capitalize() == output); + state = state && success; + OS::get_singleton()->print("Capitalize %ls (existing incorrect behavior): %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL"); + + input = "snake_case_function(snake_case_arg)"; + output = "Snake Case Function(snake Case Arg)"; + success = (input.capitalize() == output); + state = state && success; + OS::get_singleton()->print("Capitalize %ls (existing incorrect behavior): %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL"); + + input = "snake_case_function( snake_case_arg )"; + output = "Snake Case Function( Snake Case Arg )"; + success = (input.capitalize() == output); + state = state && success; + OS::get_singleton()->print("Capitalize %ls: %ls, %s\n", input.c_str(), output.c_str(), success ? "OK" : "FAIL"); return state; }; @@ -887,6 +977,7 @@ TestFunc test_funcs[] = { test_27, test_28, test_29, + test_30, 0 }; diff --git a/main/tests/test_string.h b/main/tests/test_string.h index 110e115309..e293e96604 100644 --- a/main/tests/test_string.h +++ b/main/tests/test_string.h @@ -31,8 +31,8 @@ #ifndef TEST_STRING_H #define TEST_STRING_H -#include "os/main_loop.h" -#include "ustring.h" +#include "core/os/main_loop.h" +#include "core/ustring.h" namespace TestString { diff --git a/main/timer_sync.cpp b/main/timer_sync.cpp index 9f4f6ed271..275465914e 100644 --- a/main/timer_sync.cpp +++ b/main/timer_sync.cpp @@ -83,7 +83,7 @@ MainFrameTime MainTimerSync::advance_core(float p_frame_slice, int p_iterations_ int min_typical_steps = typical_physics_steps[0]; int max_typical_steps = min_typical_steps + 1; - // given the past recorded steps and typcial steps to match, calculate bounds for this + // given the past recorded steps and typical steps to match, calculate bounds for this // step to be typical bool update_typical = false; diff --git a/methods.py b/methods.py index e9450d95e2..111a2347be 100644 --- a/methods.py +++ b/methods.py @@ -13,12 +13,24 @@ def add_source_files(self, sources, filetype, lib_env=None, shared=False): if isbasestring(filetype): dir_path = self.Dir('.').abspath - filetype = glob.glob(dir_path + "/" + filetype) + filetype = sorted(glob.glob(dir_path + "/" + filetype)) for path in filetype: sources.append(self.Object(path)) +def disable_warnings(self): + # 'self' is the environment + if self.msvc: + # We have to remove existing warning level defines before appending /w, + # otherwise we get: "warning D9025 : overriding '/W3' with '/w'" + warn_flags = ['/Wall', '/W4', '/W3', '/W2', '/W1', '/WX'] + self['CCFLAGS'] = [x for x in self['CCFLAGS'] if not x in warn_flags] + self.Append(CCFLAGS=['/w']) + else: + self.Append(CCFLAGS=['-w']) + + def add_module_version_string(self,s): self.module_version_string += "." + s @@ -43,7 +55,7 @@ def update_version(module_version_string=""): 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") - f.write("#define VERSION_YEAR " + str(datetime.datetime.now().year) + "\n") + f.write("#define VERSION_YEAR " + str(2018) + "\n") f.close() # NOTE: It is safe to generate this file here, since this is still executed serially @@ -318,7 +330,7 @@ def split_lib(self, libname, src_list = None, env_lib = None): list = [] lib_list = [] - if src_list == None: + if src_list is None: src_list = getattr(env, libname + "_sources") if type(env_lib) == type(None): @@ -578,6 +590,9 @@ 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 + if not env.get('MSVS'): + env['MSVS']['PROJECTSUFFIX'] = '.vcxproj' + env['MSVS']['SOLUTIONSUFFIX'] = '.sln' env.MSVSProject( target=['#godot' + env['MSVSPROJECTSUFFIX']], incs=env.vs_incs, diff --git a/misc/dist/linux/org.godotengine.Godot.appdata.xml b/misc/dist/linux/org.godotengine.Godot.appdata.xml index 9f3f9e34af..c11317cd35 100644 --- a/misc/dist/linux/org.godotengine.Godot.appdata.xml +++ b/misc/dist/linux/org.godotengine.Godot.appdata.xml @@ -27,8 +27,8 @@ </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="faq">https://docs.godotengine.org/en/latest/about/faq.html</url> + <url type="help">https://docs.godotengine.org</url> <url type="donation">https://godotengine.org/donate</url> <url type="translate">https://hosted.weblate.org/projects/godot-engine/godot</url> <developer_name>The Godot Engine Community</developer_name> diff --git a/misc/hooks/pre-commit-clang-format b/misc/hooks/pre-commit-clang-format index 016a43038b..db241ad172 100755 --- a/misc/hooks/pre-commit-clang-format +++ b/misc/hooks/pre-commit-clang-format @@ -31,7 +31,7 @@ PARSE_EXTS=true # File types to parse. Only effective when PARSE_EXTS is true. # FILE_EXTS=".c .h .cpp .hpp" -FILE_EXTS=".c .h .cpp .hpp .cc .hh .cxx .m .mm .inc *.java" +FILE_EXTS=".c .h .cpp .hpp .cc .hh .cxx .m .mm .inc .java .glsl" # Use pygmentize instead of cat to parse diff with highlighting. # Install it with `pip install pygments` (Linux) or `easy_install Pygments` (Mac) diff --git a/misc/travis/android-tools-linux.sh b/misc/travis/android-tools-linux.sh index 04fb2eee21..830820b10b 100755 --- a/misc/travis/android-tools-linux.sh +++ b/misc/travis/android-tools-linux.sh @@ -17,19 +17,19 @@ cd $GODOT_BUILD_TOOLS_PATH ANDROID_BASE_URL=http://dl.google.com/android/repository -ANDROID_SDK_RELEASE=3859397 +ANDROID_SDK_RELEASE=4333796 ANDROID_SDK_DIR=android-sdk ANDROID_SDK_FILENAME=sdk-tools-linux-$ANDROID_SDK_RELEASE.zip ANDROID_SDK_URL=$ANDROID_BASE_URL/$ANDROID_SDK_FILENAME ANDROID_SDK_PATH=$GODOT_BUILD_TOOLS_PATH/$ANDROID_SDK_DIR -ANDROID_SDK_SHA256=444e22ce8ca0f67353bda4b85175ed3731cae3ffa695ca18119cbacef1c1bea0 +ANDROID_SDK_SHA256=92ffee5a1d98d856634e8b71132e8a95d96c83a63fde1099be3d86df3106def9 -ANDROID_NDK_RELEASE=r15c +ANDROID_NDK_RELEASE=r18 ANDROID_NDK_DIR=android-ndk ANDROID_NDK_FILENAME=android-ndk-$ANDROID_NDK_RELEASE-linux-x86_64.zip ANDROID_NDK_URL=$ANDROID_BASE_URL/$ANDROID_NDK_FILENAME ANDROID_NDK_PATH=$GODOT_BUILD_TOOLS_PATH/$ANDROID_NDK_DIR -ANDROID_NDK_SHA1=0bf02d4e8b85fd770fd7b9b2cdec57f9441f27a2 +ANDROID_NDK_SHA1=2ac2e8e1ef73ed551cac3a1479bb28bd49369212 echo echo "Download and install Android development tools ..." @@ -75,7 +75,7 @@ echo "Installing: Android Tools ..." yes | $ANDROID_SDK_DIR/tools/bin/sdkmanager --licenses > /dev/null $ANDROID_SDK_DIR/tools/bin/sdkmanager 'tools' > /dev/null $ANDROID_SDK_DIR/tools/bin/sdkmanager 'platform-tools' > /dev/null -$ANDROID_SDK_DIR/tools/bin/sdkmanager 'build-tools;26.0.2' > /dev/null +$ANDROID_SDK_DIR/tools/bin/sdkmanager 'build-tools;28.0.1' > /dev/null echo EXPORT_VAL="export ANDROID_HOME=$ANDROID_SDK_PATH" diff --git a/misc/travis/clang-format.sh b/misc/travis/clang-format.sh index 5463998720..ee4d0b2a10 100755 --- a/misc/travis/clang-format.sh +++ b/misc/travis/clang-format.sh @@ -11,7 +11,7 @@ else RANGE=HEAD fi -FILES=$(git diff-tree --no-commit-id --name-only -r $RANGE | grep -v thirdparty/ | grep -E "\.(c|h|cpp|hpp|cc|hh|cxx|m|mm|inc|java)$") +FILES=$(git diff-tree --no-commit-id --name-only -r $RANGE | grep -v thirdparty/ | grep -E "\.(c|h|cpp|hpp|cc|hh|cxx|m|mm|inc|java|glsl)$") echo "Checking files:\n$FILES" # create a random filename to store our generated patch diff --git a/modules/SCsub b/modules/SCsub index 74a5267355..67f5893db4 100644 --- a/modules/SCsub +++ b/modules/SCsub @@ -9,7 +9,6 @@ Export('env_modules') env.modules_sources = [ "register_module_types.gen.cpp", ] -Export('env') for x in env.module_list: if (x in env.disabled_modules): @@ -20,7 +19,6 @@ for x in env.module_list: if env.split_modules: env.split_lib("modules", env_lib = env_modules) else: - lib = env_modules.add_library("modules", env.modules_sources) env.Prepend(LIBS=[lib]) diff --git a/modules/bmp/image_loader_bmp.cpp b/modules/bmp/image_loader_bmp.cpp index 919731b52b..063508a25f 100644 --- a/modules/bmp/image_loader_bmp.cpp +++ b/modules/bmp/image_loader_bmp.cpp @@ -42,12 +42,9 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image, if (err == OK) { size_t index = 0; - size_t width = - static_cast<size_t>(p_header.bmp_info_header.bmp_width < 0 ? -p_header.bmp_info_header.bmp_width : p_header.bmp_info_header.bmp_width); - size_t height = - static_cast<size_t>(p_header.bmp_info_header.bmp_height < 0 ? -p_header.bmp_info_header.bmp_height : p_header.bmp_info_header.bmp_height); - size_t bits_per_pixel = - static_cast<size_t>(p_header.bmp_info_header.bmp_bit_count); + size_t width = (size_t)p_header.bmp_info_header.bmp_width; + size_t height = (size_t)p_header.bmp_info_header.bmp_height; + size_t bits_per_pixel = (size_t)p_header.bmp_info_header.bmp_bit_count; if (p_header.bmp_info_header.bmp_compression != 0) { err = FAILED; diff --git a/modules/bmp/image_loader_bmp.h b/modules/bmp/image_loader_bmp.h index 3fa7481287..e0e50859fc 100644 --- a/modules/bmp/image_loader_bmp.h +++ b/modules/bmp/image_loader_bmp.h @@ -31,7 +31,7 @@ #ifndef IMAGE_LOADER_BMP_H #define IMAGE_LOADER_BMP_H -#include "io/image_loader.h" +#include "core/io/image_loader.h" class ImageLoaderBMP : public ImageFormatLoader { protected: diff --git a/modules/bullet/SCsub b/modules/bullet/SCsub index d8d0b930a5..11ce18449b 100644 --- a/modules/bullet/SCsub +++ b/modules/bullet/SCsub @@ -68,11 +68,13 @@ if env['builtin_bullet']: , "BulletCollision/CollisionShapes/btEmptyShape.cpp" , "BulletCollision/CollisionShapes/btHeightfieldTerrainShape.cpp" , "BulletCollision/CollisionShapes/btMinkowskiSumShape.cpp" + , "BulletCollision/CollisionShapes/btMiniSDF.cpp" , "BulletCollision/CollisionShapes/btMultimaterialTriangleMeshShape.cpp" , "BulletCollision/CollisionShapes/btMultiSphereShape.cpp" , "BulletCollision/CollisionShapes/btOptimizedBvh.cpp" , "BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp" , "BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.cpp" + , "BulletCollision/CollisionShapes/btSdfCollisionShape.cpp" , "BulletCollision/CollisionShapes/btShapeHull.cpp" , "BulletCollision/CollisionShapes/btSphereShape.cpp" , "BulletCollision/CollisionShapes/btStaticPlaneShape.cpp" @@ -184,8 +186,12 @@ if env['builtin_bullet']: thirdparty_sources = [thirdparty_dir + file for file in bullet2_src] - env_bullet.add_source_files(env.modules_sources, thirdparty_sources) env_bullet.Append(CPPPATH=[thirdparty_dir]) + env_thirdparty = env_bullet.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + + # Godot source files env_bullet.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/bullet/area_bullet.cpp b/modules/bullet/area_bullet.cpp index 3668088590..a0486443c2 100644 --- a/modules/bullet/area_bullet.cpp +++ b/modules/bullet/area_bullet.cpp @@ -30,6 +30,7 @@ #include "area_bullet.h" +#include "bullet_physics_server.h" #include "bullet_types_converter.h" #include "bullet_utilities.h" #include "collision_object_bullet.h" @@ -45,7 +46,6 @@ AreaBullet::AreaBullet() : RigidCollisionObjectBullet(CollisionObjectBullet::TYPE_AREA), monitorable(true), - isScratched(false), spOv_mode(PhysicsServer::AREA_SPACE_OVERRIDE_DISABLED), spOv_gravityPoint(false), spOv_gravityPointDistanceScale(0), @@ -54,10 +54,11 @@ AreaBullet::AreaBullet() : spOv_gravityMag(10), spOv_linearDump(0.1), spOv_angularDump(1), - spOv_priority(0) { + spOv_priority(0), + isScratched(false) { btGhost = bulletnew(btGhostObject); - btGhost->setCollisionShape(compoundShape); + reload_shapes(); setupBulletCollisionObject(btGhost); /// Collision objects with a callback still have collision response with dynamic rigid bodies. /// In order to use collision objects as trigger, you have to disable the collision response. @@ -93,6 +94,9 @@ void AreaBullet::dispatch_callbacks() { otherObj.object->on_exit_area(this); overlappingObjects.remove(i); // Remove after callback break; + case OVERLAP_STATE_DIRTY: + case OVERLAP_STATE_INSIDE: + break; } } } @@ -162,6 +166,11 @@ bool AreaBullet::is_monitoring() const { return get_godot_object_flags() & GOF_IS_MONITORING_AREA; } +void AreaBullet::main_shape_changed() { + CRASH_COND(!get_main_shape()) + btGhost->setCollisionShape(get_main_shape()); +} + void AreaBullet::reload_body() { if (space) { space->remove_area(this); diff --git a/modules/bullet/area_bullet.h b/modules/bullet/area_bullet.h index b2046c684e..1c5962cfe3 100644 --- a/modules/bullet/area_bullet.h +++ b/modules/bullet/area_bullet.h @@ -142,6 +142,7 @@ public: _FORCE_INLINE_ void set_spOv_priority(int p_priority) { spOv_priority = p_priority; } _FORCE_INLINE_ int get_spOv_priority() { return spOv_priority; } + virtual void main_shape_changed(); virtual void reload_body(); virtual void set_space(SpaceBullet *p_space); @@ -156,6 +157,7 @@ public: virtual void on_collision_filters_change(); virtual void on_collision_checker_start() {} + virtual void on_collision_checker_end() { isTransformChanged = false; } void add_overlap(CollisionObjectBullet *p_otherObject); void put_overlap_as_exit(int p_index); diff --git a/modules/bullet/btRayShape.cpp b/modules/bullet/btRayShape.cpp index 8707096038..6cc63d79ce 100644 --- a/modules/bullet/btRayShape.cpp +++ b/modules/bullet/btRayShape.cpp @@ -30,7 +30,7 @@ #include "btRayShape.h" -#include "math/math_funcs.h" +#include "core/math/math_funcs.h" #include <LinearMath/btAabbUtil2.h> diff --git a/modules/bullet/bullet_physics_server.cpp b/modules/bullet/bullet_physics_server.cpp index dbd27a3564..315afe3d72 100644 --- a/modules/bullet/bullet_physics_server.cpp +++ b/modules/bullet/bullet_physics_server.cpp @@ -31,8 +31,8 @@ #include "bullet_physics_server.h" #include "bullet_utilities.h" -#include "class_db.h" #include "cone_twist_joint_bullet.h" +#include "core/class_db.h" #include "core/error_macros.h" #include "core/ustring.h" #include "generic_6dof_joint_bullet.h" @@ -74,12 +74,6 @@ body->get_space()->add_constraint(joint, joint->is_disabled_collisions_between_bodies()); // <--------------- Joint creation asserts -btEmptyShape *BulletPhysicsServer::emptyShape(ShapeBullet::create_shape_empty()); - -btEmptyShape *BulletPhysicsServer::get_empty_shape() { - return emptyShape; -} - void BulletPhysicsServer::_bind_methods() { //ClassDB::bind_method(D_METHOD("DoTest"), &BulletPhysicsServer::DoTest); } @@ -89,9 +83,7 @@ BulletPhysicsServer::BulletPhysicsServer() : active(true), active_spaces_count(0) {} -BulletPhysicsServer::~BulletPhysicsServer() { - bulletdelete(emptyShape); -} +BulletPhysicsServer::~BulletPhysicsServer() {} RID BulletPhysicsServer::shape_create(ShapeType p_shape) { ShapeBullet *shape = NULL; @@ -338,7 +330,7 @@ Transform BulletPhysicsServer::area_get_shape_transform(RID p_area, int p_shape_ void BulletPhysicsServer::area_remove_shape(RID p_area, int p_shape_idx) { AreaBullet *area = area_owner.get(p_area); ERR_FAIL_COND(!area); - return area->remove_shape(p_shape_idx); + return area->remove_shape_full(p_shape_idx); } void BulletPhysicsServer::area_clear_shapes(RID p_area) { @@ -346,7 +338,7 @@ void BulletPhysicsServer::area_clear_shapes(RID p_area) { ERR_FAIL_COND(!area); for (int i = area->get_shape_count(); 0 < i; --i) - area->remove_shape(0); + area->remove_shape_full(0); } void BulletPhysicsServer::area_set_shape_disabled(RID p_area, int p_shape_idx, bool p_disabled) { @@ -567,7 +559,7 @@ void BulletPhysicsServer::body_remove_shape(RID p_body, int p_shape_idx) { RigidBodyBullet *body = rigid_body_owner.get(p_body); ERR_FAIL_COND(!body); - body->remove_shape(p_shape_idx); + body->remove_shape_full(p_shape_idx); } void BulletPhysicsServer::body_clear_shapes(RID p_body) { @@ -1486,7 +1478,7 @@ void BulletPhysicsServer::free(RID p_rid) { // Notify the shape is configured for (Map<ShapeOwnerBullet *, int>::Element *element = shape->get_owners().front(); element; element = element->next()) { - static_cast<ShapeOwnerBullet *>(element->key())->remove_shape(shape); + static_cast<ShapeOwnerBullet *>(element->key())->remove_shape_full(shape); } shape_owner.free(p_rid); @@ -1497,7 +1489,7 @@ void BulletPhysicsServer::free(RID p_rid) { body->set_space(NULL); - body->remove_all_shapes(true); + body->remove_all_shapes(true, true); rigid_body_owner.free(p_rid); bulletdelete(body); @@ -1517,7 +1509,7 @@ void BulletPhysicsServer::free(RID p_rid) { area->set_space(NULL); - area->remove_all_shapes(true); + area->remove_all_shapes(true, true); area_owner.free(p_rid); bulletdelete(area); diff --git a/modules/bullet/bullet_physics_server.h b/modules/bullet/bullet_physics_server.h index e9c568d605..6b7dcd86e6 100644 --- a/modules/bullet/bullet_physics_server.h +++ b/modules/bullet/bullet_physics_server.h @@ -32,8 +32,8 @@ #define BULLET_PHYSICS_SERVER_H #include "area_bullet.h" +#include "core/rid.h" #include "joint_bullet.h" -#include "rid.h" #include "rigid_body_bullet.h" #include "servers/physics_server.h" #include "shape_bullet.h" @@ -60,13 +60,6 @@ class BulletPhysicsServer : public PhysicsServer { mutable RID_Owner<SoftBodyBullet> soft_body_owner; mutable RID_Owner<JointBullet> joint_owner; -private: - /// This is used when a collision shape is not active, so the bullet compound shapes index are always sync with godot index - static btEmptyShape *emptyShape; - -public: - static btEmptyShape *get_empty_shape(); - protected: static void _bind_methods(); diff --git a/modules/bullet/bullet_types_converter.cpp b/modules/bullet/bullet_types_converter.cpp index a0fe598227..f9b7126173 100644 --- a/modules/bullet/bullet_types_converter.cpp +++ b/modules/bullet/bullet_types_converter.cpp @@ -28,8 +28,6 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#pragma once - #include "bullet_types_converter.h" /** diff --git a/modules/bullet/bullet_utilities.h b/modules/bullet/bullet_utilities.h index 2841dfbe69..553c1d0384 100644 --- a/modules/bullet/bullet_utilities.h +++ b/modules/bullet/bullet_utilities.h @@ -35,13 +35,12 @@ @author AndreaCatania */ -#pragma once - #define bulletnew(cl) \ new cl #define bulletdelete(cl) \ - delete cl; \ - cl = NULL; - + { \ + delete cl; \ + cl = NULL; \ + } #endif diff --git a/modules/bullet/collision_object_bullet.cpp b/modules/bullet/collision_object_bullet.cpp index 271cdb0223..402a276f95 100644 --- a/modules/bullet/collision_object_bullet.cpp +++ b/modules/bullet/collision_object_bullet.cpp @@ -43,8 +43,7 @@ @author AndreaCatania */ -#define enableDynamicAabbTree true -#define initialChildCapacity 1 +#define enableDynamicAabbTree false CollisionObjectBullet::ShapeWrapper::~ShapeWrapper() {} @@ -53,19 +52,30 @@ void CollisionObjectBullet::ShapeWrapper::set_transform(const Transform &p_trans G_TO_B(p_transform, transform); UNSCALE_BT_BASIS(transform); } + void CollisionObjectBullet::ShapeWrapper::set_transform(const btTransform &p_transform) { transform = p_transform; } +void CollisionObjectBullet::ShapeWrapper::claim_bt_shape(const btVector3 &body_scale) { + if (!bt_shape) { + if (active) + bt_shape = shape->create_bt_shape(scale * body_scale); + else + bt_shape = ShapeBullet::create_shape_empty(); + } +} + CollisionObjectBullet::CollisionObjectBullet(Type p_type) : RIDBullet(), - space(NULL), type(p_type), collisionsEnabled(true), m_isStatic(false), bt_collision_object(NULL), body_scale(1., 1., 1.), - force_shape_reset(false) {} + force_shape_reset(false), + space(NULL), + isTransformChanged(false) {} CollisionObjectBullet::~CollisionObjectBullet() { // Remove all overlapping, notify is not required since godot take care of it @@ -83,7 +93,7 @@ bool equal(real_t first, real_t second) { void CollisionObjectBullet::set_body_scale(const Vector3 &p_new_scale) { if (!equal(p_new_scale[0], body_scale[0]) || !equal(p_new_scale[1], body_scale[1]) || !equal(p_new_scale[2], body_scale[2])) { body_scale = p_new_scale; - on_body_scale_changed(); + body_scale_changed(); } } @@ -93,7 +103,7 @@ btVector3 CollisionObjectBullet::get_bt_body_scale() const { return s; } -void CollisionObjectBullet::on_body_scale_changed() { +void CollisionObjectBullet::body_scale_changed() { force_shape_reset = true; } @@ -107,6 +117,7 @@ void CollisionObjectBullet::setupBulletCollisionObject(btCollisionObject *p_coll bt_collision_object->setUserIndex(type); // Force the enabling of collision and avoid problems set_collision_enabled(collisionsEnabled); + p_collisionObject->setCollisionFlags(p_collisionObject->getCollisionFlags() | btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK); } void CollisionObjectBullet::add_collision_exception(const CollisionObjectBullet *p_ignoreCollisionObject) { @@ -178,48 +189,33 @@ Transform CollisionObjectBullet::get_transform() const { void CollisionObjectBullet::set_transform__bullet(const btTransform &p_global_transform) { bt_collision_object->setWorldTransform(p_global_transform); + notify_transform_changed(); } const btTransform &CollisionObjectBullet::get_transform__bullet() const { return bt_collision_object->getWorldTransform(); } +void CollisionObjectBullet::notify_transform_changed() { + isTransformChanged = true; +} + RigidCollisionObjectBullet::RigidCollisionObjectBullet(Type p_type) : CollisionObjectBullet(p_type), - compoundShape(bulletnew(btCompoundShape(enableDynamicAabbTree, initialChildCapacity))) { + mainShape(NULL) { } RigidCollisionObjectBullet::~RigidCollisionObjectBullet() { - remove_all_shapes(true); - bt_collision_object->setCollisionShape(NULL); - bulletdelete(compoundShape); -} - -/* Not used -void RigidCollisionObjectBullet::_internal_replaceShape(btCollisionShape *p_old_shape, btCollisionShape *p_new_shape) { - bool at_least_one_was_changed = false; - btTransform old_transf; - // Inverse because I need remove the shapes - // Fetch all shapes to be sure to remove all shapes - for (int i = compoundShape->getNumChildShapes() - 1; 0 <= i; --i) { - if (compoundShape->getChildShape(i) == p_old_shape) { - - old_transf = compoundShape->getChildTransform(i); - compoundShape->removeChildShapeByIndex(i); - compoundShape->addChildShape(old_transf, p_new_shape); - at_least_one_was_changed = true; - } - } - - if (at_least_one_was_changed) { - on_shapes_changed(); + remove_all_shapes(true, true); + if (mainShape && mainShape->isCompound()) { + bulletdelete(mainShape); } -}*/ +} void RigidCollisionObjectBullet::add_shape(ShapeBullet *p_shape, const Transform &p_transform) { shapes.push_back(ShapeWrapper(p_shape, p_transform, true)); p_shape->add_owner(this); - on_shapes_changed(); + reload_shapes(); } void RigidCollisionObjectBullet::set_shape(int p_index, ShapeBullet *p_shape) { @@ -227,17 +223,31 @@ void RigidCollisionObjectBullet::set_shape(int p_index, ShapeBullet *p_shape) { shp.shape->remove_owner(this); p_shape->add_owner(this); shp.shape = p_shape; - on_shapes_changed(); + reload_shapes(); } -void RigidCollisionObjectBullet::set_shape_transform(int p_index, const Transform &p_transform) { - ERR_FAIL_INDEX(p_index, get_shape_count()); +int RigidCollisionObjectBullet::get_shape_count() const { + return shapes.size(); +} - shapes.write[p_index].set_transform(p_transform); - on_shape_changed(shapes.write[p_index].shape); +ShapeBullet *RigidCollisionObjectBullet::get_shape(int p_index) const { + return shapes[p_index].shape; +} + +btCollisionShape *RigidCollisionObjectBullet::get_bt_shape(int p_index) const { + return shapes[p_index].bt_shape; } -void RigidCollisionObjectBullet::remove_shape(ShapeBullet *p_shape) { +int RigidCollisionObjectBullet::find_shape(ShapeBullet *p_shape) const { + const int size = shapes.size(); + for (int i = 0; i < size; ++i) { + if (shapes[i].shape == p_shape) + return i; + } + return -1; +} + +void RigidCollisionObjectBullet::remove_shape_full(ShapeBullet *p_shape) { // Remove the shape, all the times it appears // Reverse order required for delete. for (int i = shapes.size() - 1; 0 <= i; --i) { @@ -246,35 +256,36 @@ void RigidCollisionObjectBullet::remove_shape(ShapeBullet *p_shape) { shapes.remove(i); } } - on_shapes_changed(); + reload_shapes(); } -void RigidCollisionObjectBullet::remove_shape(int p_index) { +void RigidCollisionObjectBullet::remove_shape_full(int p_index) { ERR_FAIL_INDEX(p_index, get_shape_count()); internal_shape_destroy(p_index); shapes.remove(p_index); - on_shapes_changed(); + reload_shapes(); } -void RigidCollisionObjectBullet::remove_all_shapes(bool p_permanentlyFromThisBody) { +void RigidCollisionObjectBullet::remove_all_shapes(bool p_permanentlyFromThisBody, bool p_force_not_reload) { // Reverse order required for delete. for (int i = shapes.size() - 1; 0 <= i; --i) { internal_shape_destroy(i, p_permanentlyFromThisBody); } shapes.clear(); - on_shapes_changed(); + if (!p_force_not_reload) + reload_shapes(); } -int RigidCollisionObjectBullet::get_shape_count() const { - return shapes.size(); -} +void RigidCollisionObjectBullet::set_shape_transform(int p_index, const Transform &p_transform) { + ERR_FAIL_INDEX(p_index, get_shape_count()); -ShapeBullet *RigidCollisionObjectBullet::get_shape(int p_index) const { - return shapes[p_index].shape; + shapes.write[p_index].set_transform(p_transform); + // Note, enableDynamicAabbTree is false because on transform change compound is destroyed + reload_shapes(); } -btCollisionShape *RigidCollisionObjectBullet::get_bt_shape(int p_index) const { - return shapes[p_index].bt_shape; +const btTransform &RigidCollisionObjectBullet::get_bt_shape_transform(int p_index) const { + return shapes[p_index].transform; } Transform RigidCollisionObjectBullet::get_shape_transform(int p_index) const { @@ -283,68 +294,73 @@ Transform RigidCollisionObjectBullet::get_shape_transform(int p_index) const { return trs; } -void RigidCollisionObjectBullet::on_shape_changed(const ShapeBullet *const p_shape) { - const int size = shapes.size(); - for (int i = 0; i < size; ++i) { - if (shapes[i].shape == p_shape) { - bulletdelete(shapes.write[i].bt_shape); - } - } - on_shapes_changed(); +void RigidCollisionObjectBullet::set_shape_disabled(int p_index, bool p_disabled) { + shapes.write[p_index].active = !p_disabled; + shape_changed(p_index); +} + +bool RigidCollisionObjectBullet::is_shape_disabled(int p_index) { + return !shapes[p_index].active; +} + +void RigidCollisionObjectBullet::shape_changed(int p_shape_index) { + bulletdelete(shapes.write[p_shape_index].bt_shape); + reload_shapes(); } -void RigidCollisionObjectBullet::on_shapes_changed() { - int i; +void RigidCollisionObjectBullet::reload_shapes() { - // Remove all shapes, reverse order for performance reason (Array resize) - for (i = compoundShape->getNumChildShapes() - 1; 0 <= i; --i) { - compoundShape->removeChildShapeByIndex(i); + if (mainShape && mainShape->isCompound()) { + // Destroy compound + bulletdelete(mainShape); } + mainShape = NULL; + ShapeWrapper *shpWrapper; - const int shapes_size = shapes.size(); + const int shape_count = shapes.size(); // Reset shape if required if (force_shape_reset) { - for (i = 0; i < shapes_size; ++i) { + for (int i(0); i < shape_count; ++i) { shpWrapper = &shapes.write[i]; bulletdelete(shpWrapper->bt_shape); } force_shape_reset = false; } - // Insert all shapes - btVector3 body_scale(get_bt_body_scale()); - for (i = 0; i < shapes_size; ++i) { - shpWrapper = &shapes.write[i]; - if (shpWrapper->active) { - if (!shpWrapper->bt_shape) { - shpWrapper->bt_shape = shpWrapper->shape->create_bt_shape(shpWrapper->scale * body_scale); - } - - btTransform scaled_shape_transform(shpWrapper->transform); - scaled_shape_transform.getOrigin() *= body_scale; - compoundShape->addChildShape(scaled_shape_transform, shpWrapper->bt_shape); - } else { - compoundShape->addChildShape(btTransform(), BulletPhysicsServer::get_empty_shape()); + const btVector3 body_scale(get_bt_body_scale()); + + // Try to optimize by not using compound + if (1 == shape_count) { + shpWrapper = &shapes.write[0]; + if (shpWrapper->transform.getOrigin().isZero() && shpWrapper->transform.getBasis() == shpWrapper->transform.getBasis().getIdentity()) { + shpWrapper->claim_bt_shape(body_scale); + mainShape = shpWrapper->bt_shape; + main_shape_changed(); + return; } } - compoundShape->recalculateLocalAabb(); -} + // Optimization not possible use a compound shape + btCompoundShape *compoundShape = bulletnew(btCompoundShape(enableDynamicAabbTree, shape_count)); -void RigidCollisionObjectBullet::set_shape_disabled(int p_index, bool p_disabled) { - shapes.write[p_index].active = !p_disabled; - on_shapes_changed(); -} + for (int i(0); i < shape_count; ++i) { + shpWrapper = &shapes.write[i]; + shpWrapper->claim_bt_shape(body_scale); + btTransform scaled_shape_transform(shpWrapper->transform); + scaled_shape_transform.getOrigin() *= body_scale; + compoundShape->addChildShape(scaled_shape_transform, shpWrapper->bt_shape); + } -bool RigidCollisionObjectBullet::is_shape_disabled(int p_index) { - return !shapes[p_index].active; + compoundShape->recalculateLocalAabb(); + mainShape = compoundShape; + main_shape_changed(); } -void RigidCollisionObjectBullet::on_body_scale_changed() { - CollisionObjectBullet::on_body_scale_changed(); - on_shapes_changed(); +void RigidCollisionObjectBullet::body_scale_changed() { + CollisionObjectBullet::body_scale_changed(); + reload_shapes(); } void RigidCollisionObjectBullet::internal_shape_destroy(int p_index, bool p_permanentlyFromThisBody) { diff --git a/modules/bullet/collision_object_bullet.h b/modules/bullet/collision_object_bullet.h index 506976eabf..4a0c805ce5 100644 --- a/modules/bullet/collision_object_bullet.h +++ b/modules/bullet/collision_object_bullet.h @@ -31,11 +31,11 @@ #ifndef COLLISION_OBJECT_BULLET_H #define COLLISION_OBJECT_BULLET_H +#include "core/math/transform.h" +#include "core/math/vector3.h" +#include "core/object.h" #include "core/vset.h" -#include "object.h" #include "shape_owner_bullet.h" -#include "transform.h" -#include "vector3.h" #include <LinearMath/btTransform.h> @@ -109,6 +109,8 @@ public: void set_transform(const Transform &p_transform); void set_transform(const btTransform &p_transform); + + void claim_bt_shape(const btVector3 &body_scale); }; protected: @@ -130,6 +132,7 @@ protected: /// New area is added when overlap with new area (AreaBullet::addOverlap), then is removed when it exit (CollisionObjectBullet::onExitArea) /// This array is used mainly to know which area hold the pointer of this object Vector<AreaBullet *> areasOverlapped; + bool isTransformChanged; public: CollisionObjectBullet(Type p_type); @@ -155,7 +158,7 @@ public: void set_body_scale(const Vector3 &p_new_scale); const Vector3 &get_body_scale() const { return body_scale; } btVector3 get_bt_body_scale() const; - virtual void on_body_scale_changed(); + virtual void body_scale_changed(); void add_collision_exception(const CollisionObjectBullet *p_ignoreCollisionObject); void remove_collision_exception(const CollisionObjectBullet *p_ignoreCollisionObject); @@ -183,8 +186,9 @@ public: virtual void reload_body() = 0; virtual void set_space(SpaceBullet *p_space) = 0; _FORCE_INLINE_ SpaceBullet *get_space() const { return space; } - /// This is an event that is called when a collision checker starts + virtual void on_collision_checker_start() = 0; + virtual void on_collision_checker_end() = 0; virtual void dispatch_callbacks() = 0; @@ -195,7 +199,6 @@ public: virtual void on_enter_area(AreaBullet *p_area) = 0; virtual void on_exit_area(AreaBullet *p_area); - /// GodotObjectFlags void set_godot_object_flags(int flags); int get_godot_object_flags() const; @@ -203,14 +206,14 @@ public: Transform get_transform() const; virtual void set_transform__bullet(const btTransform &p_global_transform); virtual const btTransform &get_transform__bullet() const; + + bool is_transform_changed() const { return isTransformChanged; } + virtual void notify_transform_changed(); }; class RigidCollisionObjectBullet : public CollisionObjectBullet, public ShapeOwnerBullet { protected: - /// This is required to combine some shapes together. - /// Since Godot allow to have multiple shapes for each body with custom relative location, - /// each body will attach the shapes using this class even if there is only one shape. - btCompoundShape *compoundShape; + btCollisionShape *mainShape; Vector<ShapeWrapper> shapes; public: @@ -219,28 +222,34 @@ public: _FORCE_INLINE_ const Vector<ShapeWrapper> &get_shapes_wrappers() const { return shapes; } - /// This is used to set new shape or replace existing - //virtual void _internal_replaceShape(btCollisionShape *p_old_shape, btCollisionShape *p_new_shape) = 0; + _FORCE_INLINE_ btCollisionShape *get_main_shape() const { return mainShape; } + void add_shape(ShapeBullet *p_shape, const Transform &p_transform = Transform()); void set_shape(int p_index, ShapeBullet *p_shape); - void set_shape_transform(int p_index, const Transform &p_transform); - virtual void remove_shape(ShapeBullet *p_shape); - void remove_shape(int p_index); - void remove_all_shapes(bool p_permanentlyFromThisBody = false); - - virtual void on_shape_changed(const ShapeBullet *const p_shape); - virtual void on_shapes_changed(); - _FORCE_INLINE_ btCompoundShape *get_compound_shape() const { return compoundShape; } int get_shape_count() const; ShapeBullet *get_shape(int p_index) const; btCollisionShape *get_bt_shape(int p_index) const; + + int find_shape(ShapeBullet *p_shape) const; + + virtual void remove_shape_full(ShapeBullet *p_shape); + void remove_shape_full(int p_index); + void remove_all_shapes(bool p_permanentlyFromThisBody = false, bool p_force_not_reload = false); + + void set_shape_transform(int p_index, const Transform &p_transform); + + const btTransform &get_bt_shape_transform(int p_index) const; Transform get_shape_transform(int p_index) const; void set_shape_disabled(int p_index, bool p_disabled); bool is_shape_disabled(int p_index); - virtual void on_body_scale_changed(); + virtual void shape_changed(int p_shape_index); + virtual void reload_shapes(); + + virtual void main_shape_changed() = 0; + virtual void body_scale_changed(); private: void internal_shape_destroy(int p_index, bool p_permanentlyFromThisBody = false); diff --git a/modules/bullet/cone_twist_joint_bullet.cpp b/modules/bullet/cone_twist_joint_bullet.cpp index 6b5438c60f..ecacce0bee 100644 --- a/modules/bullet/cone_twist_joint_bullet.cpp +++ b/modules/bullet/cone_twist_joint_bullet.cpp @@ -83,7 +83,9 @@ void ConeTwistJointBullet::set_param(PhysicsServer::ConeTwistJointParam p_param, coneConstraint->setLimit(coneConstraint->getSwingSpan1(), coneConstraint->getSwingSpan2(), coneConstraint->getTwistSpan(), coneConstraint->getLimitSoftness(), coneConstraint->getBiasFactor(), p_value); break; default: - WARN_PRINT("This parameter is not supported by Bullet engine"); + ERR_EXPLAIN("This parameter " + itos(p_param) + " is deprecated"); + WARN_DEPRECATED + break; } } @@ -100,7 +102,8 @@ real_t ConeTwistJointBullet::get_param(PhysicsServer::ConeTwistJointParam p_para case PhysicsServer::CONE_TWIST_JOINT_RELAXATION: return coneConstraint->getRelaxationFactor(); default: - WARN_PRINT("This parameter is not supported by Bullet engine"); + ERR_EXPLAIN("This parameter " + itos(p_param) + " is deprecated"); + WARN_DEPRECATED; return 0; } } diff --git a/modules/bullet/generic_6dof_joint_bullet.cpp b/modules/bullet/generic_6dof_joint_bullet.cpp index 6275a0d2ed..a36f1123bc 100644 --- a/modules/bullet/generic_6dof_joint_bullet.cpp +++ b/modules/bullet/generic_6dof_joint_bullet.cpp @@ -153,7 +153,9 @@ void Generic6DOFJointBullet::set_param(Vector3::Axis p_axis, PhysicsServer::G6DO sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_maxMotorForce = p_value; break; default: - WARN_PRINT("This parameter is not supported"); + ERR_EXPLAIN("This parameter " + itos(p_param) + " is deprecated"); + WARN_DEPRECATED + break; } } @@ -181,8 +183,9 @@ real_t Generic6DOFJointBullet::get_param(Vector3::Axis p_axis, PhysicsServer::G6 case PhysicsServer::G6DOF_JOINT_ANGULAR_MOTOR_FORCE_LIMIT: return sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_maxMotorForce; default: - WARN_PRINT("This parameter is not supported"); - return 0.; + ERR_EXPLAIN("This parameter " + itos(p_param) + " is deprecated"); + WARN_DEPRECATED; + return 0; } } @@ -213,8 +216,9 @@ void Generic6DOFJointBullet::set_flag(Vector3::Axis p_axis, PhysicsServer::G6DOF sixDOFConstraint->getTranslationalLimitMotor()->m_enableMotor[p_axis] = flags[p_axis][p_flag]; break; default: - WARN_PRINT("This flag is not supported by Bullet engine"); - return; + ERR_EXPLAIN("This flag " + itos(p_flag) + " is deprecated"); + WARN_DEPRECATED + break; } } diff --git a/modules/bullet/godot_collision_configuration.cpp b/modules/bullet/godot_collision_configuration.cpp index f4bb9acbd7..919c3152d7 100644 --- a/modules/bullet/godot_collision_configuration.cpp +++ b/modules/bullet/godot_collision_configuration.cpp @@ -94,3 +94,59 @@ btCollisionAlgorithmCreateFunc *GodotCollisionConfiguration::getClosestPointsAlg return btDefaultCollisionConfiguration::getClosestPointsAlgorithmCreateFunc(proxyType0, proxyType1); } } + +GodotSoftCollisionConfiguration::GodotSoftCollisionConfiguration(const btDiscreteDynamicsWorld *world, const btDefaultCollisionConstructionInfo &constructionInfo) : + btSoftBodyRigidBodyCollisionConfiguration(constructionInfo) { + + void *mem = NULL; + + mem = btAlignedAlloc(sizeof(GodotRayWorldAlgorithm::CreateFunc), 16); + m_rayWorldCF = new (mem) GodotRayWorldAlgorithm::CreateFunc(world); + + mem = btAlignedAlloc(sizeof(GodotRayWorldAlgorithm::SwappedCreateFunc), 16); + m_swappedRayWorldCF = new (mem) GodotRayWorldAlgorithm::SwappedCreateFunc(world); +} + +GodotSoftCollisionConfiguration::~GodotSoftCollisionConfiguration() { + m_rayWorldCF->~btCollisionAlgorithmCreateFunc(); + btAlignedFree(m_rayWorldCF); + + m_swappedRayWorldCF->~btCollisionAlgorithmCreateFunc(); + btAlignedFree(m_swappedRayWorldCF); +} + +btCollisionAlgorithmCreateFunc *GodotSoftCollisionConfiguration::getCollisionAlgorithmCreateFunc(int proxyType0, int proxyType1) { + + if (CUSTOM_CONVEX_SHAPE_TYPE == proxyType0 && CUSTOM_CONVEX_SHAPE_TYPE == proxyType1) { + + // This collision is not supported + return m_emptyCreateFunc; + } else if (CUSTOM_CONVEX_SHAPE_TYPE == proxyType0) { + + return m_rayWorldCF; + } else if (CUSTOM_CONVEX_SHAPE_TYPE == proxyType1) { + + return m_swappedRayWorldCF; + } else { + + return btSoftBodyRigidBodyCollisionConfiguration::getCollisionAlgorithmCreateFunc(proxyType0, proxyType1); + } +} + +btCollisionAlgorithmCreateFunc *GodotSoftCollisionConfiguration::getClosestPointsAlgorithmCreateFunc(int proxyType0, int proxyType1) { + + if (CUSTOM_CONVEX_SHAPE_TYPE == proxyType0 && CUSTOM_CONVEX_SHAPE_TYPE == proxyType1) { + + // This collision is not supported + return m_emptyCreateFunc; + } else if (CUSTOM_CONVEX_SHAPE_TYPE == proxyType0) { + + return m_rayWorldCF; + } else if (CUSTOM_CONVEX_SHAPE_TYPE == proxyType1) { + + return m_swappedRayWorldCF; + } else { + + return btSoftBodyRigidBodyCollisionConfiguration::getClosestPointsAlgorithmCreateFunc(proxyType0, proxyType1); + } +} diff --git a/modules/bullet/godot_collision_configuration.h b/modules/bullet/godot_collision_configuration.h index 9b30ad0c62..11012c5f6d 100644 --- a/modules/bullet/godot_collision_configuration.h +++ b/modules/bullet/godot_collision_configuration.h @@ -32,6 +32,7 @@ #define GODOT_COLLISION_CONFIGURATION_H #include <BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h> +#include <BulletSoftBody/btSoftBodyRigidBodyCollisionConfiguration.h> /** @author AndreaCatania @@ -50,4 +51,16 @@ public: virtual btCollisionAlgorithmCreateFunc *getCollisionAlgorithmCreateFunc(int proxyType0, int proxyType1); virtual btCollisionAlgorithmCreateFunc *getClosestPointsAlgorithmCreateFunc(int proxyType0, int proxyType1); }; + +class GodotSoftCollisionConfiguration : public btSoftBodyRigidBodyCollisionConfiguration { + btCollisionAlgorithmCreateFunc *m_rayWorldCF; + btCollisionAlgorithmCreateFunc *m_swappedRayWorldCF; + +public: + GodotSoftCollisionConfiguration(const btDiscreteDynamicsWorld *world, const btDefaultCollisionConstructionInfo &constructionInfo = btDefaultCollisionConstructionInfo()); + virtual ~GodotSoftCollisionConfiguration(); + + virtual btCollisionAlgorithmCreateFunc *getCollisionAlgorithmCreateFunc(int proxyType0, int proxyType1); + virtual btCollisionAlgorithmCreateFunc *getClosestPointsAlgorithmCreateFunc(int proxyType0, int proxyType1); +}; #endif diff --git a/modules/bullet/godot_collision_dispatcher.h b/modules/bullet/godot_collision_dispatcher.h index 2e5a6c2732..1faaa68626 100644 --- a/modules/bullet/godot_collision_dispatcher.h +++ b/modules/bullet/godot_collision_dispatcher.h @@ -31,7 +31,7 @@ #ifndef GODOT_COLLISION_DISPATCHER_H #define GODOT_COLLISION_DISPATCHER_H -#include "int_types.h" +#include "core/int_types.h" #include <btBulletDynamicsCommon.h> diff --git a/modules/bullet/godot_motion_state.h b/modules/bullet/godot_motion_state.h index fa58e86589..5844ef8bf3 100644 --- a/modules/bullet/godot_motion_state.h +++ b/modules/bullet/godot_motion_state.h @@ -82,7 +82,7 @@ public: virtual void setWorldTransform(const btTransform &worldTrans) { bodyCurrentWorldTransform = worldTrans; - owner->scratch(); + owner->notify_transform_changed(); } public: diff --git a/modules/bullet/godot_ray_world_algorithm.cpp b/modules/bullet/godot_ray_world_algorithm.cpp index 53d0ab7e3c..27ee44d1bd 100644 --- a/modules/bullet/godot_ray_world_algorithm.cpp +++ b/modules/bullet/godot_ray_world_algorithm.cpp @@ -49,9 +49,9 @@ GodotRayWorldAlgorithm::SwappedCreateFunc::SwappedCreateFunc(const btDiscreteDyn GodotRayWorldAlgorithm::GodotRayWorldAlgorithm(const btDiscreteDynamicsWorld *world, btPersistentManifold *mf, const btCollisionAlgorithmConstructionInfo &ci, const btCollisionObjectWrapper *body0Wrap, const btCollisionObjectWrapper *body1Wrap, bool isSwapped) : btActivatingCollisionAlgorithm(ci, body0Wrap, body1Wrap), + m_world(world), m_manifoldPtr(mf), m_ownManifold(false), - m_world(world), m_isSwapped(isSwapped) {} GodotRayWorldAlgorithm::~GodotRayWorldAlgorithm() { diff --git a/modules/bullet/godot_result_callbacks.cpp b/modules/bullet/godot_result_callbacks.cpp index 534034d707..3b44ab838e 100644 --- a/modules/bullet/godot_result_callbacks.cpp +++ b/modules/bullet/godot_result_callbacks.cpp @@ -34,11 +34,19 @@ #include "bullet_types_converter.h" #include "collision_object_bullet.h" #include "rigid_body_bullet.h" +#include <BulletCollision/CollisionDispatch/btInternalEdgeUtility.h> /** @author AndreaCatania */ +bool godotContactAddedCallback(btManifoldPoint &cp, const btCollisionObjectWrapper *colObj0Wrap, int partId0, int index0, const btCollisionObjectWrapper *colObj1Wrap, int partId1, int index1) { + if (!colObj1Wrap->getCollisionObject()->getCollisionShape()->isCompound()) { + btAdjustInternalEdgeContacts(cp, colObj1Wrap, colObj0Wrap, partId1, index1); + } + return true; +} + bool GodotFilterCallback::test_collision_filters(uint32_t body0_collision_layer, uint32_t body0_collision_mask, uint32_t body1_collision_layer, uint32_t body1_collision_mask) { return body0_collision_layer & body1_collision_mask || body1_collision_layer & body0_collision_mask; } @@ -156,9 +164,11 @@ bool GodotClosestConvexResultCallback::needsCollision(btBroadphaseProxy *proxy0) } btScalar GodotClosestConvexResultCallback::addSingleResult(btCollisionWorld::LocalConvexResult &convexResult, bool normalInWorldSpace) { - btScalar res = btCollisionWorld::ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace); - m_shapeId = convexResult.m_localShapeInfo->m_triangleIndex; // "m_triangleIndex" Is a odd name but contains the compound shape ID - return res; + if (convexResult.m_localShapeInfo) + m_shapeId = convexResult.m_localShapeInfo->m_triangleIndex; // "m_triangleIndex" Is a odd name but contains the compound shape ID + else + m_shapeId = 0; + return btCollisionWorld::ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace); } bool GodotAllContactResultCallback::needsCollision(btBroadphaseProxy *proxy0) const { diff --git a/modules/bullet/godot_result_callbacks.h b/modules/bullet/godot_result_callbacks.h index 3948f43c00..73e1fc9627 100644 --- a/modules/bullet/godot_result_callbacks.h +++ b/modules/bullet/godot_result_callbacks.h @@ -42,6 +42,9 @@ class RigidBodyBullet; +/// This callback is injected inside bullet server and allow me to smooth contacts against trimesh +bool godotContactAddedCallback(btManifoldPoint &cp, const btCollisionObjectWrapper *colObj0Wrap, int partId0, int index0, const btCollisionObjectWrapper *colObj1Wrap, int partId1, int index1); + /// This class is required to implement custom collision behaviour in the broadphase struct GodotFilterCallback : public btOverlapFilterCallback { static bool test_collision_filters(uint32_t body0_collision_layer, uint32_t body0_collision_mask, uint32_t body1_collision_layer, uint32_t body1_collision_mask); @@ -71,7 +74,10 @@ public: virtual bool needsCollision(btBroadphaseProxy *proxy0) const; virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult &rayResult, bool normalInWorldSpace) { - m_shapeId = rayResult.m_localShapeInfo->m_triangleIndex; // "m_triangleIndex" Is a odd name but contains the compound shape ID + if (rayResult.m_localShapeInfo) + m_shapeId = rayResult.m_localShapeInfo->m_triangleIndex; // "m_triangleIndex" Is a odd name but contains the compound shape ID + else + m_shapeId = 0; return btCollisionWorld::ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace); } }; @@ -81,13 +87,13 @@ struct GodotAllConvexResultCallback : public btCollisionWorld::ConvexResultCallb public: PhysicsDirectSpaceState::ShapeResult *m_results; int m_resultMax; - int count; const Set<RID> *m_exclude; + int count; GodotAllConvexResultCallback(PhysicsDirectSpaceState::ShapeResult *p_results, int p_resultMax, const Set<RID> *p_exclude) : m_results(p_results), - m_exclude(p_exclude), m_resultMax(p_resultMax), + m_exclude(p_exclude), count(0) {} virtual bool needsCollision(btBroadphaseProxy *proxy0) const; @@ -119,6 +125,7 @@ public: GodotClosestConvexResultCallback(const btVector3 &convexFromWorld, const btVector3 &convexToWorld, const Set<RID> *p_exclude, bool p_collide_with_bodies, bool p_collide_with_areas) : btCollisionWorld::ClosestConvexResultCallback(convexFromWorld, convexToWorld), m_exclude(p_exclude), + m_shapeId(0), collide_with_bodies(p_collide_with_bodies), collide_with_areas(p_collide_with_areas) {} @@ -132,8 +139,8 @@ public: const btCollisionObject *m_self_object; PhysicsDirectSpaceState::ShapeResult *m_results; int m_resultMax; - int m_count; const Set<RID> *m_exclude; + int m_count; bool collide_with_bodies; bool collide_with_areas; @@ -141,8 +148,8 @@ public: GodotAllContactResultCallback(btCollisionObject *p_self_object, PhysicsDirectSpaceState::ShapeResult *p_results, int p_resultMax, const Set<RID> *p_exclude, bool p_collide_with_bodies, bool p_collide_with_areas) : m_self_object(p_self_object), m_results(p_results), - m_exclude(p_exclude), m_resultMax(p_resultMax), + m_exclude(p_exclude), m_count(0), collide_with_bodies(p_collide_with_bodies), collide_with_areas(p_collide_with_areas) {} @@ -158,8 +165,8 @@ public: const btCollisionObject *m_self_object; Vector3 *m_results; int m_resultMax; - int m_count; const Set<RID> *m_exclude; + int m_count; bool collide_with_bodies; bool collide_with_areas; @@ -167,8 +174,8 @@ public: GodotContactPairContactResultCallback(btCollisionObject *p_self_object, Vector3 *p_results, int p_resultMax, const Set<RID> *p_exclude, bool p_collide_with_bodies, bool p_collide_with_areas) : m_self_object(p_self_object), m_results(p_results), - m_exclude(p_exclude), m_resultMax(p_resultMax), + m_exclude(p_exclude), m_count(0), collide_with_bodies(p_collide_with_bodies), collide_with_areas(p_collide_with_areas) {} @@ -182,11 +189,11 @@ struct GodotRestInfoContactResultCallback : public btCollisionWorld::ContactResu public: const btCollisionObject *m_self_object; PhysicsDirectSpaceState::ShapeRestInfo *m_result; + const Set<RID> *m_exclude; bool m_collided; real_t m_min_distance; const btCollisionObject *m_rest_info_collision_object; btVector3 m_rest_info_bt_point; - const Set<RID> *m_exclude; bool collide_with_bodies; bool collide_with_areas; diff --git a/modules/bullet/hinge_joint_bullet.cpp b/modules/bullet/hinge_joint_bullet.cpp index 07fde6efb9..3a4459a581 100644 --- a/modules/bullet/hinge_joint_bullet.cpp +++ b/modules/bullet/hinge_joint_bullet.cpp @@ -95,11 +95,6 @@ real_t HingeJointBullet::get_hinge_angle() { void HingeJointBullet::set_param(PhysicsServer::HingeJointParam p_param, real_t p_value) { switch (p_param) { - case PhysicsServer::HINGE_JOINT_BIAS: - if (0 < p_value) { - WARN_PRINTS("HingeJoint doesn't support bias in the Bullet backend, so it's always 0."); - } - break; case PhysicsServer::HINGE_JOINT_LIMIT_UPPER: hingeConstraint->setLimit(hingeConstraint->getLowerLimit(), p_value, hingeConstraint->getLimitSoftness(), hingeConstraint->getLimitBiasFactor(), hingeConstraint->getLimitRelaxationFactor()); break; @@ -122,7 +117,9 @@ void HingeJointBullet::set_param(PhysicsServer::HingeJointParam p_param, real_t hingeConstraint->setMaxMotorImpulse(p_value); break; default: - WARN_PRINTS("HingeJoint doesn't support this parameter in the Bullet backend: " + itos(p_param) + ", value: " + itos(p_value)); + ERR_EXPLAIN("The HingeJoint parameter " + itos(p_param) + " is deprecated."); + WARN_DEPRECATED + break; } } @@ -146,7 +143,8 @@ real_t HingeJointBullet::get_param(PhysicsServer::HingeJointParam p_param) const case PhysicsServer::HINGE_JOINT_MOTOR_MAX_IMPULSE: return hingeConstraint->getMaxMotorImpulse(); default: - WARN_PRINTS("HingeJoint doesn't support this parameter in the Bullet backend: " + itos(p_param)); + ERR_EXPLAIN("The HingeJoint parameter " + itos(p_param) + " is deprecated."); + WARN_DEPRECATED; return 0; } } @@ -161,6 +159,7 @@ void HingeJointBullet::set_flag(PhysicsServer::HingeJointFlag p_flag, bool p_val case PhysicsServer::HINGE_JOINT_FLAG_ENABLE_MOTOR: hingeConstraint->enableMotor(p_value); break; + case PhysicsServer::HINGE_JOINT_FLAG_MAX: break; // Can't happen, but silences warning } } diff --git a/modules/bullet/pin_joint_bullet.cpp b/modules/bullet/pin_joint_bullet.cpp index c4e5b8cdbe..183a7e75b9 100644 --- a/modules/bullet/pin_joint_bullet.cpp +++ b/modules/bullet/pin_joint_bullet.cpp @@ -85,7 +85,8 @@ real_t PinJointBullet::get_param(PhysicsServer::PinJointParam p_param) const { case PhysicsServer::PIN_JOINT_IMPULSE_CLAMP: return p2pConstraint->m_setting.m_impulseClamp; default: - WARN_PRINTS("This get parameter is not supported"); + ERR_EXPLAIN("This parameter " + itos(p_param) + " is deprecated"); + WARN_DEPRECATED return 0; } } diff --git a/modules/bullet/register_types.cpp b/modules/bullet/register_types.cpp index a76b0438b4..31e5f6784e 100644 --- a/modules/bullet/register_types.cpp +++ b/modules/bullet/register_types.cpp @@ -31,8 +31,8 @@ #include "register_types.h" #include "bullet_physics_server.h" -#include "class_db.h" -#include "project_settings.h" +#include "core/class_db.h" +#include "core/project_settings.h" /** @author AndreaCatania diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp index 9c0e802be5..37e7718969 100644 --- a/modules/bullet/rigid_body_bullet.cpp +++ b/modules/bullet/rigid_body_bullet.cpp @@ -259,29 +259,30 @@ RigidBodyBullet::RigidBodyBullet() : RigidCollisionObjectBullet(CollisionObjectBullet::TYPE_RIGID_BODY), kinematic_utilities(NULL), locked_axis(0), - gravity_scale(1), mass(1), + gravity_scale(1), linearDamp(0), angularDamp(0), can_sleep(true), omit_forces_integration(false), - force_integration_callback(NULL), - isTransformChanged(false), - previousActiveState(true), + can_integrate_forces(false), maxCollisionsDetection(0), collisionsCount(0), maxAreasWhereIam(10), areaWhereIamCount(0), countGravityPointSpaces(0), - isScratchedSpaceOverrideModificator(false) { + isScratchedSpaceOverrideModificator(false), + previousActiveState(true), + force_integration_callback(NULL) { godotMotionState = bulletnew(GodotMotionState(this)); // Initial properties const btVector3 localInertia(0, 0, 0); - btRigidBody::btRigidBodyConstructionInfo cInfo(mass, godotMotionState, compoundShape, localInertia); + btRigidBody::btRigidBodyConstructionInfo cInfo(mass, godotMotionState, NULL, localInertia); btBody = bulletnew(btRigidBody(cInfo)); + reload_shapes(); setupBulletCollisionObject(btBody); set_mode(PhysicsServer::BODY_MODE_RIGID); @@ -314,17 +315,24 @@ void RigidBodyBullet::destroy_kinematic_utilities() { } } +void RigidBodyBullet::main_shape_changed() { + CRASH_COND(!get_main_shape()) + btBody->setCollisionShape(get_main_shape()); + set_continuous_collision_detection(is_continuous_collision_detection_enabled()); // Reset +} + void RigidBodyBullet::reload_body() { if (space) { space->remove_rigid_body(this); - space->add_rigid_body(this); + if (get_main_shape()) + space->add_rigid_body(this); } } void RigidBodyBullet::set_space(SpaceBullet *p_space) { // Clear the old space if there is one if (space) { - isTransformChanged = false; + can_integrate_forces = false; // Remove all eventual constraints assert_no_constraints(); @@ -341,8 +349,8 @@ void RigidBodyBullet::set_space(SpaceBullet *p_space) { } void RigidBodyBullet::dispatch_callbacks() { - /// The check isTransformChanged is necessary in order to call integrated forces only when the first transform is sent - if ((btBody->isActive() || previousActiveState != btBody->isActive()) && force_integration_callback && isTransformChanged) { + /// The check isFirstTransformChanged is necessary in order to call integrated forces only when the first transform is sent + if ((btBody->isKinematicObject() || btBody->isActive() || previousActiveState != btBody->isActive()) && force_integration_callback && can_integrate_forces) { if (omit_forces_integration) btBody->clearForces(); @@ -391,10 +399,6 @@ void RigidBodyBullet::set_force_integration_callback(ObjectID p_id, const String } } -void RigidBodyBullet::scratch() { - isTransformChanged = true; -} - void RigidBodyBullet::scratch_space_override_modificator() { isScratchedSpaceOverrideModificator = true; } @@ -409,6 +413,11 @@ void RigidBodyBullet::on_collision_checker_start() { collisionsCount = 0; } +void RigidBodyBullet::on_collision_checker_end() { + // Always true if active and not a static or kinematic body + isTransformChanged = btBody->isActive() && !btBody->isStaticOrKinematicObject(); +} + 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) { @@ -511,7 +520,7 @@ real_t RigidBodyBullet::get_param(PhysicsServer::BodyParameter p_param) const { void RigidBodyBullet::set_mode(PhysicsServer::BodyMode p_mode) { // This is necessary to block force_integration untile next move - isTransformChanged = false; + can_integrate_forces = false; destroy_kinematic_utilities(); // The mode change is relevant to its mass switch (p_mode) { @@ -526,20 +535,18 @@ void RigidBodyBullet::set_mode(PhysicsServer::BodyMode p_mode) { reload_axis_lock(); _internal_set_mass(0); break; - case PhysicsServer::BODY_MODE_RIGID: { + case PhysicsServer::BODY_MODE_RIGID: mode = PhysicsServer::BODY_MODE_RIGID; reload_axis_lock(); _internal_set_mass(0 == mass ? 1 : mass); scratch_space_override_modificator(); break; - } - case PhysicsServer::BODY_MODE_CHARACTER: { + case PhysicsServer::BODY_MODE_CHARACTER: mode = PhysicsServer::BODY_MODE_CHARACTER; reload_axis_lock(); _internal_set_mass(0 == mass ? 1 : mass); scratch_space_override_modificator(); break; - } } btBody->setAngularVelocity(btVector3(0, 0, 0)); @@ -711,15 +718,19 @@ void RigidBodyBullet::set_continuous_collision_detection(bool p_enable) { if (p_enable) { // This threshold enable CCD if the object moves more than // 1 meter in one simulation frame - btBody->setCcdMotionThreshold(1); + btBody->setCcdMotionThreshold(0.1); /// Calculate using the rule writte below the CCD swept sphere radius /// CCD works on an embedded sphere of radius, make sure this radius /// is embedded inside the convex objects, preferably smaller: /// for an object of dimensions 1 meter, try 0.2 - btVector3 center; btScalar radius; - btBody->getCollisionShape()->getBoundingSphere(center, radius); + if (btBody->getCollisionShape()) { + btVector3 center; + btBody->getCollisionShape()->getBoundingSphere(center, radius); + } else { + radius = 0; + } btBody->setCcdSweptSphereRadius(radius * 0.2); } else { btBody->setCcdMotionThreshold(0.); @@ -728,7 +739,7 @@ void RigidBodyBullet::set_continuous_collision_detection(bool p_enable) { } bool RigidBodyBullet::is_continuous_collision_detection_enabled() const { - return 0. != btBody->getCcdMotionThreshold(); + return 0. < btBody->getCcdMotionThreshold(); } void RigidBodyBullet::set_linear_velocity(const Vector3 &p_velocity) { @@ -761,10 +772,12 @@ Vector3 RigidBodyBullet::get_angular_velocity() const { void RigidBodyBullet::set_transform__bullet(const btTransform &p_global_transform) { if (mode == PhysicsServer::BODY_MODE_KINEMATIC) { + if (space) + btBody->setLinearVelocity((p_global_transform.getOrigin() - btBody->getWorldTransform().getOrigin()) / space->get_delta_time()); // The kinematic use MotionState class godotMotionState->moveBody(p_global_transform); } - btBody->setWorldTransform(p_global_transform); + CollisionObjectBullet::set_transform__bullet(p_global_transform); } const btTransform &RigidBodyBullet::get_transform__bullet() const { @@ -777,15 +790,17 @@ const btTransform &RigidBodyBullet::get_transform__bullet() const { } } -void RigidBodyBullet::on_shapes_changed() { - RigidCollisionObjectBullet::on_shapes_changed(); +void RigidBodyBullet::reload_shapes() { + RigidCollisionObjectBullet::reload_shapes(); const btScalar invMass = btBody->getInvMass(); const btScalar mass = invMass == 0 ? 0 : 1 / invMass; - btVector3 inertia; - btBody->getCollisionShape()->calculateLocalInertia(mass, inertia); - btBody->setMassProps(mass, inertia); + if (mainShape) { + btVector3 inertia; + mainShape->calculateLocalInertia(mass, inertia); + btBody->setMassProps(mass, inertia); + } btBody->updateInertiaTensor(); reload_kinematic_shapes(); @@ -834,7 +849,7 @@ void RigidBodyBullet::on_exit_area(AreaBullet *p_area) { bool wasTheAreaFound = false; for (int i = 0; i < areaWhereIamCount; ++i) { if (p_area == areasWhereIam[i]) { - // The area was fount, just shift down all elements + // The area was found, just shift down all elements for (int j = i; j < areaWhereIamCount; ++j) { areasWhereIam.write[j] = areasWhereIam[j + 1]; } @@ -909,10 +924,10 @@ void RigidBodyBullet::reload_space_override_modificator() { } switch (currentArea->get_spOv_mode()) { - ///case PhysicsServer::AREA_SPACE_OVERRIDE_DISABLED: - /// This area does not affect gravity/damp. These are generally areas - /// that exist only to detect collisions, and objects entering or exiting them. - /// break; + case PhysicsServer::AREA_SPACE_OVERRIDE_DISABLED: + /// This area does not affect gravity/damp. These are generally areas + /// that exist only to detect collisions, and objects entering or exiting them. + break; case PhysicsServer::AREA_SPACE_OVERRIDE_COMBINE: /// This area adds its gravity/damp values to whatever has been /// calculated so far. This way, many overlapping areas can combine @@ -971,6 +986,11 @@ void RigidBodyBullet::reload_kinematic_shapes() { kinematic_utilities->copyAllOwnerShapes(); } +void RigidBodyBullet::notify_transform_changed() { + RigidCollisionObjectBullet::notify_transform_changed(); + can_integrate_forces = true; +} + void RigidBodyBullet::_internal_set_mass(real_t p_mass) { btVector3 localInertia(0, 0, 0); @@ -986,7 +1006,8 @@ void RigidBodyBullet::_internal_set_mass(real_t p_mass) { return; m_isStatic = false; - compoundShape->calculateLocalInertia(p_mass, localInertia); + if (mainShape) + mainShape->calculateLocalInertia(p_mass, localInertia); if (PhysicsServer::BODY_MODE_RIGID == mode) { diff --git a/modules/bullet/rigid_body_bullet.h b/modules/bullet/rigid_body_bullet.h index f03009bce9..26e5018c87 100644 --- a/modules/bullet/rigid_body_bullet.h +++ b/modules/bullet/rigid_body_bullet.h @@ -202,6 +202,7 @@ private: real_t angularDamp; bool can_sleep; bool omit_forces_integration; + bool can_integrate_forces; Vector<CollisionData> collisions; // these parameters are used to avoid vector resize @@ -216,7 +217,6 @@ private: int countGravityPointSpaces; bool isScratchedSpaceOverrideModificator; - bool isTransformChanged; bool previousActiveState; // Last check state ForceIntegrationCallback *force_integration_callback; @@ -227,20 +227,22 @@ public: void init_kinematic_utilities(); void destroy_kinematic_utilities(); - _FORCE_INLINE_ class KinematicUtilities *get_kinematic_utilities() const { return kinematic_utilities; } + _FORCE_INLINE_ KinematicUtilities *get_kinematic_utilities() const { return kinematic_utilities; } _FORCE_INLINE_ btRigidBody *get_bt_rigid_body() { return btBody; } + virtual void main_shape_changed(); virtual void reload_body(); virtual void set_space(SpaceBullet *p_space); virtual void dispatch_callbacks(); void set_force_integration_callback(ObjectID p_id, const StringName &p_method, const Variant &p_udata = Variant()); - void scratch(); void scratch_space_override_modificator(); virtual void on_collision_filters_change(); virtual void on_collision_checker_start(); + virtual void on_collision_checker_end(); + void set_max_collisions_detection(int p_maxCollisionsDetection) { maxCollisionsDetection = p_maxCollisionsDetection; collisions.resize(p_maxCollisionsDetection); @@ -301,7 +303,7 @@ public: virtual void set_transform__bullet(const btTransform &p_global_transform); virtual const btTransform &get_transform__bullet() const; - virtual void on_shapes_changed(); + virtual void reload_shapes(); virtual void on_enter_area(AreaBullet *p_area); virtual void on_exit_area(AreaBullet *p_area); @@ -310,6 +312,8 @@ public: /// Kinematic void reload_kinematic_shapes(); + virtual void notify_transform_changed(); + private: void _internal_set_mass(real_t p_mass); }; diff --git a/modules/bullet/shape_bullet.cpp b/modules/bullet/shape_bullet.cpp index fab8d0cf3d..8bb621a863 100644 --- a/modules/bullet/shape_bullet.cpp +++ b/modules/bullet/shape_bullet.cpp @@ -34,8 +34,10 @@ #include "bullet_physics_server.h" #include "bullet_types_converter.h" #include "bullet_utilities.h" +#include "core/project_settings.h" #include "shape_owner_bullet.h" +#include <BulletCollision/CollisionDispatch/btInternalEdgeUtility.h> #include <BulletCollision/CollisionShapes/btConvexPointCloudShape.h> #include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h> #include <btBulletCollisionCommon.h> @@ -63,7 +65,8 @@ btCollisionShape *ShapeBullet::prepare(btCollisionShape *p_btShape) const { void ShapeBullet::notifyShapeChanged() { for (Map<ShapeOwnerBullet *, int>::Element *E = owners.front(); E; E = E->next()) { - static_cast<ShapeOwnerBullet *>(E->key())->on_shape_changed(this); + ShapeOwnerBullet *owner = static_cast<ShapeOwnerBullet *>(E->key()); + owner->shape_changed(owner->find_shape(this)); } } @@ -340,6 +343,9 @@ void ConvexPolygonShapeBullet::setup(const Vector<Vector3> &p_vertices) { } btCollisionShape *ConvexPolygonShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) { + if (!vertices.size()) + // This is necessary since 0 vertices + return prepare(ShapeBullet::create_shape_empty()); btCollisionShape *cs(ShapeBullet::create_shape_convex(vertices)); cs->setLocalScaling(p_implicit_scale); prepare(cs); @@ -355,7 +361,8 @@ ConcavePolygonShapeBullet::ConcavePolygonShapeBullet() : ConcavePolygonShapeBullet::~ConcavePolygonShapeBullet() { if (meshShape) { delete meshShape->getMeshInterface(); - delete meshShape; + delete meshShape->getTriangleInfoMap(); + bulletdelete(meshShape); } faces = PoolVector<Vector3>(); } @@ -377,6 +384,7 @@ void ConcavePolygonShapeBullet::setup(PoolVector<Vector3> p_faces) { if (meshShape) { /// Clear previous created shape delete meshShape->getMeshInterface(); + delete meshShape->getTriangleInfoMap(); bulletdelete(meshShape); } int src_face_count = faces.size(); @@ -394,16 +402,22 @@ void ConcavePolygonShapeBullet::setup(PoolVector<Vector3> p_faces) { btVector3 supVec_1; btVector3 supVec_2; for (int i = 0; i < src_face_count; ++i) { - G_TO_B(facesr[i * 3], supVec_0); + G_TO_B(facesr[i * 3 + 0], supVec_0); G_TO_B(facesr[i * 3 + 1], supVec_1); G_TO_B(facesr[i * 3 + 2], supVec_2); - shapeInterface->addTriangle(supVec_0, supVec_1, supVec_2); + // Inverted from standard godot otherwise btGenerateInternalEdgeInfo generates wrong edge info + shapeInterface->addTriangle(supVec_2, supVec_1, supVec_0); } const bool useQuantizedAabbCompression = true; meshShape = bulletnew(btBvhTriangleMeshShape(shapeInterface, useQuantizedAabbCompression)); + + if (GLOBAL_DEF("physics/3d/smooth_trimesh_collision", false)) { + btTriangleInfoMap *triangleInfoMap = new btTriangleInfoMap(); + btGenerateInternalEdgeInfo(meshShape, triangleInfoMap); + } } else { meshShape = NULL; ERR_PRINT("The faces count are 0, the mesh shape cannot be created"); @@ -418,6 +432,7 @@ btCollisionShape *ConcavePolygonShapeBullet::create_bt_shape(const btVector3 &p_ cs = ShapeBullet::create_shape_empty(); cs->setLocalScaling(p_implicit_scale); prepare(cs); + cs->setMargin(0); return cs; } diff --git a/modules/bullet/shape_bullet.h b/modules/bullet/shape_bullet.h index 638e044e6a..9a1c8f5bfa 100644 --- a/modules/bullet/shape_bullet.h +++ b/modules/bullet/shape_bullet.h @@ -31,8 +31,8 @@ #ifndef SHAPE_BULLET_H #define SHAPE_BULLET_H +#include "core/math/geometry.h" #include "core/variant.h" -#include "geometry.h" #include "rid_bullet.h" #include "servers/physics_server.h" diff --git a/modules/bullet/shape_owner_bullet.h b/modules/bullet/shape_owner_bullet.h index 29d42d12f2..72ddbc482c 100644 --- a/modules/bullet/shape_owner_bullet.h +++ b/modules/bullet/shape_owner_bullet.h @@ -45,11 +45,10 @@ class CollisionObjectBullet; /// E.G. BodyShape is a child of this class ShapeOwnerBullet { public: - /// This is used to set new shape or replace existing - //virtual void _internal_replaceShape(btCollisionShape *p_old_shape, btCollisionShape *p_new_shape) = 0; - virtual void on_shape_changed(const ShapeBullet *const p_shape) = 0; - virtual void on_shapes_changed() = 0; - virtual void remove_shape(class ShapeBullet *p_shape) = 0; + virtual int find_shape(ShapeBullet *p_shape) const = 0; + virtual void shape_changed(int p_shape_index) = 0; + virtual void reload_shapes() = 0; + virtual void remove_shape_full(class ShapeBullet *p_shape) = 0; virtual ~ShapeOwnerBullet() {} }; #endif diff --git a/modules/bullet/slider_joint_bullet.cpp b/modules/bullet/slider_joint_bullet.cpp index 9e1cd23989..9016ec3bf5 100644 --- a/modules/bullet/slider_joint_bullet.cpp +++ b/modules/bullet/slider_joint_bullet.cpp @@ -366,6 +366,7 @@ void SliderJointBullet::set_param(PhysicsServer::SliderJointParam p_param, real_ case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_SOFTNESS: setSoftnessOrthoAng(p_value); break; case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_RESTITUTION: setRestitutionOrthoAng(p_value); break; case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_DAMPING: setDampingOrthoAng(p_value); break; + case PhysicsServer::SLIDER_JOINT_MAX: break; // Can't happen, but silences warning } } diff --git a/modules/bullet/soft_body_bullet.cpp b/modules/bullet/soft_body_bullet.cpp index 9fc7230f91..94f350210f 100644 --- a/modules/bullet/soft_body_bullet.cpp +++ b/modules/bullet/soft_body_bullet.cpp @@ -37,17 +37,17 @@ SoftBodyBullet::SoftBodyBullet() : CollisionObjectBullet(CollisionObjectBullet::TYPE_SOFT_BODY), - total_mass(1), + bt_soft_body(NULL), + isScratched(false), simulation_precision(5), + total_mass(1.), 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), - isScratched(false) {} + drag_coefficient(0.) {} SoftBodyBullet::~SoftBodyBullet() { } @@ -72,12 +72,6 @@ void SoftBodyBullet::set_space(SpaceBullet *p_space) { } } -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) {} diff --git a/modules/bullet/soft_body_bullet.h b/modules/bullet/soft_body_bullet.h index c775193584..d04bfca046 100644 --- a/modules/bullet/soft_body_bullet.h +++ b/modules/bullet/soft_body_bullet.h @@ -91,9 +91,10 @@ public: virtual void reload_body(); virtual void set_space(SpaceBullet *p_space); - virtual void dispatch_callbacks(); - virtual void on_collision_filters_change(); - virtual void on_collision_checker_start(); + virtual void dispatch_callbacks() {} + virtual void on_collision_filters_change() {} + virtual void on_collision_checker_start() {} + virtual void on_collision_checker_end() {} virtual void on_enter_area(AreaBullet *p_area); virtual void on_exit_area(AreaBullet *p_area); diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp index b5329bc347..ab2d1781ad 100644 --- a/modules/bullet/space_bullet.cpp +++ b/modules/bullet/space_bullet.cpp @@ -34,13 +34,13 @@ #include "bullet_types_converter.h" #include "bullet_utilities.h" #include "constraint_bullet.h" +#include "core/project_settings.h" +#include "core/ustring.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" -#include "ustring.h" #include <BulletCollision/CollisionDispatch/btCollisionObject.h> #include <BulletCollision/CollisionDispatch/btGhostObject.h> @@ -150,14 +150,14 @@ int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Tra return btQuery.m_count; } -bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transform &p_xform, const Vector3 &p_motion, float p_margin, float &p_closest_safe, float &p_closest_unsafe, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas, ShapeRestInfo *r_info) { +bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transform &p_xform, const Vector3 &p_motion, float p_margin, float &r_closest_safe, float &r_closest_unsafe, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas, ShapeRestInfo *r_info) { ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->get(p_shape); btCollisionShape *btShape = shape->create_bt_shape(p_xform.basis.get_scale(), p_margin); if (!btShape->isConvex()) { bulletdelete(btShape); ERR_PRINTS("The shape is not a convex shape, then is not supported: shape type: " + itos(shape->get_type())); - return 0; + return false; } btConvexShape *bt_convex_shape = static_cast<btConvexShape *>(btShape); @@ -175,10 +175,15 @@ bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transf btResult.m_collisionFilterGroup = 0; btResult.m_collisionFilterMask = p_collision_mask; - space->dynamicsWorld->convexSweepTest(bt_convex_shape, bt_xform_from, bt_xform_to, btResult, 0.002); + space->dynamicsWorld->convexSweepTest(bt_convex_shape, bt_xform_from, bt_xform_to, btResult, space->dynamicsWorld->getDispatchInfo().m_allowedCcdPenetration); + + r_closest_unsafe = 1.0; + r_closest_safe = 1.0; if (btResult.hasHit()) { - p_closest_safe = p_closest_unsafe = btResult.m_closestHitFraction; + const btScalar l = bt_motion.length(); + r_closest_unsafe = btResult.m_closestHitFraction; + r_closest_safe = MAX(r_closest_unsafe - (1 - ((l - 0.01) / l)), 0); if (r_info) { if (btCollisionObject::CO_RIGID_BODY == btResult.m_hitCollisionObject->getInternalType()) { B_TO_G(static_cast<const btRigidBody *>(btResult.m_hitCollisionObject)->getVelocityInLocalPoint(btResult.m_hitPointWorld), r_info->linear_velocity); @@ -193,7 +198,7 @@ bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transf } bulletdelete(bt_convex_shape); - return btResult.hasHit(); + return true; // Mean success } /// Returns the list of contacts pairs in this order: Local contact, other body contact @@ -293,11 +298,10 @@ Vector3 BulletPhysicsDirectSpaceState::get_closest_point_to_object_volume(RID p_ bool shapes_found = false; - btCompoundShape *compound = rigid_object->get_compound_shape(); - for (int i = compound->getNumChildShapes() - 1; 0 <= i; --i) { - shape = compound->getChildShape(i); + for (int i = rigid_object->get_shape_count() - 1; 0 <= i; --i) { + shape = rigid_object->get_bt_shape(i); if (shape->isConvex()) { - child_transform = compound->getChildTransform(i); + child_transform = rigid_object->get_bt_shape_transform(i); convex_shape = static_cast<btConvexShape *>(shape); input.m_transformB = body_transform * child_transform; @@ -328,16 +332,17 @@ Vector3 BulletPhysicsDirectSpaceState::get_closest_point_to_object_volume(RID p_ SpaceBullet::SpaceBullet() : broadphase(NULL), + collisionConfiguration(NULL), dispatcher(NULL), solver(NULL), - collisionConfiguration(NULL), dynamicsWorld(NULL), soft_body_world_info(NULL), ghostPairCallback(NULL), godotFilterCallback(NULL), gravityDirection(0, -1, 0), gravityMagnitude(10), - contactDebugCount(0) { + contactDebugCount(0), + delta_time(0.) { create_empty_world(GLOBAL_DEF("physics/3d/active_soft_world", true)); direct_access = memnew(BulletPhysicsDirectSpaceState(this)); @@ -535,17 +540,20 @@ void onBulletPreTickCallback(btDynamicsWorld *p_dynamicsWorld, btScalar timeStep void onBulletTickCallback(btDynamicsWorld *p_dynamicsWorld, btScalar timeStep) { - // Notify all Collision objects the collision checker is started const btCollisionObjectArray &colObjArray = p_dynamicsWorld->getCollisionObjectArray(); + + // Notify all Collision objects the collision checker is started for (int i = colObjArray.size() - 1; 0 <= i; --i) { - CollisionObjectBullet *colObj = static_cast<CollisionObjectBullet *>(colObjArray[i]->getUserPointer()); - assert(NULL != colObj); - colObj->on_collision_checker_start(); + static_cast<CollisionObjectBullet *>(colObjArray[i]->getUserPointer())->on_collision_checker_start(); } SpaceBullet *sb = static_cast<SpaceBullet *>(p_dynamicsWorld->getWorldUserInfo()); sb->check_ghost_overlaps(); sb->check_body_collision(); + + for (int i = colObjArray.size() - 1; 0 <= i; --i) { + static_cast<CollisionObjectBullet *>(colObjArray[i]->getUserPointer())->on_collision_checker_end(); + } } BulletPhysicsDirectSpaceState *SpaceBullet::get_direct_state() { @@ -566,7 +574,6 @@ void SpaceBullet::create_empty_world(bool p_create_soft_world) { gjk_epa_pen_solver = bulletnew(btGjkEpaPenetrationDepthSolver); gjk_simplex_solver = bulletnew(btVoronoiSimplexSolver); - gjk_simplex_solver->setEqualVertexThreshold(0.f); void *world_mem; if (p_create_soft_world) { @@ -576,7 +583,7 @@ void SpaceBullet::create_empty_world(bool p_create_soft_world) { } if (p_create_soft_world) { - collisionConfiguration = bulletnew(btSoftBodyRigidBodyCollisionConfiguration); + collisionConfiguration = bulletnew(GodotSoftCollisionConfiguration(static_cast<btDiscreteDynamicsWorld *>(world_mem))); } else { collisionConfiguration = bulletnew(GodotCollisionConfiguration(static_cast<btDiscreteDynamicsWorld *>(world_mem))); } @@ -596,6 +603,7 @@ void SpaceBullet::create_empty_world(bool p_create_soft_world) { godotFilterCallback = bulletnew(GodotFilterCallback); gCalculateCombinedRestitutionCallback = &calculateGodotCombinedRestitution; gCalculateCombinedFrictionCallback = &calculateGodotCombinedFriction; + gContactAddedCallback = &godotContactAddedCallback; dynamicsWorld->setWorldUserInfo(this); @@ -640,11 +648,10 @@ void SpaceBullet::destroy_world() { void SpaceBullet::check_ghost_overlaps() { /// Algorithm support variables - btConvexShape *other_body_shape; + btCollisionShape *other_body_shape; btConvexShape *area_shape; btGjkPairDetector::ClosestPointInput gjk_input; AreaBullet *area; - RigidCollisionObjectBullet *otherObject; int x(-1), i(-1), y(-1), z(-1), indexOverlap(-1); /// For each areas @@ -671,41 +678,67 @@ void SpaceBullet::check_ghost_overlaps() { // For each overlapping for (i = ghostOverlaps.size() - 1; 0 <= i; --i) { - if (ghostOverlaps[i]->getUserIndex() == CollisionObjectBullet::TYPE_AREA) { - if (!static_cast<AreaBullet *>(ghostOverlaps[i]->getUserPointer())->is_monitorable()) - continue; - } else if (ghostOverlaps[i]->getUserIndex() != CollisionObjectBullet::TYPE_RIGID_BODY) + btCollisionObject *overlapped_bt_co = ghostOverlaps[i]; + RigidCollisionObjectBullet *otherObject = static_cast<RigidCollisionObjectBullet *>(overlapped_bt_co->getUserPointer()); + + if (!area->is_transform_changed() && !otherObject->is_transform_changed()) continue; - otherObject = static_cast<RigidCollisionObjectBullet *>(ghostOverlaps[i]->getUserPointer()); + if (overlapped_bt_co->getUserIndex() == CollisionObjectBullet::TYPE_AREA) { + if (!static_cast<AreaBullet *>(overlapped_bt_co->getUserPointer())->is_monitorable()) + continue; + } else if (overlapped_bt_co->getUserIndex() != CollisionObjectBullet::TYPE_RIGID_BODY) + continue; bool hasOverlap = false; // For each area shape - for (y = area->get_compound_shape()->getNumChildShapes() - 1; 0 <= y; --y) { - if (!area->get_compound_shape()->getChildShape(y)->isConvex()) + for (y = area->get_shape_count() - 1; 0 <= y; --y) { + if (!area->get_bt_shape(y)->isConvex()) continue; - gjk_input.m_transformA = area->get_transform__bullet() * area->get_compound_shape()->getChildTransform(y); - area_shape = static_cast<btConvexShape *>(area->get_compound_shape()->getChildShape(y)); + gjk_input.m_transformA = area->get_transform__bullet() * area->get_bt_shape_transform(y); + area_shape = static_cast<btConvexShape *>(area->get_bt_shape(y)); // For each other object shape - for (z = otherObject->get_compound_shape()->getNumChildShapes() - 1; 0 <= z; --z) { + for (z = otherObject->get_shape_count() - 1; 0 <= z; --z) { - if (!otherObject->get_compound_shape()->getChildShape(z)->isConvex()) - continue; + other_body_shape = static_cast<btCollisionShape *>(otherObject->get_bt_shape(z)); + gjk_input.m_transformB = otherObject->get_transform__bullet() * otherObject->get_bt_shape_transform(z); - other_body_shape = static_cast<btConvexShape *>(otherObject->get_compound_shape()->getChildShape(z)); - gjk_input.m_transformB = otherObject->get_transform__bullet() * otherObject->get_compound_shape()->getChildTransform(z); + if (other_body_shape->isConvex()) { - btPointCollector result; - btGjkPairDetector gjk_pair_detector(area_shape, other_body_shape, gjk_simplex_solver, gjk_epa_pen_solver); - gjk_pair_detector.getClosestPoints(gjk_input, result, 0); + btPointCollector result; + btGjkPairDetector gjk_pair_detector(area_shape, static_cast<btConvexShape *>(other_body_shape), gjk_simplex_solver, gjk_epa_pen_solver); + gjk_pair_detector.getClosestPoints(gjk_input, result, 0); + + if (0 >= result.m_distance) { + hasOverlap = true; + goto collision_found; + } - if (0 >= result.m_distance) { - hasOverlap = true; - goto collision_found; + } else { + + btCollisionObjectWrapper obA(NULL, area_shape, area->get_bt_ghost(), gjk_input.m_transformA, -1, y); + btCollisionObjectWrapper obB(NULL, other_body_shape, otherObject->get_bt_collision_object(), gjk_input.m_transformB, -1, z); + + btCollisionAlgorithm *algorithm = dispatcher->findAlgorithm(&obA, &obB, NULL, BT_CONTACT_POINT_ALGORITHMS); + + if (!algorithm) + continue; + + GodotDeepPenetrationContactResultCallback contactPointResult(&obA, &obB); + algorithm->processCollision(&obA, &obB, dynamicsWorld->getDispatchInfo(), &contactPointResult); + + algorithm->~btCollisionAlgorithm(); + dispatcher->freeCollisionAlgorithm(algorithm); + + if (contactPointResult.hasHit()) { + hasOverlap = true; + goto collision_found; + } } + } // ~For each other object shape } // ~For each area shape @@ -762,30 +795,32 @@ void SpaceBullet::check_body_collision() { if (numContacts) { btManifoldPoint &pt = contactManifold->getContactPoint(0); #endif - 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, 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, appliedImpulse * -1, pt.m_index0, pt.m_index1); - } + if (pt.getDistance() <= 0.0) { + 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, 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, appliedImpulse * -1, pt.m_index0, pt.m_index1); + } #ifdef DEBUG_ENABLED - if (is_debugging_contacts()) { - add_debug_contact(collisionWorldPosition); - } + if (is_debugging_contacts()) { + add_debug_contact(collisionWorldPosition); + } #endif + } } } } @@ -978,7 +1013,7 @@ int SpaceBullet::test_ray_separation(RigidBodyBullet *p_body, const Transform &p btVector3 recover_motion(0, 0, 0); - int rays_found; + int rays_found = 0; for (int t(RECOVERING_MOVEMENT_CYCLES); 0 < t; --t) { int last_ray_index = recover_from_penetration_ray(p_body, body_transform, RECOVERING_MOVEMENT_SCALE, p_infinite_inertia, p_result_max, recover_motion, r_results); diff --git a/modules/bullet/space_bullet.h b/modules/bullet/space_bullet.h index 517ec67ffa..c3d55cbbb1 100644 --- a/modules/bullet/space_bullet.h +++ b/modules/bullet/space_bullet.h @@ -57,7 +57,7 @@ class btDiscreteDynamicsWorld; class btEmptyShape; class btGhostPairCallback; class btSoftRigidDynamicsWorld; -class btSoftBodyWorldInfo; +struct btSoftBodyWorldInfo; class ConstraintBullet; class CollisionObjectBullet; class RigidBodyBullet; @@ -65,6 +65,8 @@ class SpaceBullet; class SoftBodyBullet; class btGjkEpaPenetrationDepthSolver; +extern ContactAddedCallback gContactAddedCallback; + class BulletPhysicsDirectSpaceState : public PhysicsDirectSpaceState { GDCLASS(BulletPhysicsDirectSpaceState, PhysicsDirectSpaceState) private: @@ -76,7 +78,7 @@ public: virtual int intersect_point(const Vector3 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false); virtual bool intersect_ray(const Vector3 &p_from, const Vector3 &p_to, RayResult &r_result, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, bool p_pick_ray = false); virtual int intersect_shape(const RID &p_shape, const Transform &p_xform, float p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false); - virtual bool cast_motion(const RID &p_shape, const Transform &p_xform, const Vector3 &p_motion, float p_margin, float &p_closest_safe, float &p_closest_unsafe, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, ShapeRestInfo *r_info = NULL); + virtual bool cast_motion(const RID &p_shape, const Transform &p_xform, const Vector3 &p_motion, float p_margin, float &r_closest_safe, float &r_closest_unsafe, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, ShapeRestInfo *r_info = NULL); /// Returns the list of contacts pairs in this order: Local contact, other body contact virtual bool collide_shape(RID p_shape, const Transform &p_shape_xform, float p_margin, Vector3 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false); virtual bool rest_info(RID p_shape, const Transform &p_shape_xform, float p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false); @@ -94,9 +96,9 @@ class SpaceBullet : public RIDBullet { btCollisionDispatcher *dispatcher; btConstraintSolver *solver; btDiscreteDynamicsWorld *dynamicsWorld; + btSoftBodyWorldInfo *soft_body_world_info; btGhostPairCallback *ghostPairCallback; GodotFilterCallback *godotFilterCallback; - btSoftBodyWorldInfo *soft_body_world_info; btGjkEpaPenetrationDepthSolver *gjk_epa_pen_solver; btVoronoiSimplexSolver *gjk_simplex_solver; diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp index 87c2caec0d..88f3c0338a 100644 --- a/modules/csg/csg.cpp +++ b/modules/csg/csg.cpp @@ -29,10 +29,10 @@ /*************************************************************************/ #include "csg.h" -#include "face3.h" -#include "geometry.h" -#include "os/os.h" -#include "sort.h" +#include "core/math/face3.h" +#include "core/math/geometry.h" +#include "core/os/os.h" +#include "core/sort.h" #include "thirdparty/misc/triangulator.h" void CSGBrush::clear() { @@ -292,12 +292,12 @@ void CSGBrushOperation::BuildPoly::_clip_segment(const CSGBrush *p_brush, int p_ for (int j = 0; j < 2; j++) { if (edges[i].points[0] == segment_idx[0] || edges[i].points[1] == segment_idx[1] || edges[i].points[0] == segment_idx[1] || edges[i].points[1] == segment_idx[0]) { - edge_valid = false; //segment has this point, cant check against this + edge_valid = false; //segment has this point, can't check against this break; } } - if (!edge_valid) //already hit a point in this edge, so dont test it + if (!edge_valid) //already hit a point in this edge, so don't test it continue; //see if either points are within the edge isntead of crossing it @@ -573,7 +573,7 @@ void CSGBrushOperation::_collision_callback(const CSGBrush *A, int p_face_a, Map } } - //if we are still here, it means they most likely intersect, so create BuildPolys if they dont existy + //if we are still here, it means they most likely intersect, so create BuildPolys if they don't exist BuildPoly *poly_a = NULL; @@ -750,7 +750,7 @@ void CSGBrushOperation::_add_poly_outline(const BuildPoly &p_poly, int p_from_po t2d.affine_invert(); - float max_angle; + float max_angle = 0; int next_point_angle = -1; for (int i = 0; i < vertex_process[to_point].size(); i++) { @@ -805,7 +805,7 @@ void CSGBrushOperation::_merge_poly(MeshMerge &mesh, int p_face_idx, const Build //process points that were not processed for (int i = 0; i < edge_process.size(); i++) { - if (edge_process[i] == true) + if (edge_process[i]) continue; //already processed int intersect_poly = -1; @@ -896,7 +896,7 @@ void CSGBrushOperation::_merge_poly(MeshMerge &mesh, int p_face_idx, const Build Vector2 to = p_poly.points[to_idx].point; with_outline_vertex = l; - //try agaisnt outline (other points) first + //try against outline (other points) first valid = true; @@ -915,7 +915,7 @@ void CSGBrushOperation::_merge_poly(MeshMerge &mesh, int p_face_idx, const Build if (!valid) continue; - //try agaisnt all holes including self + //try against all holes including self for (int m = 0; m < polys[i].holes.size(); m++) { diff --git a/modules/csg/csg.h b/modules/csg/csg.h index 2e07c23e28..5d6432eca8 100644 --- a/modules/csg/csg.h +++ b/modules/csg/csg.h @@ -31,15 +31,15 @@ #ifndef CSG_H #define CSG_H -#include "aabb.h" -#include "dvector.h" -#include "map.h" -#include "oa_hash_map.h" -#include "plane.h" -#include "rect2.h" +#include "core/dvector.h" +#include "core/map.h" +#include "core/math/aabb.h" +#include "core/math/plane.h" +#include "core/math/rect2.h" +#include "core/math/transform.h" +#include "core/math/vector3.h" +#include "core/oa_hash_map.h" #include "scene/resources/material.h" -#include "transform.h" -#include "vector3.h" struct CSGBrush { diff --git a/modules/csg/csg_gizmos.cpp b/modules/csg/csg_gizmos.cpp index f9744c72af..5864d02615 100644 --- a/modules/csg/csg_gizmos.cpp +++ b/modules/csg/csg_gizmos.cpp @@ -139,9 +139,9 @@ void CSGShapeSpatialGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, int p_i d = 0.001; switch (p_idx) { - case 0: s->set_width(d); break; - case 1: s->set_height(d); break; - case 2: s->set_depth(d); break; + case 0: s->set_width(d * 2); break; + case 1: s->set_height(d * 2); break; + case 2: s->set_depth(d * 2); break; } } @@ -215,7 +215,7 @@ void CSGShapeSpatialGizmoPlugin::commit_handle(EditorSpatialGizmo *p_gizmo, int UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); ur->create_action(TTR("Change Box Shape Extents")); static const char *method[3] = { "set_width", "set_height", "set_depth" }; - float current; + float current = 0; switch (p_idx) { case 0: current = s->get_width(); break; case 1: current = s->get_height(); break; @@ -329,9 +329,9 @@ void CSGShapeSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { CSGBox *s = Object::cast_to<CSGBox>(cs); Vector<Vector3> handles; - 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())); + handles.push_back(Vector3(s->get_width() * 0.5, 0, 0)); + handles.push_back(Vector3(0, s->get_height() * 0.5, 0)); + handles.push_back(Vector3(0, 0, s->get_depth() * 0.5)); p_gizmo->add_handles(handles, handles_material); } diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index 258c628d93..4eb798de11 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -521,6 +521,11 @@ CSGBrush *CSGMesh::_build_brush() { Array arrays = mesh->surface_get_arrays(i); + if (arrays.size() == 0) { + _make_dirty(); + ERR_FAIL_COND_V(arrays.size() == 0, NULL); + } + PoolVector<Vector3> avertices = arrays[Mesh::ARRAY_VERTEX]; if (avertices.size() == 0) continue; @@ -593,8 +598,8 @@ CSGBrush *CSGMesh::_build_brush() { mw[j / 3] = mat; } } else { - int is = vertices.size(); - int as = avertices.size(); + int as = vertices.size(); + int is = avertices.size(); vertices.resize(as + is); smooth.resize((as + is) / 3); @@ -917,7 +922,7 @@ CSGBrush *CSGBox::_build_brush() { int face = 0; - Vector3 vertex_mul(width, height, depth); + Vector3 vertex_mul(width * 0.5, height * 0.5, depth * 0.5); { @@ -1051,9 +1056,9 @@ Ref<Material> CSGBox::get_material() const { CSGBox::CSGBox() { // defaults - width = 1.0; - height = 1.0; - depth = 1.0; + width = 2.0; + height = 2.0; + depth = 2.0; } /////////////// @@ -1573,7 +1578,7 @@ CSGBrush *CSGPolygon::_build_brush() { } CSGBrush *brush = memnew(CSGBrush); - int face_count; + int face_count = 0; switch (mode) { case MODE_DEPTH: face_count = triangles.size() * 2 / 3 + (final_polygon.size()) * 2; break; diff --git a/modules/cvtt/SCsub b/modules/cvtt/SCsub index 5c396482aa..fcc69d8371 100644 --- a/modules/cvtt/SCsub +++ b/modules/cvtt/SCsub @@ -14,8 +14,11 @@ if env['builtin_squish']: thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_cvtt.add_source_files(env.modules_sources, thirdparty_sources) env_cvtt.Append(CPPPATH=[thirdparty_dir]) + env_thirdparty = env_cvtt.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + # Godot source files env_cvtt.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/cvtt/image_compress_cvtt.cpp b/modules/cvtt/image_compress_cvtt.cpp index 3a371c8597..732b9cf733 100644 --- a/modules/cvtt/image_compress_cvtt.cpp +++ b/modules/cvtt/image_compress_cvtt.cpp @@ -30,9 +30,9 @@ #include "image_compress_cvtt.h" -#include "os/os.h" -#include "os/thread.h" -#include "print_string.h" +#include "core/os/os.h" +#include "core/os/thread.h" +#include "core/print_string.h" #include <ConvectionKernels.h> @@ -118,7 +118,7 @@ static void _digest_row_task(const CVTTCompressionJobParams &p_job_params, const cvtt::Kernels::EncodeBC7(output_blocks, input_blocks_ldr, p_job_params.options); } - int num_real_blocks = ((w - x_start) + 3) / 4; + unsigned int num_real_blocks = ((w - x_start) + 3) / 4; if (num_real_blocks > cvtt::NumParallelBlocks) { num_real_blocks = cvtt::NumParallelBlocks; } @@ -131,7 +131,7 @@ static void _digest_row_task(const CVTTCompressionJobParams &p_job_params, const static void _digest_job_queue(void *p_job_queue) { CVTTCompressionJobQueue *job_queue = static_cast<CVTTCompressionJobQueue *>(p_job_queue); - for (int next_task = atomic_increment(&job_queue->current_task); next_task <= job_queue->num_tasks; next_task = atomic_increment(&job_queue->current_task)) { + for (uint32_t next_task = atomic_increment(&job_queue->current_task); next_task <= job_queue->num_tasks; next_task = atomic_increment(&job_queue->current_task)) { _digest_row_task(job_queue->job_params, job_queue->job_tasks[next_task - 1]); } } @@ -228,8 +228,6 @@ void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::CompressS uint8_t *out_bytes = &wb[dst_ofs]; for (int y_start = 0; y_start < h; y_start += 4) { - int y_end = y_start + 4; - CVTTCompressionRowTask row_task; row_task.width = w; row_task.height = h; @@ -247,8 +245,8 @@ void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::CompressS } dst_ofs += (MAX(4, bw) * MAX(4, bh)) >> shift; - w >>= 1; - h >>= 1; + w = MAX(w / 2, 1); + h = MAX(h / 2, 1); } if (num_job_threads > 0) { @@ -308,7 +306,6 @@ void image_decompress_cvtt(Image *p_image) { 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); - int shift = Image::get_format_pixel_rshift(target_format); PoolVector<uint8_t>::Write wb = data.write(); @@ -335,7 +332,7 @@ void image_decompress_cvtt(Image *p_image) { uint8_t input_blocks[16 * cvtt::NumParallelBlocks]; memset(input_blocks, 0, sizeof(input_blocks)); - int num_real_blocks = ((w - x_start) + 3) / 4; + unsigned int num_real_blocks = ((w - x_start) + 3) / 4; if (num_real_blocks > cvtt::NumParallelBlocks) { num_real_blocks = cvtt::NumParallelBlocks; } diff --git a/modules/cvtt/image_compress_cvtt.h b/modules/cvtt/image_compress_cvtt.h index 0e18b247e5..fe912ad3bb 100644 --- a/modules/cvtt/image_compress_cvtt.h +++ b/modules/cvtt/image_compress_cvtt.h @@ -31,7 +31,7 @@ #ifndef IMAGE_COMPRESS_CVTT_H #define IMAGE_COMPRESS_CVTT_H -#include "image.h" +#include "core/image.h" void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::CompressSource p_source); void image_decompress_cvtt(Image *p_image); diff --git a/modules/dds/texture_loader_dds.cpp b/modules/dds/texture_loader_dds.cpp index 3cb24d0407..53e9773791 100644 --- a/modules/dds/texture_loader_dds.cpp +++ b/modules/dds/texture_loader_dds.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "texture_loader_dds.h" -#include "os/file_access.h" +#include "core/os/file_access.h" enum { DDS_MAGIC = 0x20534444, @@ -108,8 +108,8 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path, uint32_t magic = f->get_32(); uint32_t hsize = f->get_32(); uint32_t flags = f->get_32(); - uint32_t width = f->get_32(); uint32_t height = f->get_32(); + uint32_t width = f->get_32(); uint32_t pitch = f->get_32(); /* uint32_t depth = */ f->get_32(); uint32_t mipmaps = f->get_32(); diff --git a/modules/dds/texture_loader_dds.h b/modules/dds/texture_loader_dds.h index 14d99ff506..4e2593c744 100644 --- a/modules/dds/texture_loader_dds.h +++ b/modules/dds/texture_loader_dds.h @@ -31,7 +31,7 @@ #ifndef TEXTURE_LOADER_DDS_H #define TEXTURE_LOADER_DDS_H -#include "io/resource_loader.h" +#include "core/io/resource_loader.h" #include "scene/resources/texture.h" class ResourceFormatDDS : public ResourceFormatLoader { diff --git a/modules/enet/SCsub b/modules/enet/SCsub index 7caeafa1d6..a57a4b29ea 100644 --- a/modules/enet/SCsub +++ b/modules/enet/SCsub @@ -21,8 +21,11 @@ if env['builtin_enet']: ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_enet.add_source_files(env.modules_sources, thirdparty_sources) env_enet.Append(CPPPATH=[thirdparty_dir]) env_enet.Append(CPPFLAGS=["-DGODOT_ENET"]) + env_thirdparty = env_enet.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + env_enet.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml index fab4b05da9..e057a435ac 100644 --- a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml +++ b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml @@ -7,7 +7,7 @@ A PacketPeer implementation that should be passed to [method SceneTree.set_network_peer] after being initialized as either a client or server. Events can then be handled by connecting to [SceneTree] signals. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/networking/high_level_multiplayer.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/networking/high_level_multiplayer.html</link> <link>http://enet.bespin.org/usergroup0.html</link> </tutorials> <demos> @@ -36,7 +36,7 @@ <argument index="4" name="client_port" type="int" default="0"> </argument> <description> - Create client that connects to a server at [code]address[/code] using specified [code]port[/code]. The given address needs to be either a fully qualified domain nome (e.g. [code]www.example.com[/code]) or an IP address in IPv4 or IPv6 format (e.g. [code]192.168.1.1[/code]). The [code]port[/code] is the port the server is listening on. The [code]in_bandwidth[/code] and [code]out_bandwidth[/code] parameters can be used to limit the incoming and outgoing bandwidth to the given number of bytes per second. The default of 0 means unlimited bandwidth. Note that ENet will strategically drop packets on specific sides of a connection between peers to ensure the peer's bandwidth is not overwhelmed. The bandwidth parameters also determine the window size of a connection which limits the amount of reliable packets that may be in transit at any given time. Returns [code]OK[/code] if a client was created, [code]ERR_ALREADY_IN_USE[/code] if this NetworkedMultiplayerEnet instance already has an open connection (in which case you need to call [method close_connection] first) or [code]ERR_CANT_CREATE[/code] if the client could not be created. If [code]client_port[/code] is specified, the client will also listen to the given port, this is useful in some NAT traveral technique. + Create client that connects to a server at [code]address[/code] using specified [code]port[/code]. The given address needs to be either a fully qualified domain nome (e.g. [code]www.example.com[/code]) or an IP address in IPv4 or IPv6 format (e.g. [code]192.168.1.1[/code]). The [code]port[/code] is the port the server is listening on. The [code]in_bandwidth[/code] and [code]out_bandwidth[/code] parameters can be used to limit the incoming and outgoing bandwidth to the given number of bytes per second. The default of 0 means unlimited bandwidth. Note that ENet will strategically drop packets on specific sides of a connection between peers to ensure the peer's bandwidth is not overwhelmed. The bandwidth parameters also determine the window size of a connection which limits the amount of reliable packets that may be in transit at any given time. Returns [code]OK[/code] if a client was created, [code]ERR_ALREADY_IN_USE[/code] if this NetworkedMultiplayerEnet instance already has an open connection (in which case you need to call [method close_connection] first) or [code]ERR_CANT_CREATE[/code] if the client could not be created. If [code]client_port[/code] is specified, the client will also listen to the given port, this is useful in some NAT traversal technique. </description> </method> <method name="create_server"> @@ -76,7 +76,7 @@ <return type="int"> </return> <description> - Returns the channel of the next packet that will be retrieved via [method PacketPeer.get_packet_peer] + Returns the channel of the next packet that will be retrieved via [method PacketPeer.get_packet] </description> </method> <method name="get_peer_address" qualifiers="const"> diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp index 25b7f2472d..7b5fd854ff 100644 --- a/modules/enet/networked_multiplayer_enet.cpp +++ b/modules/enet/networked_multiplayer_enet.cpp @@ -29,9 +29,9 @@ /*************************************************************************/ #include "networked_multiplayer_enet.h" -#include "io/ip.h" -#include "io/marshalls.h" -#include "os/os.h" +#include "core/io/ip.h" +#include "core/io/marshalls.h" +#include "core/os/os.h" void NetworkedMultiplayerENet::set_transfer_mode(TransferMode p_mode) { @@ -100,8 +100,8 @@ Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int host = enet_host_create(&address /* the address to bind the server host to */, p_max_clients /* allow up to 32 clients and/or outgoing connections */, channel_count /* allow up to channel_count to be used */, - p_in_bandwidth /* limit incoming bandwith if > 0 */, - p_out_bandwidth /* limit outgoing bandwith if > 0 */); + p_in_bandwidth /* limit incoming bandwidth if > 0 */, + p_out_bandwidth /* limit outgoing bandwidth if > 0 */); ERR_FAIL_COND_V(!host, ERR_CANT_CREATE); @@ -144,14 +144,14 @@ Error NetworkedMultiplayerENet::create_client(const String &p_address, int p_por host = enet_host_create(&c_client /* create a client host */, 1 /* only allow 1 outgoing connection */, channel_count /* allow up to channel_count to be used */, - p_in_bandwidth /* limit incoming bandwith if > 0 */, - p_out_bandwidth /* limit outgoing bandwith if > 0 */); + p_in_bandwidth /* limit incoming bandwidth if > 0 */, + p_out_bandwidth /* limit outgoing bandwidth if > 0 */); } else { host = enet_host_create(NULL /* create a client host */, 1 /* only allow 1 outgoing connection */, channel_count /* allow up to channel_count to be used */, - p_in_bandwidth /* limit incoming bandwith if > 0 */, - p_out_bandwidth /* limit outgoing bandwith if > 0 */); + p_in_bandwidth /* limit incoming bandwidth if > 0 */, + p_out_bandwidth /* limit outgoing bandwidth if > 0 */); } ERR_FAIL_COND_V(!host, ERR_CANT_CREATE); @@ -207,13 +207,13 @@ void NetworkedMultiplayerENet::poll() { _pop_current_packet(); ENetEvent event; - /* Wait up to 1000 milliseconds for an event. */ + /* Keep servicing until there are no available events left in queue. */ while (true) { if (!host || !active) // Might have been disconnected while emitting a notification return; - int ret = enet_host_service(host, &event, 1); + int ret = enet_host_service(host, &event, 0); if (ret < 0) { // Error, do something? @@ -293,7 +293,7 @@ void NetworkedMultiplayerENet::poll() { encode_uint32(*id, &packet->data[4]); enet_peer_send(E->get(), SYSCH_CONFIG, packet); } - } else if (!server) { + } else { emit_signal("server_disconnected"); close_connection(); return; diff --git a/modules/enet/networked_multiplayer_enet.h b/modules/enet/networked_multiplayer_enet.h index 705807d429..a2b35f2395 100644 --- a/modules/enet/networked_multiplayer_enet.h +++ b/modules/enet/networked_multiplayer_enet.h @@ -31,8 +31,8 @@ #ifndef NETWORKED_MULTIPLAYER_ENET_H #define NETWORKED_MULTIPLAYER_ENET_H -#include "io/compression.h" -#include "io/networked_multiplayer_peer.h" +#include "core/io/compression.h" +#include "core/io/networked_multiplayer_peer.h" #include <enet/enet.h> diff --git a/modules/enet/register_types.cpp b/modules/enet/register_types.cpp index cabaeb692a..cde70e8d5c 100644 --- a/modules/enet/register_types.cpp +++ b/modules/enet/register_types.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "register_types.h" -#include "error_macros.h" +#include "core/error_macros.h" #include "networked_multiplayer_enet.h" static bool enet_ok = false; diff --git a/modules/etc/SCsub b/modules/etc/SCsub index 31d8f00ef3..d2c77d6e3c 100644 --- a/modules/etc/SCsub +++ b/modules/etc/SCsub @@ -27,16 +27,20 @@ thirdparty_sources = [ ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env_etc.add_source_files(env.modules_sources, thirdparty_sources) env_etc.Append(CPPPATH=[thirdparty_dir]) -# Godot source files -env_etc.add_source_files(env.modules_sources, "*.cpp") - # upstream uses c++11 -if (not env_etc.msvc): +if not env.msvc: env_etc.Append(CCFLAGS="-std=c++11") -# -ffast-math seems to be incompatible with ec2comp on recent versions of + +# -ffast-math seems to be incompatible with etc2comp on recent versions of # GCC and Clang if '-ffast-math' in env_etc['CCFLAGS']: env_etc['CCFLAGS'].remove('-ffast-math') + +env_thirdparty = env_etc.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + +# Godot source files +env_etc.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/etc/image_etc.cpp b/modules/etc/image_etc.cpp index f5c817c816..fbbc765bf2 100644 --- a/modules/etc/image_etc.cpp +++ b/modules/etc/image_etc.cpp @@ -31,10 +31,10 @@ #include "image_etc.h" #include "Etc.h" #include "EtcFilter.h" -#include "image.h" -#include "os/copymem.h" -#include "os/os.h" -#include "print_string.h" +#include "core/image.h" +#include "core/os/copymem.h" +#include "core/os/os.h" +#include "core/print_string.h" static Image::Format _get_etc2_mode(Image::DetectChannels format) { switch (format) { @@ -47,13 +47,14 @@ static Image::Format _get_etc2_mode(Image::DetectChannels format) { case Image::DETECTED_RGB: return Image::FORMAT_ETC2_RGB8; - default: + case Image::DETECTED_RGBA: return Image::FORMAT_ETC2_RGBA8; - // TODO: would be nice if we could use FORMAT_ETC2_RGB8A1 for FORMAT_RGBA5551 + // TODO: would be nice if we could use FORMAT_ETC2_RGB8A1 for FORMAT_RGBA5551 + default: + // TODO: Kept for compatibility, but should be investigated whether it's correct or if it should error out + return Image::FORMAT_ETC2_RGBA8; } - - ERR_FAIL_COND_V(true, Image::FORMAT_MAX); } static Etc::Image::Format _image_format_to_etc2comp_format(Image::Format format) { @@ -81,17 +82,10 @@ static Etc::Image::Format _image_format_to_etc2comp_format(Image::Format format) case Image::FORMAT_ETC2_RGB8A1: return Etc::Image::Format::RGB8A1; - } - - ERR_FAIL_COND_V(true, Etc::Image::Format::UNKNOWN); -} - -static void _decompress_etc1(Image *p_img) { - // not implemented, to be removed -} -static void _decompress_etc2(Image *p_img) { - // not implemented, to be removed + default: + ERR_FAIL_V(Etc::Image::Format::UNKNOWN); + } } static void _compress_etc(Image *p_img, float p_lossy_quality, bool force_etc1_format, Image::CompressSource p_source) { @@ -174,7 +168,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()); + unsigned 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; @@ -243,8 +237,5 @@ static void _compress_etc2(Image *p_img, float p_lossy_quality, Image::CompressS void _register_etc_compress_func() { Image::_image_compress_etc1_func = _compress_etc1; - //Image::_image_decompress_etc1 = _decompress_etc1; - Image::_image_compress_etc2_func = _compress_etc2; - //Image::_image_decompress_etc2 = _decompress_etc2; } diff --git a/modules/etc/texture_loader_pkm.cpp b/modules/etc/texture_loader_pkm.cpp index ac89259c9b..3041dde876 100644 --- a/modules/etc/texture_loader_pkm.cpp +++ b/modules/etc/texture_loader_pkm.cpp @@ -30,7 +30,7 @@ #include "texture_loader_pkm.h" -#include "os/file_access.h" +#include "core/os/file_access.h" #include <string.h> struct ETC1Header { diff --git a/modules/etc/texture_loader_pkm.h b/modules/etc/texture_loader_pkm.h index 3c6d9180bd..b5a95767c7 100644 --- a/modules/etc/texture_loader_pkm.h +++ b/modules/etc/texture_loader_pkm.h @@ -31,7 +31,7 @@ #ifndef TEXTURE_LOADER_PKM_H #define TEXTURE_LOADER_PKM_H -#include "io/resource_loader.h" +#include "core/io/resource_loader.h" #include "scene/resources/texture.h" class ResourceFormatPKM : public ResourceFormatLoader { diff --git a/modules/freetype/SCsub b/modules/freetype/SCsub index 301f218361..3e2068b8db 100644 --- a/modules/freetype/SCsub +++ b/modules/freetype/SCsub @@ -1,9 +1,11 @@ #!/usr/bin/env python Import('env') +Import('env_modules') + from compat import isbasestring -# Not building in a separate env as scene needs it +env_freetype = env_modules.Clone() # Thirdparty source files if env['builtin_freetype']: @@ -54,26 +56,35 @@ if env['builtin_freetype']: ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - sfnt = thirdparty_dir + 'src/sfnt/sfnt.c' - - if 'platform' in env: - if env['platform'] == 'uwp': - # Include header for UWP to fix build issues - env.Append(CCFLAGS=['/FI', '"modules/freetype/uwpdef.h"']) - elif env['platform'] == 'javascript': - # Forcibly undefine this macro so SIMD is not used in this file, - # since currently unsuported in WASM - sfnt = env.Object(sfnt, CPPFLAGS=['-U__OPTIMIZE__']) + if env['platform'] == 'uwp': + # Include header for UWP to fix build issues + env_freetype.Append(CCFLAGS=['/FI', '"modules/freetype/uwpdef.h"']) + # Globally too, as freetype is used in scene (see bottom) + env.Append(CCFLAGS=['/FI', '"modules/freetype/uwpdef.h"']) + sfnt = thirdparty_dir + 'src/sfnt/sfnt.c' + if env['platform'] == 'javascript': + # Forcibly undefine this macro so SIMD is not used in this file, + # since currently unsupported in WASM + sfnt = env_freetype.Object(sfnt, CPPFLAGS=['-U__OPTIMIZE__']) thirdparty_sources += [sfnt] - env.Append(CPPPATH=[thirdparty_dir, thirdparty_dir + "/include"]) + env_freetype.Append(CPPPATH=[thirdparty_dir + "/include"]) + # Also needed in main env for scene/ + env.Append(CPPPATH=[thirdparty_dir + "/include"]) - # also requires libpng headers + env_freetype.Append(CCFLAGS=['-DFT2_BUILD_LIBRARY', '-DFT_CONFIG_OPTION_USE_PNG']) + if (env['target'] != 'release'): + env_freetype.Append(CCFLAGS=['-DZLIB_DEBUG']) + + # Also requires libpng headers if env['builtin_libpng']: - env.Append(CPPPATH=["#thirdparty/libpng"]) + env_freetype.Append(CPPPATH=["#thirdparty/libpng"]) + + env_thirdparty = env_freetype.Clone() + env_thirdparty.disable_warnings() + lib = env_thirdparty.add_library("freetype_builtin", thirdparty_sources) - lib = env.add_library("freetype_builtin", thirdparty_sources) # Needs to be appended to arrive after libscene in the linker call, # but we don't want it to arrive *after* system libs, so manual hack # LIBS contains first SCons Library objects ("SCons.Node.FS.File object") @@ -86,12 +97,8 @@ if env['builtin_freetype']: break if not inserted: env.Append(LIBS=[lib]) - env.Append(CCFLAGS=['-DFT2_BUILD_LIBRARY']) - if (env['target'] != 'release'): - env.Append(CCFLAGS=['-DZLIB_DEBUG']) # Godot source files -env.add_source_files(env.modules_sources, "*.cpp") -env.Append(CCFLAGS=['-DFREETYPE_ENABLED', '-DFT_CONFIG_OPTION_USE_PNG']) - -Export('env') +env_freetype.add_source_files(env.modules_sources, "*.cpp") +# Used in scene/, needs to be in main env +env.Append(CCFLAGS=['-DFREETYPE_ENABLED']) diff --git a/modules/gdnative/SCsub b/modules/gdnative/SCsub index 46b2a832f1..fe2d8c7ce9 100644 --- a/modules/gdnative/SCsub +++ b/modules/gdnative/SCsub @@ -1,35 +1,38 @@ #!/usr/bin/env python Import('env') +Import('env_modules') -gdn_env = env.Clone() -gdn_env.add_source_files(env.modules_sources, "gdnative.cpp") -gdn_env.add_source_files(env.modules_sources, "register_types.cpp") -gdn_env.add_source_files(env.modules_sources, "android/*.cpp") -gdn_env.add_source_files(env.modules_sources, "gdnative/*.cpp") -gdn_env.add_source_files(env.modules_sources, "nativescript/*.cpp") -gdn_env.add_source_files(env.modules_sources, "gdnative_library_singleton_editor.cpp") -gdn_env.add_source_files(env.modules_sources, "gdnative_library_editor_plugin.cpp") +env_gdnative = env_modules.Clone() +env_gdnative.add_source_files(env.modules_sources, "gdnative.cpp") +env_gdnative.add_source_files(env.modules_sources, "register_types.cpp") +env_gdnative.add_source_files(env.modules_sources, "android/*.cpp") +env_gdnative.add_source_files(env.modules_sources, "gdnative/*.cpp") +env_gdnative.add_source_files(env.modules_sources, "nativescript/*.cpp") +env_gdnative.add_source_files(env.modules_sources, "gdnative_library_singleton_editor.cpp") +env_gdnative.add_source_files(env.modules_sources, "gdnative_library_editor_plugin.cpp") -gdn_env.Append(CPPPATH=['#modules/gdnative/include/']) +env_gdnative.Append(CPPPATH=['#modules/gdnative/include/']) + +Export('env_gdnative') SConscript("net/SCsub") SConscript("arvr/SCsub") SConscript("pluginscript/SCsub") + from platform_methods import run_in_subprocess import gdnative_builders - -_, gensource = gdn_env.CommandNoCache(['include/gdnative_api_struct.gen.h', 'gdnative_api_struct.gen.cpp'], +_, gensource = env_gdnative.CommandNoCache(['include/gdnative_api_struct.gen.h', 'gdnative_api_struct.gen.cpp'], 'gdnative_api.json', run_in_subprocess(gdnative_builders.build_gdnative_api_struct)) -gdn_env.add_source_files(env.modules_sources, [gensource]) +env_gdnative.add_source_files(env.modules_sources, [gensource]) env.use_ptrcall = True if ARGUMENTS.get('gdnative_wrapper', False): - gensource, = gdn_env.CommandNoCache('gdnative_wrapper_code.gen.cpp', 'gdnative_api.json', run_in_subprocess(gdnative_builders.build_gdnative_wrapper_code)) + gensource, = env_gdnative.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/SCsub b/modules/gdnative/arvr/SCsub index ecc5996108..20eaa99592 100644 --- a/modules/gdnative/arvr/SCsub +++ b/modules/gdnative/arvr/SCsub @@ -1,13 +1,6 @@ #!/usr/bin/env python -import os -import methods - Import('env') -Import('env_modules') - -env_arvr_gdnative = env_modules.Clone() - -env_arvr_gdnative.Append(CPPPATH=['#modules/gdnative/include/']) -env_arvr_gdnative.add_source_files(env.modules_sources, '*.cpp') +Import('env_gdnative') +env_gdnative.add_source_files(env.modules_sources, '*.cpp') diff --git a/modules/gdnative/arvr/config.py b/modules/gdnative/arvr/config.py index 4d1bdfe4d1..53bc827027 100644 --- a/modules/gdnative/arvr/config.py +++ b/modules/gdnative/arvr/config.py @@ -1,4 +1,4 @@ -def can_build(platform): +def can_build(env, platform): return True def configure(env): diff --git a/modules/gdnative/gdnative.cpp b/modules/gdnative/gdnative.cpp index 0acd6c27d8..283e9b3bc8 100644 --- a/modules/gdnative/gdnative.cpp +++ b/modules/gdnative/gdnative.cpp @@ -30,11 +30,11 @@ #include "gdnative.h" -#include "global_constants.h" -#include "io/file_access_encrypted.h" -#include "os/file_access.h" -#include "os/os.h" -#include "project_settings.h" +#include "core/global_constants.h" +#include "core/io/file_access_encrypted.h" +#include "core/os/file_access.h" +#include "core/os/os.h" +#include "core/project_settings.h" #include "scene/main/scene_tree.h" @@ -306,6 +306,13 @@ bool GDNative::initialize() { #elif defined(UWP_ENABLED) // On UWP we use a relative path from the app String path = lib_path.replace("res://", ""); +#elif defined(OSX_ENABLED) + // On OSX the exported libraries are located under the Frameworks directory. + // So we need to replace the library path. + String path = ProjectSettings::get_singleton()->globalize_path(lib_path); + if (!FileAccess::exists(path)) { + path = OS::get_singleton()->get_executable_path().get_base_dir().plus_file("../Frameworks").plus_file(lib_path.get_file()); + } #else String path = ProjectSettings::get_singleton()->globalize_path(lib_path); #endif @@ -390,7 +397,7 @@ bool GDNative::terminate() { if (library->should_load_once()) { Vector<Ref<GDNative> > *gdnatives = &(*GDNativeLibrary::loaded_libraries)[library->get_current_library_path()]; if (gdnatives->size() > 1) { - // there are other GDNative's still using this library, so we actually don't terminte + // there are other GDNative's still using this library, so we actually don't terminate gdnatives->erase(Ref<GDNative>(this)); initialized = false; return true; diff --git a/modules/gdnative/gdnative.h b/modules/gdnative/gdnative.h index 148f85723e..c5364a72ac 100644 --- a/modules/gdnative/gdnative.h +++ b/modules/gdnative/gdnative.h @@ -31,15 +31,15 @@ #ifndef GDNATIVE_H #define GDNATIVE_H -#include "io/resource_loader.h" -#include "io/resource_saver.h" -#include "os/thread_safe.h" -#include "resource.h" +#include "core/io/resource_loader.h" +#include "core/io/resource_saver.h" +#include "core/os/thread_safe.h" +#include "core/resource.h" #include "gdnative/gdnative.h" #include "gdnative_api_struct.gen.h" -#include "io/config_file.h" +#include "core/io/config_file.h" class GDNativeLibraryResourceLoader; class GDNative; diff --git a/modules/gdnative/gdnative/array.cpp b/modules/gdnative/gdnative/array.cpp index 1fb0ff0500..a30cc09bf6 100644 --- a/modules/gdnative/gdnative/array.cpp +++ b/modules/gdnative/gdnative/array.cpp @@ -318,6 +318,38 @@ void GDAPI godot_array_destroy(godot_array *p_self) { ((Array *)p_self)->~Array(); } +godot_array GDAPI godot_array_duplicate(const godot_array *p_self, const godot_bool p_deep) { + const Array *self = (const Array *)p_self; + godot_array res; + Array *val = (Array *)&res; + memnew_placement(val, Array); + *val = self->duplicate(p_deep); + return res; +} + +godot_variant GDAPI godot_array_max(const godot_array *p_self) { + const Array *self = (const Array *)p_self; + godot_variant v; + Variant *val = (Variant *)&v; + memnew_placement(val, Variant); + *val = self->max(); + return v; +} + +godot_variant GDAPI godot_array_min(const godot_array *p_self) { + const Array *self = (const Array *)p_self; + godot_variant v; + Variant *val = (Variant *)&v; + memnew_placement(val, Variant); + *val = self->min(); + return v; +} + +void GDAPI godot_array_shuffle(godot_array *p_self) { + Array *self = (Array *)p_self; + self->shuffle(); +} + #ifdef __cplusplus } #endif diff --git a/modules/gdnative/gdnative/basis.cpp b/modules/gdnative/gdnative/basis.cpp index 372bdf3fb1..d88499ade1 100644 --- a/modules/gdnative/gdnative/basis.cpp +++ b/modules/gdnative/gdnative/basis.cpp @@ -113,6 +113,40 @@ godot_vector3 GDAPI godot_basis_get_scale(const godot_basis *p_self) { return dest; } +godot_quat GDAPI godot_basis_get_quat(const godot_basis *p_self) { + godot_quat dest; + const Basis *self = (const Basis *)p_self; + *((Quat *)&dest) = self->get_quat(); + return dest; +} + +void GDAPI godot_basis_set_quat(godot_basis *p_self, const godot_quat *p_quat) { + Basis *self = (Basis *)p_self; + const Quat *quat = (const Quat *)p_quat; + self->set_quat(*quat); +} + +void GDAPI godot_basis_set_axis_angle_scale(godot_basis *p_self, const godot_vector3 *p_axis, godot_real p_phi, const godot_vector3 *p_scale) { + Basis *self = (Basis *)p_self; + const Vector3 *axis = (const Vector3 *)p_axis; + const Vector3 *scale = (const Vector3 *)p_scale; + self->set_axis_angle_scale(*axis, p_phi, *scale); +} + +void GDAPI godot_basis_set_euler_scale(godot_basis *p_self, const godot_vector3 *p_euler, const godot_vector3 *p_scale) { + Basis *self = (Basis *)p_self; + const Vector3 *euler = (const Vector3 *)p_euler; + const Vector3 *scale = (const Vector3 *)p_scale; + self->set_euler_scale(*euler, *scale); +} + +void GDAPI godot_basis_set_quat_scale(godot_basis *p_self, const godot_quat *p_quat, const godot_vector3 *p_scale) { + Basis *self = (Basis *)p_self; + const Quat *quat = (const Quat *)p_quat; + const Vector3 *scale = (const Vector3 *)p_scale; + self->set_quat_scale(*quat, *scale); +} + godot_vector3 GDAPI godot_basis_get_euler(const godot_basis *p_self) { godot_vector3 dest; const Basis *self = (const Basis *)p_self; @@ -248,6 +282,15 @@ godot_basis GDAPI godot_basis_operator_multiply_scalar(const godot_basis *p_self return raw_dest; } +godot_basis GDAPI godot_basis_slerp(const godot_basis *p_self, const godot_basis *p_b, const godot_real p_t) { + godot_basis raw_dest; + Basis *dest = (Basis *)&raw_dest; + const Basis *self = (const Basis *)p_self; + const Basis *b = (const Basis *)p_b; + *dest = self->slerp(*b, p_t); + return raw_dest; +} + #ifdef __cplusplus } #endif diff --git a/modules/gdnative/gdnative/color.cpp b/modules/gdnative/gdnative/color.cpp index 4089f4458a..79f0c71b5e 100644 --- a/modules/gdnative/gdnative/color.cpp +++ b/modules/gdnative/gdnative/color.cpp @@ -116,6 +116,26 @@ godot_int GDAPI godot_color_to_rgba32(const godot_color *p_self) { return self->to_rgba32(); } +godot_int GDAPI godot_color_to_abgr32(const godot_color *p_self) { + const Color *self = (const Color *)p_self; + return self->to_abgr32(); +} + +godot_int GDAPI godot_color_to_abgr64(const godot_color *p_self) { + const Color *self = (const Color *)p_self; + return self->to_abgr64(); +} + +godot_int GDAPI godot_color_to_argb64(const godot_color *p_self) { + const Color *self = (const Color *)p_self; + return self->to_argb64(); +} + +godot_int GDAPI godot_color_to_rgba64(const godot_color *p_self) { + const Color *self = (const Color *)p_self; + return self->to_rgba64(); +} + godot_int GDAPI godot_color_to_argb32(const godot_color *p_self) { const Color *self = (const Color *)p_self; return self->to_argb32(); @@ -156,6 +176,27 @@ godot_color GDAPI godot_color_blend(const godot_color *p_self, const godot_color return dest; } +godot_color GDAPI godot_color_darkened(const godot_color *p_self, const godot_real p_amount) { + godot_color dest; + const Color *self = (const Color *)p_self; + *((Color *)&dest) = self->darkened(p_amount); + return dest; +} + +godot_color GDAPI godot_color_from_hsv(const godot_color *p_self, const godot_real p_h, const godot_real p_s, const godot_real p_v, const godot_real p_a) { + godot_color dest; + const Color *self = (const Color *)p_self; + *((Color *)&dest) = self->from_hsv(p_h, p_s, p_v, p_a); + return dest; +} + +godot_color GDAPI godot_color_lightened(const godot_color *p_self, const godot_real p_amount) { + godot_color dest; + const Color *self = (const Color *)p_self; + *((Color *)&dest) = self->lightened(p_amount); + return dest; +} + godot_string GDAPI godot_color_to_html(const godot_color *p_self, const godot_bool p_with_alpha) { godot_string dest; const Color *self = (const Color *)p_self; diff --git a/modules/gdnative/gdnative/dictionary.cpp b/modules/gdnative/gdnative/dictionary.cpp index 786e614158..34cc91129e 100644 --- a/modules/gdnative/gdnative/dictionary.cpp +++ b/modules/gdnative/gdnative/dictionary.cpp @@ -155,6 +155,12 @@ godot_string GDAPI godot_dictionary_to_json(const godot_dictionary *p_self) { return raw_dest; } +godot_bool GDAPI godot_dictionary_erase_with_return(godot_dictionary *p_self, const godot_variant *p_key) { + Dictionary *self = (Dictionary *)p_self; + const Variant *key = (const Variant *)p_key; + return self->erase(*key); +} + #ifdef __cplusplus } #endif diff --git a/modules/gdnative/gdnative/gdnative.cpp b/modules/gdnative/gdnative/gdnative.cpp index 041990e137..8f10f116e6 100644 --- a/modules/gdnative/gdnative/gdnative.cpp +++ b/modules/gdnative/gdnative/gdnative.cpp @@ -30,12 +30,12 @@ #include "gdnative/gdnative.h" -#include "class_db.h" -#include "engine.h" -#include "error_macros.h" -#include "global_constants.h" -#include "os/os.h" -#include "variant.h" +#include "core/class_db.h" +#include "core/engine.h" +#include "core/error_macros.h" +#include "core/global_constants.h" +#include "core/os/os.h" +#include "core/variant.h" #include "modules/gdnative/gdnative.h" @@ -166,6 +166,10 @@ void _gdnative_report_loading_error(const godot_object *p_library, const char *p _err_print_error("gdnative_init", library->get_current_library_path().utf8().ptr(), 0, message.utf8().ptr()); } +bool GDAPI godot_is_instance_valid(const godot_object *p_object) { + return ObjectDB::instance_validate((Object *)p_object); +} + #ifdef __cplusplus } #endif diff --git a/modules/gdnative/gdnative/node_path.cpp b/modules/gdnative/gdnative/node_path.cpp index f24facaae8..cf82940e09 100644 --- a/modules/gdnative/gdnative/node_path.cpp +++ b/modules/gdnative/gdnative/node_path.cpp @@ -110,6 +110,15 @@ godot_bool GDAPI godot_node_path_operator_equal(const godot_node_path *p_self, c return *self == *b; } +godot_node_path godot_node_path_get_as_property_path(const godot_node_path *p_self) { + const NodePath *self = (const NodePath *)p_self; + godot_node_path res; + NodePath *val = (NodePath *)&res; + memnew_placement(val, NodePath); + *val = self->get_as_property_path(); + return res; +} + #ifdef __cplusplus } #endif diff --git a/modules/gdnative/gdnative/pool_arrays.cpp b/modules/gdnative/gdnative/pool_arrays.cpp index 2b6b7a823a..d55d81b5b6 100644 --- a/modules/gdnative/gdnative/pool_arrays.cpp +++ b/modules/gdnative/gdnative/pool_arrays.cpp @@ -30,9 +30,9 @@ #include "gdnative/pool_arrays.h" -#include "array.h" +#include "core/array.h" +#include "core/dvector.h" #include "core/variant.h" -#include "dvector.h" #include "core/color.h" #include "core/math/vector2.h" diff --git a/modules/gdnative/gdnative/quat.cpp b/modules/gdnative/gdnative/quat.cpp index 56ff7fe3a8..2594759508 100644 --- a/modules/gdnative/gdnative/quat.cpp +++ b/modules/gdnative/gdnative/quat.cpp @@ -49,6 +49,18 @@ void GDAPI godot_quat_new_with_axis_angle(godot_quat *r_dest, const godot_vector *dest = Quat(*axis, p_angle); } +void GDAPI godot_quat_new_with_basis(godot_quat *r_dest, const godot_basis *p_basis) { + const Basis *basis = (const Basis *)p_basis; + Quat *dest = (Quat *)r_dest; + *dest = Quat(*basis); +} + +void GDAPI godot_quat_new_with_euler(godot_quat *r_dest, const godot_vector3 *p_euler) { + const Vector3 *euler = (const Vector3 *)p_euler; + Quat *dest = (Quat *)r_dest; + *dest = Quat(*euler); +} + godot_real GDAPI godot_quat_get_x(const godot_quat *p_self) { const Quat *self = (const Quat *)p_self; return self->x; @@ -213,6 +225,12 @@ godot_quat GDAPI godot_quat_operator_neg(const godot_quat *p_self) { return raw_dest; } +void GDAPI godot_quat_set_axis_angle(godot_quat *p_self, const godot_vector3 *p_axis, const godot_real p_angle) { + Quat *self = (Quat *)p_self; + const Vector3 *axis = (const Vector3 *)p_axis; + self->set_axis_angle(*axis, p_angle); +} + #ifdef __cplusplus } #endif diff --git a/modules/gdnative/gdnative/rect2.cpp b/modules/gdnative/gdnative/rect2.cpp index 54b98fc4e5..5cbc2712c3 100644 --- a/modules/gdnative/gdnative/rect2.cpp +++ b/modules/gdnative/gdnative/rect2.cpp @@ -109,6 +109,27 @@ godot_rect2 GDAPI godot_rect2_grow(const godot_rect2 *p_self, const godot_real p return dest; } +godot_rect2 GDAPI godot_rect2_grow_individual(const godot_rect2 *p_self, const godot_real p_left, const godot_real p_top, const godot_real p_right, const godot_real p_bottom) { + godot_rect2 dest; + const Rect2 *self = (const Rect2 *)p_self; + *((Rect2 *)&dest) = self->grow_individual(p_left, p_top, p_right, p_bottom); + return dest; +} + +godot_rect2 GDAPI godot_rect2_grow_margin(const godot_rect2 *p_self, const godot_int p_margin, const godot_real p_by) { + godot_rect2 dest; + const Rect2 *self = (const Rect2 *)p_self; + *((Rect2 *)&dest) = self->grow_margin((Margin)p_margin, p_by); + return dest; +} + +godot_rect2 GDAPI godot_rect2_abs(const godot_rect2 *p_self) { + godot_rect2 dest; + const Rect2 *self = (const Rect2 *)p_self; + *((Rect2 *)&dest) = self->abs(); + return dest; +} + godot_rect2 GDAPI godot_rect2_expand(const godot_rect2 *p_self, const godot_vector2 *p_to) { godot_rect2 dest; const Rect2 *self = (const Rect2 *)p_self; diff --git a/modules/gdnative/gdnative/string.cpp b/modules/gdnative/gdnative/string.cpp index 8ca57392a3..0996633b70 100644 --- a/modules/gdnative/gdnative/string.cpp +++ b/modules/gdnative/gdnative/string.cpp @@ -1285,6 +1285,64 @@ godot_bool GDAPI godot_string_is_valid_ip_address(const godot_string *p_self) { return self->is_valid_ip_address(); } +godot_string GDAPI godot_string_dedent(const godot_string *p_self) { + const String *self = (const String *)p_self; + godot_string result; + String return_value = self->dedent(); + memnew_placement(&result, String(return_value)); + + return result; +} + +godot_string GDAPI godot_string_trim_prefix(const godot_string *p_self, const godot_string *p_prefix) { + const String *self = (const String *)p_self; + String *prefix = (String *)p_prefix; + godot_string result; + String return_value = self->trim_prefix(*prefix); + memnew_placement(&result, String(return_value)); + + return result; +} + +godot_string GDAPI godot_string_trim_suffix(const godot_string *p_self, const godot_string *p_suffix) { + const String *self = (const String *)p_self; + String *suffix = (String *)p_suffix; + godot_string result; + String return_value = self->trim_suffix(*suffix); + memnew_placement(&result, String(return_value)); + + return result; +} + +godot_string GDAPI godot_string_rstrip(const godot_string *p_self, const godot_string *p_chars) { + const String *self = (const String *)p_self; + String *chars = (String *)p_chars; + godot_string result; + String return_value = self->rstrip(*chars); + memnew_placement(&result, String(return_value)); + + return result; +} + +godot_pool_string_array GDAPI godot_string_rsplit(const godot_string *p_self, const godot_string *p_divisor, + const godot_bool p_allow_empty, const godot_int p_maxsplit) { + const String *self = (const String *)p_self; + String *divisor = (String *)p_divisor; + + godot_pool_string_array result; + memnew_placement(&result, PoolStringArray); + PoolStringArray *proxy = (PoolStringArray *)&result; + PoolStringArray::Write proxy_writer = proxy->write(); + Vector<String> tmp_result = self->rsplit(*divisor, p_allow_empty, p_maxsplit); + proxy->resize(tmp_result.size()); + + for (int i = 0; i < tmp_result.size(); i++) { + proxy_writer[i] = tmp_result[i]; + } + + return result; +} + #ifdef __cplusplus } #endif diff --git a/modules/gdnative/gdnative/transform.cpp b/modules/gdnative/gdnative/transform.cpp index 715f2e3c08..ee6140c7d0 100644 --- a/modules/gdnative/gdnative/transform.cpp +++ b/modules/gdnative/gdnative/transform.cpp @@ -56,6 +56,12 @@ void GDAPI godot_transform_new(godot_transform *r_dest, const godot_basis *p_bas *dest = Transform(*basis, *origin); } +void GDAPI godot_transform_new_with_quat(godot_transform *r_dest, const godot_quat *p_quat) { + const Quat *quat = (const Quat *)p_quat; + Transform *dest = (Transform *)r_dest; + *dest = Transform(*quat); +} + godot_basis GDAPI godot_transform_get_basis(const godot_transform *p_self) { godot_basis dest; const Transform *self = (const Transform *)p_self; diff --git a/modules/gdnative/gdnative/variant.cpp b/modules/gdnative/gdnative/variant.cpp index 423f3312e1..fd6babfc3a 100644 --- a/modules/gdnative/gdnative/variant.cpp +++ b/modules/gdnative/gdnative/variant.cpp @@ -489,6 +489,24 @@ void GDAPI godot_variant_destroy(godot_variant *p_self) { self->~Variant(); } +// GDNative core 1.1 + +godot_string GDAPI godot_variant_get_operator_name(godot_variant_operator p_op) { + Variant::Operator op = (Variant::Operator)p_op; + godot_string raw_dest; + String *dest = (String *)&raw_dest; + memnew_placement(dest, String(Variant::get_operator_name(op))); // operator = is overloaded by String + return raw_dest; +} + +void GDAPI godot_variant_evaluate(godot_variant_operator p_op, const godot_variant *p_a, const godot_variant *p_b, godot_variant *r_ret, godot_bool *r_valid) { + Variant::Operator op = (Variant::Operator)p_op; + const Variant *a = (const Variant *)p_a; + const Variant *b = (const Variant *)p_b; + Variant *ret = (Variant *)r_ret; + Variant::evaluate(op, a, b, *ret, *r_valid); +} + #ifdef __cplusplus } #endif diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json index e326d11a84..c5a1fa139e 100644 --- a/modules/gdnative/gdnative_api.json +++ b/modules/gdnative/gdnative_api.json @@ -5,7 +5,293 @@ "major": 1, "minor": 0 }, - "next": null, + "next": { + "type": "CORE", + "version": { + "major": 1, + "minor": 1 + }, + "next": null, + "api": [ + { + "name": "godot_color_to_abgr32", + "return_type": "godot_int", + "arguments": [ + ["const godot_color *", "p_self"] + ] + }, + { + "name": "godot_color_to_abgr64", + "return_type": "godot_int", + "arguments": [ + ["const godot_color *", "p_self"] + ] + }, + { + "name": "godot_color_to_argb64", + "return_type": "godot_int", + "arguments": [ + ["const godot_color *", "p_self"] + ] + }, + { + "name": "godot_color_to_rgba64", + "return_type": "godot_int", + "arguments": [ + ["const godot_color *", "p_self"] + ] + }, + { + "name": "godot_color_darkened", + "return_type": "godot_color", + "arguments": [ + ["const godot_color *", "p_self"], + ["const godot_real", "p_amount"] + ] + }, + { + "name": "godot_color_from_hsv", + "return_type": "godot_color", + "arguments": [ + ["const godot_color *", "p_self"], + ["const godot_real", "p_h"], + ["const godot_real", "p_s"], + ["const godot_real", "p_v"], + ["const godot_real", "p_a"] + ] + }, + { + "name": "godot_color_lightened", + "return_type": "godot_color", + "arguments": [ + ["const godot_color *", "p_self"], + ["const godot_real", "p_amount"] + ] + }, + { + "name": "godot_array_duplicate", + "return_type": "godot_array", + "arguments": [ + ["const godot_array *", "p_self"], + ["const godot_bool", "p_deep"] + ] + }, + { + "name": "godot_array_max", + "return_type": "godot_variant", + "arguments": [ + ["const godot_array *", "p_self"] + ] + }, + { + "name": "godot_array_min", + "return_type": "godot_variant", + "arguments": [ + ["const godot_array *", "p_self"] + ] + }, + { + "name": "godot_array_shuffle", + "return_type": "void", + "arguments": [ + ["godot_array *", "p_self"] + ] + }, + { + "name": "godot_basis_slerp", + "return_type": "godot_basis", + "arguments": [ + ["const godot_basis *", "p_self"], + ["const godot_basis *", "p_b"], + ["const godot_real", "p_t"] + ] + }, + { + "name": "godot_node_path_get_as_property_path", + "return_type": "godot_node_path", + "arguments": [ + ["const godot_node_path *", "p_self"] + ] + }, + { + "name": "godot_quat_set_axis_angle", + "return_type": "void", + "arguments": [ + ["godot_quat *", "p_self"], + ["const godot_vector3 *", "p_axis"], + ["const godot_real", "p_angle"] + ] + }, + { + "name": "godot_rect2_grow_individual", + "return_type": "godot_rect2", + "arguments": [ + ["const godot_rect2 *", "p_self"], + ["const godot_real", "p_left"], + ["const godot_real", "p_top"], + ["const godot_real", "p_right"], + ["const godot_real", "p_bottom"] + ] + }, + { + "name": "godot_rect2_grow_margin", + "return_type": "godot_rect2", + "arguments": [ + ["const godot_rect2 *", "p_self"], + ["const godot_int", "p_margin"], + ["const godot_real", "p_by"] + ] + }, + { + "name": "godot_rect2_abs", + "return_type": "godot_rect2", + "arguments": [ + ["const godot_rect2 *", "p_self"] + ] + }, + { + "name": "godot_string_dedent", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_trim_prefix", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_prefix"] + ] + }, + { + "name": "godot_string_trim_suffix", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_suffix"] + ] + }, + { + "name": "godot_string_rstrip", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_chars"] + ] + }, + { + "name": "godot_string_rsplit", + "return_type": "godot_pool_string_array", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_divisor"], + ["const godot_bool", "p_allow_empty"], + ["const godot_int", "p_maxsplit"] + ] + }, + { + "name": "godot_basis_get_quat", + "return_type": "godot_quat", + "arguments": [ + ["const godot_basis *", "p_self"] + ] + }, + { + "name": "godot_basis_set_quat", + "return_type": "void", + "arguments": [ + ["godot_basis *", "p_self"], + ["const godot_quat *", "p_quat"] + ] + }, + { + "name": "godot_basis_set_axis_angle_scale", + "return_type": "void", + "arguments": [ + ["godot_basis *", "p_self"], + ["const godot_vector3 *", "p_axis"], + ["godot_real", "p_phi"], + ["const godot_vector3 *", "p_scale"] + ] + }, + { + "name": "godot_basis_set_euler_scale", + "return_type": "void", + "arguments": [ + ["godot_basis *", "p_self"], + ["const godot_vector3 *", "p_euler"], + ["const godot_vector3 *", "p_scale"] + ] + }, + { + "name": "godot_basis_set_quat_scale", + "return_type": "void", + "arguments": [ + ["godot_basis *", "p_self"], + ["const godot_quat *", "p_quat"], + ["const godot_vector3 *", "p_scale"] + ] + }, + { + "name": "godot_dictionary_erase_with_return", + "return_type": "bool", + "arguments": [ + ["godot_dictionary *", "p_self"], + ["const godot_variant *", "p_key"] + ] + }, + { + "name": "godot_is_instance_valid", + "return_type": "bool", + "arguments": [ + ["const godot_object *", "p_object"] + ] + }, + { + "name": "godot_quat_new_with_basis", + "return_type": "void", + "arguments": [ + ["godot_quat *", "r_dest"], + ["const godot_basis *", "p_basis"] + ] + }, + { + "name": "godot_quat_new_with_euler", + "return_type": "void", + "arguments": [ + ["godot_quat *", "r_dest"], + ["const godot_vector3 *", "p_euler"] + ] + }, + { + "name": "godot_transform_new_with_quat", + "return_type": "void", + "arguments": [ + ["godot_transform *", "r_dest"], + ["const godot_quat *", "p_quat"] + ] + }, + { + "name": "godot_variant_get_operator_name", + "return_type": "godot_string", + "arguments": [ + ["godot_variant_operator", "p_op"] + ] + }, + { + "name": "godot_variant_evaluate", + "return_type": "void", + "arguments": [ + ["godot_variant_operator", "p_op"], + ["const godot_variant *", "p_a"], + ["const godot_variant *", "p_b"], + ["godot_variant *", "r_ret"], + ["godot_bool *", "r_valid"] + ] + } + ] + }, "api": [ { "name": "godot_color_new_rgba", @@ -4484,7 +4770,7 @@ ] }, { - "name": "godot_string_wide_str", + "name": "godot_string_wide_str", "return_type": "const wchar_t *", "arguments": [ ["const godot_string *", "p_self"] @@ -5253,21 +5539,21 @@ "name": "godot_string_ascii", "return_type": "godot_char_string", "arguments": [ - ["const godot_string *", "p_self"] + ["const godot_string *", "p_self"] ] }, { "name": "godot_string_ascii_extended", "return_type": "godot_char_string", "arguments": [ - ["const godot_string *", "p_self"] + ["const godot_string *", "p_self"] ] }, { "name": "godot_string_utf8", "return_type": "godot_char_string", "arguments": [ - ["const godot_string *", "p_self"] + ["const godot_string *", "p_self"] ] }, { @@ -5765,15 +6051,15 @@ "minor": 0 }, "next": { - "type": "NATIVESCRIPT", - "version": { - "major": 1, - "minor": 1 - }, - "next": null, - "api": [ + "type": "NATIVESCRIPT", + "version": { + "major": 1, + "minor": 1 + }, + "next": null, + "api": [ { - "name": "godot_nativescript_set_method_argument_information", + "name": "godot_nativescript_set_method_argument_information", "return_type": "void", "arguments": [ ["void *", "p_gdnative_handle"], @@ -5784,7 +6070,7 @@ ] }, { - "name": "godot_nativescript_set_class_documentation", + "name": "godot_nativescript_set_class_documentation", "return_type": "void", "arguments": [ ["void *", "p_gdnative_handle"], @@ -5793,7 +6079,7 @@ ] }, { - "name": "godot_nativescript_set_method_documentation", + "name": "godot_nativescript_set_method_documentation", "return_type": "void", "arguments": [ ["void *", "p_gdnative_handle"], @@ -5803,7 +6089,7 @@ ] }, { - "name": "godot_nativescript_set_property_documentation", + "name": "godot_nativescript_set_property_documentation", "return_type": "void", "arguments": [ ["void *", "p_gdnative_handle"], @@ -5813,7 +6099,7 @@ ] }, { - "name": "godot_nativescript_set_signal_documentation", + "name": "godot_nativescript_set_signal_documentation", "return_type": "void", "arguments": [ ["void *", "p_gdnative_handle"], @@ -5874,7 +6160,7 @@ "return_type": "void *", "arguments": [ ["int", "p_idx"], - ["godot_object *", "p_object"] + ["godot_object *", "p_object"] ] }, { @@ -5885,7 +6171,7 @@ ["uint64_t", "p_line"] ] } - ] + ] }, "api": [ { diff --git a/modules/gdnative/gdnative_builders.py b/modules/gdnative/gdnative_builders.py index 8a1cd049af..f9d1ed9dc5 100644 --- a/modules/gdnative/gdnative_builders.py +++ b/modules/gdnative/gdnative_builders.py @@ -82,10 +82,35 @@ def _build_gdnative_api_struct_header(api): return ret_val + + def generate_core_extension_struct(core): + ret_val = [] + if core['next']: + ret_val += generate_core_extension_struct(core['next']) + + ret_val += [ + 'typedef struct godot_gdnative_core_' + ('{0}_{1}'.format(core['version']['major'], core['version']['minor'])) + '_api_struct {', + '\tunsigned int type;', + '\tgodot_gdnative_api_version version;', + '\tconst godot_gdnative_api_struct *next;', + ] + + for funcdef in core['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_core_' + '{0}_{1}'.format(core['version']['major'], core['version']['minor']) + '_api_struct;', ''] + + return ret_val + + for ext in api['extensions']: name = ext['name'] out += generate_extension_struct(name, ext, False) + if api['core']['next']: + out += generate_core_extension_struct(api['core']['next']) + out += [ 'typedef struct godot_gdnative_core_api_struct {', '\tunsigned int type;', @@ -146,6 +171,27 @@ def _build_gdnative_api_struct_source(api): ret_val += ['};\n'] return ret_val + + + def get_core_struct_definition(core): + ret_val = [] + + if core['next']: + ret_val += get_core_struct_definition(core['next']) + + ret_val += [ + 'extern const godot_gdnative_core_' + ('{0}_{1}_api_struct api_{0}_{1}'.format(core['version']['major'], core['version']['minor'])) + ' = {', + '\tGDNATIVE_' + core['type'] + ',', + '\t{' + str(core['version']['major']) + ', ' + str(core['version']['minor']) + '},', + '\t' + ('NULL' if not core['next'] else ('(const godot_gdnative_api_struct *)& api_{0}_{1}'.format(core['version']['major'], core['version']['minor']))) + ',' + ] + + for funcdef in core['api']: + ret_val.append('\t%s,' % funcdef['name']) + + ret_val += ['};\n'] + + return ret_val for ext in api['extensions']: name = ext['name'] @@ -158,6 +204,9 @@ def _build_gdnative_api_struct_source(api): out += ['\t(godot_gdnative_api_struct *)&api_extension_' + name + '_struct,'] out += ['};\n'] + + if api['core']['next']: + out += get_core_struct_definition(api['core']['next']) out += [ 'extern const godot_gdnative_core_api_struct api_struct = {', diff --git a/modules/gdnative/include/gdnative/array.h b/modules/gdnative/include/gdnative/array.h index 1e66d133b9..876b8f8e8f 100644 --- a/modules/gdnative/include/gdnative/array.h +++ b/modules/gdnative/include/gdnative/array.h @@ -130,6 +130,14 @@ godot_int GDAPI godot_array_bsearch_custom(godot_array *p_self, const godot_vari void GDAPI godot_array_destroy(godot_array *p_self); +godot_array GDAPI godot_array_duplicate(const godot_array *p_self, const godot_bool p_deep); + +godot_variant GDAPI godot_array_max(const godot_array *p_self); + +godot_variant GDAPI godot_array_min(const godot_array *p_self); + +void GDAPI godot_array_shuffle(godot_array *p_self); + #ifdef __cplusplus } #endif diff --git a/modules/gdnative/include/gdnative/basis.h b/modules/gdnative/include/gdnative/basis.h index 53e950b4a2..6128bf3ac3 100644 --- a/modules/gdnative/include/gdnative/basis.h +++ b/modules/gdnative/include/gdnative/basis.h @@ -62,6 +62,7 @@ extern "C" { void GDAPI godot_basis_new_with_rows(godot_basis *r_dest, const godot_vector3 *p_x_axis, const godot_vector3 *p_y_axis, const godot_vector3 *p_z_axis); void GDAPI godot_basis_new_with_axis_and_angle(godot_basis *r_dest, const godot_vector3 *p_axis, const godot_real p_phi); void GDAPI godot_basis_new_with_euler(godot_basis *r_dest, const godot_vector3 *p_euler); +void GDAPI godot_basis_new_with_euler_quat(godot_basis *r_dest, const godot_quat *p_euler); godot_string GDAPI godot_basis_as_string(const godot_basis *p_self); @@ -81,6 +82,16 @@ godot_vector3 GDAPI godot_basis_get_scale(const godot_basis *p_self); godot_vector3 GDAPI godot_basis_get_euler(const godot_basis *p_self); +godot_quat GDAPI godot_basis_get_quat(const godot_basis *p_self); + +void GDAPI godot_basis_set_quat(godot_basis *p_self, const godot_quat *p_quat); + +void GDAPI godot_basis_set_axis_angle_scale(godot_basis *p_self, const godot_vector3 *p_axis, godot_real p_phi, const godot_vector3 *p_scale); + +void GDAPI godot_basis_set_euler_scale(godot_basis *p_self, const godot_vector3 *p_euler, const godot_vector3 *p_scale); + +void GDAPI godot_basis_set_quat_scale(godot_basis *p_self, const godot_quat *p_quat, const godot_vector3 *p_scale); + godot_real GDAPI godot_basis_tdotx(const godot_basis *p_self, const godot_vector3 *p_with); godot_real GDAPI godot_basis_tdoty(const godot_basis *p_self, const godot_vector3 *p_with); @@ -95,8 +106,6 @@ godot_int GDAPI godot_basis_get_orthogonal_index(const godot_basis *p_self); void GDAPI godot_basis_new(godot_basis *r_dest); -void GDAPI godot_basis_new_with_euler_quat(godot_basis *r_dest, const godot_quat *p_euler); - // p_elements is a pointer to an array of 3 (!!) vector3 void GDAPI godot_basis_get_elements(const godot_basis *p_self, godot_vector3 *p_elements); @@ -118,6 +127,8 @@ godot_basis GDAPI godot_basis_operator_multiply_vector(const godot_basis *p_self godot_basis GDAPI godot_basis_operator_multiply_scalar(const godot_basis *p_self, const godot_real p_b); +godot_basis GDAPI godot_basis_slerp(const godot_basis *p_self, const godot_basis *p_b, const godot_real p_t); + #ifdef __cplusplus } #endif diff --git a/modules/gdnative/include/gdnative/color.h b/modules/gdnative/include/gdnative/color.h index 1f0ac8354d..3007dbc6e3 100644 --- a/modules/gdnative/include/gdnative/color.h +++ b/modules/gdnative/include/gdnative/color.h @@ -81,6 +81,14 @@ godot_string GDAPI godot_color_as_string(const godot_color *p_self); godot_int GDAPI godot_color_to_rgba32(const godot_color *p_self); +godot_int GDAPI godot_color_to_abgr32(const godot_color *p_self); + +godot_int GDAPI godot_color_to_abgr64(const godot_color *p_self); + +godot_int GDAPI godot_color_to_argb64(const godot_color *p_self); + +godot_int GDAPI godot_color_to_rgba64(const godot_color *p_self); + godot_int GDAPI godot_color_to_argb32(const godot_color *p_self); godot_real GDAPI godot_color_gray(const godot_color *p_self); @@ -93,6 +101,12 @@ godot_color GDAPI godot_color_linear_interpolate(const godot_color *p_self, cons godot_color GDAPI godot_color_blend(const godot_color *p_self, const godot_color *p_over); +godot_color GDAPI godot_color_darkened(const godot_color *p_self, const godot_real p_amount); + +godot_color GDAPI godot_color_from_hsv(const godot_color *p_self, const godot_real p_h, const godot_real p_s, const godot_real p_v, const godot_real p_a); + +godot_color GDAPI godot_color_lightened(const godot_color *p_self, const godot_real p_amount); + godot_string GDAPI godot_color_to_html(const godot_color *p_self, const godot_bool p_with_alpha); godot_bool GDAPI godot_color_operator_equal(const godot_color *p_self, const godot_color *p_b); diff --git a/modules/gdnative/include/gdnative/dictionary.h b/modules/gdnative/include/gdnative/dictionary.h index a86d60dc72..faace818ee 100644 --- a/modules/gdnative/include/gdnative/dictionary.h +++ b/modules/gdnative/include/gdnative/dictionary.h @@ -94,6 +94,8 @@ godot_bool GDAPI godot_dictionary_operator_equal(const godot_dictionary *p_self, godot_string GDAPI godot_dictionary_to_json(const godot_dictionary *p_self); +godot_bool GDAPI godot_dictionary_erase_with_return(godot_dictionary *p_self, const godot_variant *p_key); + #ifdef __cplusplus } #endif diff --git a/modules/gdnative/include/gdnative/gdnative.h b/modules/gdnative/include/gdnative/gdnative.h index 4cf6e99b06..796ced84f4 100644 --- a/modules/gdnative/include/gdnative/gdnative.h +++ b/modules/gdnative/include/gdnative/gdnative.h @@ -35,7 +35,7 @@ extern "C" { #endif -#ifdef _WIN32 +#if defined(_WIN32) || defined(__ANDROID__) #define GDCALLINGCONV #define GDAPI GDCALLINGCONV #elif defined(__APPLE__) @@ -47,7 +47,7 @@ extern "C" { #define GDCALLINGCONV __attribute__((sysv_abi)) #define GDAPI GDCALLINGCONV #endif -#else +#else // !_WIN32 && !__APPLE__ #define GDCALLINGCONV __attribute__((sysv_abi)) #define GDAPI GDCALLINGCONV #endif @@ -282,6 +282,10 @@ void GDAPI godot_print_error(const char *p_description, const char *p_function, void GDAPI godot_print_warning(const char *p_description, const char *p_function, const char *p_file, int p_line); void GDAPI godot_print(const godot_string *p_message); +// GDNATIVE CORE 1.0.1 + +bool GDAPI godot_is_instance_valid(const godot_object *p_object); + #ifdef __cplusplus } #endif diff --git a/modules/gdnative/include/gdnative/node_path.h b/modules/gdnative/include/gdnative/node_path.h index 2b55e01d13..48fe5b4d3d 100644 --- a/modules/gdnative/include/gdnative/node_path.h +++ b/modules/gdnative/include/gdnative/node_path.h @@ -80,6 +80,8 @@ godot_bool GDAPI godot_node_path_is_empty(const godot_node_path *p_self); godot_bool GDAPI godot_node_path_operator_equal(const godot_node_path *p_self, const godot_node_path *p_b); +godot_node_path godot_node_path_get_as_property_path(const godot_node_path *p_self); + #ifdef __cplusplus } #endif diff --git a/modules/gdnative/include/gdnative/quat.h b/modules/gdnative/include/gdnative/quat.h index 4e86960aaf..634f486e66 100644 --- a/modules/gdnative/include/gdnative/quat.h +++ b/modules/gdnative/include/gdnative/quat.h @@ -60,6 +60,8 @@ extern "C" { void GDAPI godot_quat_new(godot_quat *r_dest, const godot_real p_x, const godot_real p_y, const godot_real p_z, const godot_real p_w); void GDAPI godot_quat_new_with_axis_angle(godot_quat *r_dest, const godot_vector3 *p_axis, const godot_real p_angle); +void GDAPI godot_quat_new_with_basis(godot_quat *r_dest, const godot_basis *p_basis); +void GDAPI godot_quat_new_with_euler(godot_quat *r_dest, const godot_vector3 *p_euler); godot_real GDAPI godot_quat_get_x(const godot_quat *p_self); void GDAPI godot_quat_set_x(godot_quat *p_self, const godot_real val); @@ -107,6 +109,8 @@ godot_bool GDAPI godot_quat_operator_equal(const godot_quat *p_self, const godot godot_quat GDAPI godot_quat_operator_neg(const godot_quat *p_self); +void GDAPI godot_quat_set_axis_angle(godot_quat *p_self, const godot_vector3 *p_axis, const godot_real p_angle); + #ifdef __cplusplus } #endif diff --git a/modules/gdnative/include/gdnative/rect2.h b/modules/gdnative/include/gdnative/rect2.h index 4adcb73e3d..47c15c80bd 100644 --- a/modules/gdnative/include/gdnative/rect2.h +++ b/modules/gdnative/include/gdnative/rect2.h @@ -77,6 +77,12 @@ godot_bool GDAPI godot_rect2_has_point(const godot_rect2 *p_self, const godot_ve godot_rect2 GDAPI godot_rect2_grow(const godot_rect2 *p_self, const godot_real p_by); +godot_rect2 GDAPI godot_rect2_grow_individual(const godot_rect2 *p_self, const godot_real p_left, const godot_real p_top, const godot_real p_right, const godot_real p_bottom); + +godot_rect2 GDAPI godot_rect2_grow_margin(const godot_rect2 *p_self, const godot_int p_margin, const godot_real p_by); + +godot_rect2 GDAPI godot_rect2_abs(const godot_rect2 *p_self); + godot_rect2 GDAPI godot_rect2_expand(const godot_rect2 *p_self, const godot_vector2 *p_to); godot_bool GDAPI godot_rect2_operator_equal(const godot_rect2 *p_self, const godot_rect2 *p_b); diff --git a/modules/gdnative/include/gdnative/string.h b/modules/gdnative/include/gdnative/string.h index 73245160c1..95ae42a9ec 100644 --- a/modules/gdnative/include/gdnative/string.h +++ b/modules/gdnative/include/gdnative/string.h @@ -246,6 +246,12 @@ godot_bool GDAPI godot_string_is_valid_identifier(const godot_string *p_self); godot_bool GDAPI godot_string_is_valid_integer(const godot_string *p_self); godot_bool GDAPI godot_string_is_valid_ip_address(const godot_string *p_self); +godot_string GDAPI godot_string_dedent(const godot_string *p_self); +godot_string GDAPI godot_string_trim_prefix(const godot_string *p_self, const godot_string *p_prefix); +godot_string GDAPI godot_string_trim_suffix(const godot_string *p_self, const godot_string *p_suffix); +godot_string GDAPI godot_string_rstrip(const godot_string *p_self, const godot_string *p_chars); +godot_pool_string_array GDAPI godot_string_rsplit(const godot_string *p_self, const godot_string *p_divisor, const godot_bool p_allow_empty, const godot_int p_maxsplit); + void GDAPI godot_string_destroy(godot_string *p_self); #ifdef __cplusplus diff --git a/modules/gdnative/include/gdnative/transform.h b/modules/gdnative/include/gdnative/transform.h index a646da146a..880f21c88a 100644 --- a/modules/gdnative/include/gdnative/transform.h +++ b/modules/gdnative/include/gdnative/transform.h @@ -62,6 +62,7 @@ extern "C" { void GDAPI godot_transform_new_with_axis_origin(godot_transform *r_dest, const godot_vector3 *p_x_axis, const godot_vector3 *p_y_axis, const godot_vector3 *p_z_axis, const godot_vector3 *p_origin); void GDAPI godot_transform_new(godot_transform *r_dest, const godot_basis *p_basis, const godot_vector3 *p_origin); +void GDAPI godot_transform_new_with_quat(godot_transform *r_dest, const godot_quat *p_quat); godot_basis GDAPI godot_transform_get_basis(const godot_transform *p_self); void GDAPI godot_transform_set_basis(godot_transform *p_self, const godot_basis *p_v); diff --git a/modules/gdnative/include/gdnative/variant.h b/modules/gdnative/include/gdnative/variant.h index 6779dc4092..5e71aa9f11 100644 --- a/modules/gdnative/include/gdnative/variant.h +++ b/modules/gdnative/include/gdnative/variant.h @@ -100,6 +100,45 @@ typedef struct godot_variant_call_error { godot_variant_type expected; } godot_variant_call_error; +typedef enum godot_variant_operator { + // comparison + GODOT_VARIANT_OP_EQUAL, + GODOT_VARIANT_OP_NOT_EQUAL, + GODOT_VARIANT_OP_LESS, + GODOT_VARIANT_OP_LESS_EQUAL, + GODOT_VARIANT_OP_GREATER, + GODOT_VARIANT_OP_GREATER_EQUAL, + + // mathematic + GODOT_VARIANT_OP_ADD, + GODOT_VARIANT_OP_SUBTRACT, + GODOT_VARIANT_OP_MULTIPLY, + GODOT_VARIANT_OP_DIVIDE, + GODOT_VARIANT_OP_NEGATE, + GODOT_VARIANT_OP_POSITIVE, + GODOT_VARIANT_OP_MODULE, + GODOT_VARIANT_OP_STRING_CONCAT, + + // bitwise + GODOT_VARIANT_OP_SHIFT_LEFT, + GODOT_VARIANT_OP_SHIFT_RIGHT, + GODOT_VARIANT_OP_BIT_AND, + GODOT_VARIANT_OP_BIT_OR, + GODOT_VARIANT_OP_BIT_XOR, + GODOT_VARIANT_OP_BIT_NEGATE, + + // logic + GODOT_VARIANT_OP_AND, + GODOT_VARIANT_OP_OR, + GODOT_VARIANT_OP_XOR, + GODOT_VARIANT_OP_NOT, + + // containment + GODOT_VARIANT_OP_IN, + + GODOT_VARIANT_OP_MAX, +} godot_variant_operator; + // reduce extern "C" nesting for VS2013 #ifdef __cplusplus } @@ -204,6 +243,11 @@ godot_bool GDAPI godot_variant_booleanize(const godot_variant *p_self); void GDAPI godot_variant_destroy(godot_variant *p_self); +// GDNative core 1.1 + +godot_string GDAPI godot_variant_get_operator_name(godot_variant_operator p_op); +void GDAPI godot_variant_evaluate(godot_variant_operator p_op, const godot_variant *p_a, const godot_variant *p_b, godot_variant *r_ret, godot_bool *r_valid); + #ifdef __cplusplus } #endif diff --git a/modules/gdnative/include/nativescript/godot_nativescript.h b/modules/gdnative/include/nativescript/godot_nativescript.h index 29bd9eec5a..ba044117e2 100644 --- a/modules/gdnative/include/nativescript/godot_nativescript.h +++ b/modules/gdnative/include/nativescript/godot_nativescript.h @@ -40,12 +40,13 @@ extern "C" { typedef enum { GODOT_METHOD_RPC_MODE_DISABLED, GODOT_METHOD_RPC_MODE_REMOTE, - GODOT_METHOD_RPC_MODE_SYNC, GODOT_METHOD_RPC_MODE_MASTER, - GODOT_METHOD_RPC_MODE_SLAVE, + GODOT_METHOD_RPC_MODE_PUPPET, + GODOT_METHOD_RPC_MODE_SLAVE = GODOT_METHOD_RPC_MODE_PUPPET, GODOT_METHOD_RPC_MODE_REMOTESYNC, + GODOT_METHOD_RPC_MODE_SYNC = GODOT_METHOD_RPC_MODE_REMOTESYNC, GODOT_METHOD_RPC_MODE_MASTERSYNC, - GODOT_METHOD_RPC_MODE_SLAVESYNC, + GODOT_METHOD_RPC_MODE_PUPPETSYNC, } godot_method_rpc_mode; typedef enum { @@ -229,6 +230,8 @@ const void GDAPI *godot_nativescript_get_type_tag(const godot_object *p_object); typedef struct { GDCALLINGCONV void *(*alloc_instance_binding_data)(void *, const void *, godot_object *); GDCALLINGCONV void (*free_instance_binding_data)(void *, void *); + GDCALLINGCONV void (*refcount_incremented_instance_binding)(void *, godot_object *); + GDCALLINGCONV bool (*refcount_decremented_instance_binding)(void *, godot_object *); void *data; GDCALLINGCONV void (*free_func)(void *); } godot_instance_binding_functions; diff --git a/modules/gdnative/nativescript/SCsub b/modules/gdnative/nativescript/SCsub index ee3b9c351d..5841ad5531 100644 --- a/modules/gdnative/nativescript/SCsub +++ b/modules/gdnative/nativescript/SCsub @@ -1,12 +1,10 @@ #!/usr/bin/env python Import('env') +Import('env_gdnative') -mod_env = env.Clone() -mod_env.add_source_files(env.modules_sources, "*.cpp") -mod_env.Append(CPPFLAGS=['-DGDAPI_BUILT_IN']) +env_gdnative.add_source_files(env.modules_sources, '*.cpp') +env_gdnative.Append(CPPFLAGS=['-DGDAPI_BUILT_IN']) if "platform" in env and env["platform"] in ["x11", "iphone"]: env.Append(LINKFLAGS=["-rdynamic"]) - -Export('mod_env') diff --git a/modules/gdnative/nativescript/api_generator.cpp b/modules/gdnative/nativescript/api_generator.cpp index 70ca8d68b8..8c6dace847 100644 --- a/modules/gdnative/nativescript/api_generator.cpp +++ b/modules/gdnative/nativescript/api_generator.cpp @@ -35,8 +35,8 @@ #include "core/class_db.h" #include "core/engine.h" #include "core/global_constants.h" +#include "core/os/file_access.h" #include "core/pair.h" -#include "os/file_access.h" // helper stuff @@ -292,6 +292,7 @@ List<ClassAPI> generate_c_api_classes() { method_api.has_varargs = method_bind && method_bind->is_vararg(); // Method flags + method_api.is_virtual = false; if (method_info.flags) { const uint32_t flags = method_info.flags; method_api.is_editor = flags & METHOD_FLAG_EDITOR; diff --git a/modules/gdnative/nativescript/godot_nativescript.cpp b/modules/gdnative/nativescript/godot_nativescript.cpp index 72606c8340..c39efe126f 100644 --- a/modules/gdnative/nativescript/godot_nativescript.cpp +++ b/modules/gdnative/nativescript/godot_nativescript.cpp @@ -30,12 +30,12 @@ #include "nativescript/godot_nativescript.h" -#include "class_db.h" -#include "error_macros.h" +#include "core/class_db.h" +#include "core/error_macros.h" +#include "core/global_constants.h" +#include "core/project_settings.h" +#include "core/variant.h" #include "gdnative/gdnative.h" -#include "global_constants.h" -#include "project_settings.h" -#include "variant.h" #include "nativescript.h" diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp index 608c7aa4a5..3e56275396 100644 --- a/modules/gdnative/nativescript/nativescript.cpp +++ b/modules/gdnative/nativescript/nativescript.cpp @@ -33,10 +33,10 @@ #include "gdnative/gdnative.h" #include "core/global_constants.h" +#include "core/io/file_access_encrypted.h" +#include "core/os/file_access.h" +#include "core/os/os.h" #include "core/project_settings.h" -#include "io/file_access_encrypted.h" -#include "os/file_access.h" -#include "os/os.h" #include "scene/main/scene_tree.h" #include "scene/resources/scene_format_text.h" @@ -44,7 +44,7 @@ #include <stdlib.h> #ifndef NO_THREADS -#include "os/thread.h" +#include "core/os/thread.h" #endif #if defined(TOOLS_ENABLED) && defined(DEBUG_METHODS_ENABLED) @@ -810,18 +810,16 @@ MultiplayerAPI::RPCMode NativeScriptInstance::get_rpc_mode(const StringName &p_m return MultiplayerAPI::RPC_MODE_DISABLED; case GODOT_METHOD_RPC_MODE_REMOTE: return MultiplayerAPI::RPC_MODE_REMOTE; - case GODOT_METHOD_RPC_MODE_SYNC: - return MultiplayerAPI::RPC_MODE_SYNC; case GODOT_METHOD_RPC_MODE_MASTER: return MultiplayerAPI::RPC_MODE_MASTER; - case GODOT_METHOD_RPC_MODE_SLAVE: - return MultiplayerAPI::RPC_MODE_SLAVE; + case GODOT_METHOD_RPC_MODE_PUPPET: + return MultiplayerAPI::RPC_MODE_PUPPET; case GODOT_METHOD_RPC_MODE_REMOTESYNC: return MultiplayerAPI::RPC_MODE_REMOTESYNC; case GODOT_METHOD_RPC_MODE_MASTERSYNC: return MultiplayerAPI::RPC_MODE_MASTERSYNC; - case GODOT_METHOD_RPC_MODE_SLAVESYNC: - return MultiplayerAPI::RPC_MODE_SLAVESYNC; + case GODOT_METHOD_RPC_MODE_PUPPETSYNC: + return MultiplayerAPI::RPC_MODE_PUPPETSYNC; default: return MultiplayerAPI::RPC_MODE_DISABLED; } @@ -846,12 +844,16 @@ MultiplayerAPI::RPCMode NativeScriptInstance::get_rset_mode(const StringName &p_ return MultiplayerAPI::RPC_MODE_DISABLED; case GODOT_METHOD_RPC_MODE_REMOTE: return MultiplayerAPI::RPC_MODE_REMOTE; - case GODOT_METHOD_RPC_MODE_SYNC: - return MultiplayerAPI::RPC_MODE_SYNC; case GODOT_METHOD_RPC_MODE_MASTER: return MultiplayerAPI::RPC_MODE_MASTER; - case GODOT_METHOD_RPC_MODE_SLAVE: - return MultiplayerAPI::RPC_MODE_SLAVE; + case GODOT_METHOD_RPC_MODE_PUPPET: + return MultiplayerAPI::RPC_MODE_PUPPET; + case GODOT_METHOD_RPC_MODE_REMOTESYNC: + return MultiplayerAPI::RPC_MODE_REMOTESYNC; + case GODOT_METHOD_RPC_MODE_MASTERSYNC: + return MultiplayerAPI::RPC_MODE_MASTERSYNC; + case GODOT_METHOD_RPC_MODE_PUPPETSYNC: + return MultiplayerAPI::RPC_MODE_PUPPETSYNC; default: return MultiplayerAPI::RPC_MODE_DISABLED; } @@ -1014,6 +1016,16 @@ NativeScriptLanguage::NativeScriptLanguage() { #ifdef DEBUG_ENABLED profiling = false; #endif + + _init_call_type = "nativescript_init"; + _init_call_name = "nativescript_init"; + _terminate_call_name = "nativescript_terminate"; + _noarg_call_type = "nativescript_no_arg"; + _frame_call_name = "nativescript_frame"; +#ifndef NO_THREADS + _thread_enter_call_name = "nativescript_thread_enter"; + _thread_exit_call_name = "nativescript_thread_exit"; +#endif } NativeScriptLanguage::~NativeScriptLanguage() { @@ -1370,6 +1382,54 @@ void NativeScriptLanguage::free_instance_binding_data(void *p_data) { delete &binding_data; } +void NativeScriptLanguage::refcount_incremented_instance_binding(Object *p_object) { + + void *data = p_object->get_script_instance_binding(lang_idx); + + if (!data) + return; + + Vector<void *> &binding_data = *(Vector<void *> *)data; + + for (int i = 0; i < binding_data.size(); i++) { + if (!binding_data[i]) + continue; + + if (!binding_functions[i].first) + continue; + + if (binding_functions[i].second.refcount_incremented_instance_binding) { + binding_functions[i].second.refcount_incremented_instance_binding(binding_data[i], p_object); + } + } +} + +bool NativeScriptLanguage::refcount_decremented_instance_binding(Object *p_object) { + + void *data = p_object->get_script_instance_binding(lang_idx); + + if (!data) + return true; + + Vector<void *> &binding_data = *(Vector<void *> *)data; + + bool can_die = true; + + for (int i = 0; i < binding_data.size(); i++) { + if (!binding_data[i]) + continue; + + if (!binding_functions[i].first) + continue; + + if (binding_functions[i].second.refcount_decremented_instance_binding) { + can_die = can_die && binding_functions[i].second.refcount_decremented_instance_binding(binding_data[i], p_object); + } + } + + return can_die; +} + void NativeScriptLanguage::set_global_type_tag(int p_idx, StringName p_class_name, const void *p_type_tag) { if (!global_type_tags.has(p_idx)) { global_type_tags.insert(p_idx, HashMap<StringName, const void *>()); @@ -1537,12 +1597,16 @@ bool NativeScriptLanguage::handles_global_class_type(const String &p_type) const String NativeScriptLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path) const { Ref<NativeScript> script = ResourceLoader::load(p_path, "NativeScript"); if (script.is_valid()) { - *r_base_type = script->get_instance_base_type(); - *r_icon_path = script->get_script_class_icon_path(); + if (r_base_type) + *r_base_type = script->get_instance_base_type(); + if (r_icon_path) + *r_icon_path = script->get_script_class_icon_path(); return script->get_script_class_name(); } - *r_base_type = String(); - *r_icon_path = String(); + if (r_base_type) + *r_base_type = String(); + if (r_icon_path) + *r_icon_path = String(); return String(); } @@ -1647,8 +1711,7 @@ void NativeReloadNode::_notification(int p_what) { } RES ResourceFormatLoaderNativeScript::load(const String &p_path, const String &p_original_path, Error *r_error) { - ResourceFormatLoaderText rsflt; - return rsflt.load(p_path, p_original_path, r_error); + return ResourceFormatLoaderText::singleton->load(p_path, p_original_path, r_error); } void ResourceFormatLoaderNativeScript::get_recognized_extensions(List<String> *p_extensions) const { diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h index 6f18e2db27..51370f5fbf 100644 --- a/modules/gdnative/nativescript/nativescript.h +++ b/modules/gdnative/nativescript/nativescript.h @@ -31,21 +31,21 @@ #ifndef NATIVE_SCRIPT_H #define NATIVE_SCRIPT_H +#include "core/io/resource_loader.h" +#include "core/io/resource_saver.h" +#include "core/oa_hash_map.h" +#include "core/ordered_hash_map.h" +#include "core/os/thread_safe.h" #include "core/resource.h" #include "core/script_language.h" #include "core/self_list.h" -#include "io/resource_loader.h" -#include "io/resource_saver.h" -#include "oa_hash_map.h" -#include "ordered_hash_map.h" -#include "os/thread_safe.h" #include "scene/main/node.h" #include "modules/gdnative/gdnative.h" #include <nativescript/godot_nativescript.h> #ifndef NO_THREADS -#include "os/mutex.h" +#include "core/os/mutex.h" #endif struct NativeScriptDesc { @@ -70,8 +70,6 @@ struct NativeScriptDesc { String documentation; }; - String documentation; - Map<StringName, Method> methods; OrderedHashMap<StringName, Property> properties; Map<StringName, Signal> signals_; // QtCreator doesn't like the name signals @@ -81,6 +79,8 @@ struct NativeScriptDesc { godot_instance_create_func create_func; godot_instance_destroy_func destroy_func; + String documentation; + const void *type_tag; bool is_tool; @@ -277,18 +277,14 @@ public: Map<String, Set<NativeScript *> > library_script_users; - const StringName _init_call_type = "nativescript_init"; - const StringName _init_call_name = "nativescript_init"; - - const StringName _terminate_call_name = "nativescript_terminate"; - - const StringName _noarg_call_type = "nativescript_no_arg"; - - const StringName _frame_call_name = "nativescript_frame"; - + StringName _init_call_type; + StringName _init_call_name; + StringName _terminate_call_name; + StringName _noarg_call_type; + StringName _frame_call_name; #ifndef NO_THREADS - const StringName _thread_enter_call_name = "nativescript_thread_enter"; - const StringName _thread_exit_call_name = "nativescript_thread_exit"; + StringName _thread_enter_call_name; + StringName _thread_exit_call_name; #endif NativeScriptLanguage(); @@ -353,6 +349,8 @@ public: virtual void *alloc_instance_binding_data(Object *p_object); virtual void free_instance_binding_data(void *p_data); + virtual void refcount_incremented_instance_binding(Object *p_object); + virtual bool refcount_decremented_instance_binding(Object *p_object); void set_global_type_tag(int p_idx, StringName p_class_name, const void *p_type_tag); const void *get_global_type_tag(int p_idx, StringName p_class_name) const; @@ -370,11 +368,14 @@ inline NativeScriptDesc *NativeScript::get_script_desc() const { class NativeReloadNode : public Node { GDCLASS(NativeReloadNode, Node) - bool unloaded = false; + bool unloaded; public: static void _bind_methods(); void _notification(int p_what); + + NativeReloadNode() : + unloaded(false) {} }; class ResourceFormatLoaderNativeScript : public ResourceFormatLoader { diff --git a/modules/gdnative/nativescript/register_types.cpp b/modules/gdnative/nativescript/register_types.cpp index 9a0e764391..4433c0a638 100644 --- a/modules/gdnative/nativescript/register_types.cpp +++ b/modules/gdnative/nativescript/register_types.cpp @@ -30,8 +30,8 @@ #include "register_types.h" -#include "io/resource_loader.h" -#include "io/resource_saver.h" +#include "core/io/resource_loader.h" +#include "core/io/resource_saver.h" #include "nativescript.h" diff --git a/modules/gdnative/net/SCsub b/modules/gdnative/net/SCsub index 53f9271128..e915703935 100644 --- a/modules/gdnative/net/SCsub +++ b/modules/gdnative/net/SCsub @@ -1,12 +1,7 @@ #!/usr/bin/env python -import os -import methods - Import('env') -Import('env_modules') +Import('env_gdnative') -env_net_gdnative = env_modules.Clone() +env_gdnative.add_source_files(env.modules_sources, '*.cpp') -env_net_gdnative.Append(CPPPATH=['#modules/gdnative/include/']) -env_net_gdnative.add_source_files(env.modules_sources, '*.cpp') diff --git a/modules/gdnative/pluginscript/SCsub b/modules/gdnative/pluginscript/SCsub index 2031a4236b..20eaa99592 100644 --- a/modules/gdnative/pluginscript/SCsub +++ b/modules/gdnative/pluginscript/SCsub @@ -1,9 +1,6 @@ #!/usr/bin/env python Import('env') -Import('env_modules') +Import('env_gdnative') -env_pluginscript = env_modules.Clone() - -env_pluginscript.Append(CPPPATH=['#modules/gdnative/include/']) -env_pluginscript.add_source_files(env.modules_sources, '*.cpp') +env_gdnative.add_source_files(env.modules_sources, '*.cpp') diff --git a/modules/gdnative/pluginscript/pluginscript_loader.cpp b/modules/gdnative/pluginscript/pluginscript_loader.cpp index acba297fa0..52d5b2b595 100644 --- a/modules/gdnative/pluginscript/pluginscript_loader.cpp +++ b/modules/gdnative/pluginscript/pluginscript_loader.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ // Godot imports -#include "os/file_access.h" +#include "core/os/file_access.h" // Pythonscript imports #include "pluginscript_language.h" #include "pluginscript_loader.h" diff --git a/modules/gdnative/pluginscript/pluginscript_loader.h b/modules/gdnative/pluginscript/pluginscript_loader.h index 9276ea3ef9..5c17bb932e 100644 --- a/modules/gdnative/pluginscript/pluginscript_loader.h +++ b/modules/gdnative/pluginscript/pluginscript_loader.h @@ -32,9 +32,9 @@ #define PYTHONSCRIPT_PY_LOADER_H // Godot imports +#include "core/io/resource_loader.h" +#include "core/io/resource_saver.h" #include "core/script_language.h" -#include "io/resource_loader.h" -#include "io/resource_saver.h" class PluginScriptLanguage; diff --git a/modules/gdnative/pluginscript/register_types.cpp b/modules/gdnative/pluginscript/register_types.cpp index 924abf29df..e677bc9867 100644 --- a/modules/gdnative/pluginscript/register_types.cpp +++ b/modules/gdnative/pluginscript/register_types.cpp @@ -30,11 +30,11 @@ #include "register_types.h" +#include "core/io/resource_loader.h" +#include "core/io/resource_saver.h" +#include "core/os/dir_access.h" +#include "core/os/os.h" #include "core/project_settings.h" -#include "io/resource_loader.h" -#include "io/resource_saver.h" -#include "os/dir_access.h" -#include "os/os.h" #include "scene/main/scene_tree.h" #include "pluginscript_language.h" diff --git a/modules/gdnative/register_types.cpp b/modules/gdnative/register_types.cpp index ae1a218392..62e87c3651 100644 --- a/modules/gdnative/register_types.cpp +++ b/modules/gdnative/register_types.cpp @@ -29,19 +29,19 @@ /*************************************************************************/ #include "register_types.h" + #include "gdnative/gdnative.h" #include "gdnative.h" -#include "io/resource_loader.h" -#include "io/resource_saver.h" - #include "arvr/register_types.h" #include "nativescript/register_types.h" #include "net/register_types.h" #include "pluginscript/register_types.h" #include "core/engine.h" +#include "core/io/resource_loader.h" +#include "core/io/resource_saver.h" #include "core/os/os.h" #include "core/project_settings.h" @@ -148,7 +148,7 @@ protected: }; struct LibrarySymbol { - char *name; + const char *name; bool is_required; }; @@ -239,7 +239,7 @@ void GDNativeExportPlugin::_export_file(const String &p_path, const String &p_ty String additional_code = "extern void register_dynamic_symbol(char *name, void *address);\n" "extern void add_ios_init_callback(void (*cb)());\n"; String linker_flags = ""; - for (int i = 0; i < sizeof(expected_symbols) / sizeof(expected_symbols[0]); ++i) { + for (unsigned int i = 0; i < sizeof(expected_symbols) / sizeof(expected_symbols[0]); ++i) { String full_name = lib->get_symbol_prefix() + expected_symbols[i].name; String code = declare_pattern.replace("$name", full_name); code = code.replace("$weak", expected_symbols[i].is_required ? "" : " __attribute__((weak))"); @@ -255,7 +255,7 @@ void GDNativeExportPlugin::_export_file(const String &p_path, const String &p_ty additional_code += String("void $prefixinit() {\n").replace("$prefix", lib->get_symbol_prefix()); String register_pattern = " if (&$name) register_dynamic_symbol((char *)\"$name\", (void *)$name);\n"; - for (int i = 0; i < sizeof(expected_symbols) / sizeof(expected_symbols[0]); ++i) { + for (unsigned int i = 0; i < sizeof(expected_symbols) / sizeof(expected_symbols[0]); ++i) { String full_name = lib->get_symbol_prefix() + expected_symbols[i].name; additional_code += register_pattern.replace("$name", full_name); } diff --git a/modules/gdscript/SCsub b/modules/gdscript/SCsub index 73f09f1659..6904154953 100644 --- a/modules/gdscript/SCsub +++ b/modules/gdscript/SCsub @@ -9,5 +9,3 @@ env_gdscript.add_source_files(env.modules_sources, "*.cpp") if env['tools']: env_gdscript.add_source_files(env.modules_sources, "./editor/*.cpp") - -Export('env') diff --git a/modules/gdscript/doc_classes/GDScript.xml b/modules/gdscript/doc_classes/GDScript.xml index 632970f8c0..4cefdbd7cb 100644 --- a/modules/gdscript/doc_classes/GDScript.xml +++ b/modules/gdscript/doc_classes/GDScript.xml @@ -8,7 +8,7 @@ [method new] creates a new instance of the script. [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/getting_started/scripting/gdscript/index.html</link> + <link>https://docs.godotengine.org/en/latest/getting_started/scripting/gdscript/index.html</link> </tutorials> <demos> </demos> diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index f2a1a5b50c..e4aee842ba 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -43,10 +43,6 @@ static bool _is_text_char(CharType c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'; } -static bool _is_whitespace(CharType c) { - return c == '\t' || c == ' '; -} - static bool _is_char(CharType c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; @@ -76,6 +72,7 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_ bool in_word = false; bool in_function_name = false; bool in_variable_declaration = false; + bool in_function_args = false; bool in_member_variable = false; bool in_node_path = false; bool is_hex_notation = false; @@ -121,8 +118,8 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_ is_hex_notation = false; } - // 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) { + // check for dot or underscore or 'x' for hex notation in floating point number or 'e' for scientific notation + if ((str[j] == '.' || str[j] == 'x' || str[j] == '_' || str[j] == 'e') && !in_word && prev_is_number && !is_number) { is_number = true; is_symbol = false; is_char = false; @@ -224,17 +221,24 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_ } if (is_symbol) { - in_function_name = false; - in_member_variable = false; - if (expect_type && str[j] != ' ' && str[j] != '\t' && str[j] != ':') { + if (in_function_name) { + in_function_args = true; + } + + if (in_function_args && str[j] == ')') { + in_function_args = false; + } + + if (expect_type && prev_is_char) { expect_type = false; } + if (j > 0 && str[j] == '>' && str[j - 1] == '-') { expect_type = true; } - if (in_variable_declaration || previous_text == "(" || previous_text == ",") { + if (in_variable_declaration || in_function_args) { int k = j; // Skip space while (k < str.length() && (str[k] == '\t' || str[k] == ' ')) { @@ -248,6 +252,8 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_ } in_variable_declaration = false; + in_function_name = false; + in_member_variable = false; } if (!in_node_path && in_region == -1 && str[j] == '$') { diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index e05bc7d591..ef86ccae14 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -30,13 +30,13 @@ #include "gdscript.h" -#include "engine.h" +#include "core/engine.h" +#include "core/global_constants.h" +#include "core/io/file_access_encrypted.h" +#include "core/os/file_access.h" +#include "core/os/os.h" +#include "core/project_settings.h" #include "gdscript_compiler.h" -#include "global_constants.h" -#include "io/file_access_encrypted.h" -#include "os/file_access.h" -#include "os/os.h" -#include "project_settings.h" /////////////////////////// @@ -719,22 +719,36 @@ Error GDScript::load_byte_code(const String &p_path) { FileAccess *fa = FileAccess::open(p_path, FileAccess::READ); ERR_FAIL_COND_V(!fa, ERR_CANT_OPEN); + FileAccessEncrypted *fae = memnew(FileAccessEncrypted); ERR_FAIL_COND_V(!fae, ERR_CANT_OPEN); + Vector<uint8_t> key; key.resize(32); for (int i = 0; i < key.size(); 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); + + if (err) { + fa->close(); + memdelete(fa); + memdelete(fae); + + ERR_FAIL_COND_V(err, err); + } + bytecode.resize(fae->get_len()); fae->get_buffer(bytecode.ptrw(), bytecode.size()); + fae->close(); memdelete(fae); + } else { bytecode = FileAccess::get_file_as_array(p_path); } + ERR_FAIL_COND_V(bytecode.size() == 0, ERR_PARSE_ERROR); path = p_path; @@ -1762,10 +1776,11 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const { "remote", "sync", "master", + "puppet", "slave", "remotesync", "mastersync", - "slavesync", + "puppetsync", 0 }; @@ -1956,14 +1971,19 @@ String GDScriptWarning::get_message() const { } break; case UNSAFE_CAST: { CHECK_SYMBOLS(1); - return "The value is cast to '" + symbols[0] + "' but has an unkown type."; + return "The value is cast to '" + symbols[0] + "' but has an unknown 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; + case DEPRECATED_KEYWORD: { + CHECK_SYMBOLS(2); + return "The '" + symbols[0] + "' keyword is deprecated and will be removed in a future release, please replace its uses by '" + symbols[1] + "'."; + } break; + case WARNING_MAX: break; // Can't happen, but silences warning } - ERR_EXPLAIN("Invalid GDScript waring code: " + get_name_from_code(code)); + ERR_EXPLAIN("Invalid GDScript warning code: " + get_name_from_code(code)); ERR_FAIL_V(String()); #undef CHECK_SYMBOLS @@ -2001,6 +2021,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) { "UNSAFE_METHOD_ACCESS", "UNSAFE_CAST", "UNSAFE_CALL_ARGUMENT", + "DEPRECATED_KEYWORD", NULL }; @@ -2014,7 +2035,7 @@ GDScriptWarning::Code GDScriptWarning::get_code_from_name(const String &p_name) } } - ERR_EXPLAIN("Invalid GDScript waring name: " + p_name); + ERR_EXPLAIN("Invalid GDScript warning name: " + p_name); ERR_FAIL_V(WARNING_MAX); } @@ -2044,12 +2065,12 @@ GDScriptLanguage::GDScriptLanguage() { _debug_call_stack_pos = 0; int dmcs = GLOBAL_DEF("debug/settings/gdscript/max_call_stack", 1024); + ProjectSettings::get_singleton()->set_custom_property_info("debug/settings/gdscript/max_call_stack", PropertyInfo(Variant::INT, "debug/settings/gdscript/max_call_stack", PROPERTY_HINT_RANGE, "1024,4096,1,or_greater")); //minimum is 1024 + if (ScriptDebugger::get_singleton()) { //debugging enabled! _debug_max_call_stack = dmcs; - if (_debug_max_call_stack < 1024) - _debug_max_call_stack = 1024; _call_stack = memnew_arr(CallLevel, _debug_max_call_stack + 1); } else { @@ -2060,6 +2081,7 @@ GDScriptLanguage::GDScriptLanguage() { #ifdef DEBUG_ENABLED GLOBAL_DEF("debug/gdscript/warnings/enable", true); GLOBAL_DEF("debug/gdscript/warnings/treat_warnings_as_errors", false); + GLOBAL_DEF("debug/gdscript/completion/autocomplete_setters_and_getters", 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_")); @@ -2095,23 +2117,14 @@ RES ResourceFormatLoaderGDScript::load(const String &p_path, const String &p_ori script->set_script_path(p_original_path); // script needs this. script->set_path(p_original_path); Error err = script->load_byte_code(p_path); - - if (err != OK) { - - ERR_FAIL_COND_V(err != OK, RES()); - } + ERR_FAIL_COND_V(err != OK, RES()); } else { Error err = script->load_source_code(p_path); - - if (err != OK) { - - ERR_FAIL_COND_V(err != OK, RES()); - } + ERR_FAIL_COND_V(err != OK, RES()); script->set_script_path(p_original_path); // script needs this. script->set_path(p_original_path); - //script->set_name(p_path.get_file()); script->reload(); } @@ -2120,6 +2133,7 @@ RES ResourceFormatLoaderGDScript::load(const String &p_path, const String &p_ori return scriptres; } + void ResourceFormatLoaderGDScript::get_recognized_extensions(List<String> *p_extensions) const { p_extensions->push_back("gd"); diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index d400230f43..f344beba9f 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -31,10 +31,10 @@ #ifndef GDSCRIPT_H #define GDSCRIPT_H +#include "core/io/resource_loader.h" +#include "core/io/resource_saver.h" +#include "core/script_language.h" #include "gdscript_function.h" -#include "io/resource_loader.h" -#include "io/resource_saver.h" -#include "script_language.h" class GDScriptNativeClass : public Reference { @@ -147,7 +147,7 @@ public: const Map<StringName, Variant> &get_constants() const { return constants; } const Set<StringName> &get_members() const { return members; } const GDScriptDataType &get_member_type(const StringName &p_member) const { - ERR_FAIL_COND_V(!member_indices.has(p_member), GDScriptDataType()); + CRASH_COND(!member_indices.has(p_member)); return member_indices[p_member].data_type; } const Map<StringName, GDScriptFunction *> &get_member_functions() const { return member_functions; } @@ -286,9 +286,10 @@ struct GDScriptWarning { 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_METHOD_ACCESS, // Function 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 + DEPRECATED_KEYWORD, // The keyword is deprecated and should be replaced WARNING_MAX, } code; Vector<String> symbols; @@ -300,8 +301,8 @@ struct GDScriptWarning { static Code get_code_from_name(const String &p_name); GDScriptWarning() : - line(-1), - code(WARNING_MAX) {} + code(WARNING_MAX), + line(-1) {} }; #endif // DEBUG_ENABLED diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 1403184557..45319c59e7 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -388,7 +388,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: int ret = _parse_expression(codegen, an->elements[i], slevel); if (ret < 0) return ret; - if (ret & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) { + if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) { slevel++; codegen.alloc_stack(slevel); } @@ -419,7 +419,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: int ret = _parse_expression(codegen, dn->elements[i].key, slevel); if (ret < 0) return ret; - if (ret & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) { + if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) { slevel++; codegen.alloc_stack(slevel); } @@ -429,7 +429,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: ret = _parse_expression(codegen, dn->elements[i].value, slevel); if (ret < 0) return ret; - if (ret & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) { + if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) { slevel++; codegen.alloc_stack(slevel); } @@ -518,7 +518,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: } } - codegen.opcodes.push_back(src_addr); // source adddress + codegen.opcodes.push_back(src_addr); // source address int dst_addr = (p_stack_level) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS); codegen.opcodes.push_back(dst_addr); // append the stack level as destination address of the opcode codegen.alloc_stack(p_stack_level); @@ -545,7 +545,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: int ret = _parse_expression(codegen, on->arguments[i], slevel); if (ret < 0) return ret; - if (ret & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) { + if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) { slevel++; codegen.alloc_stack(slevel); } @@ -578,7 +578,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: int ret = _parse_expression(codegen, on->arguments[i], slevel); if (ret < 0) return ret; - if (ret & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) { + if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) { slevel++; codegen.alloc_stack(slevel); } @@ -606,7 +606,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: if (ret < 0) return ret; - if (ret & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) { + if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) { slevel++; codegen.alloc_stack(slevel); } @@ -655,7 +655,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: ret = _parse_expression(codegen, on->arguments[i], slevel); if (ret < 0) return ret; - if (ret & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) { + if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) { slevel++; codegen.alloc_stack(slevel); } @@ -681,7 +681,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: int ret = _parse_expression(codegen, on->arguments[i], slevel); if (ret < 0) return ret; - if (ret & (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS)) { + if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) { slevel++; codegen.alloc_stack(slevel); } @@ -1253,6 +1253,25 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: codegen.opcodes.push_back(src_address_b); // argument 2 (unary only takes one parameter) } break; + case GDScriptParser::OperatorNode::OP_IS_BUILTIN: { + ERR_FAIL_COND_V(on->arguments.size() != 2, false); + ERR_FAIL_COND_V(on->arguments[1]->type != GDScriptParser::Node::TYPE_TYPE, false); + + int slevel = p_stack_level; + + int src_address_a = _parse_expression(codegen, on->arguments[0], slevel); + if (src_address_a < 0) + return -1; + + if (src_address_a & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) + slevel++; //uses stack for return, increase stack + + const GDScriptParser::TypeNode *tn = static_cast<const GDScriptParser::TypeNode *>(on->arguments[1]); + + codegen.opcodes.push_back(GDScriptFunction::OPCODE_IS_BUILTIN); // perform operator + codegen.opcodes.push_back(src_address_a); // argument 1 + codegen.opcodes.push_back((int)tn->vtype); // argument 2 (unary only takes one parameter) + } break; default: { ERR_EXPLAIN("Bug in bytecode compiler, unexpected operator #" + itos(on->op) + " in parse tree while parsing expression."); @@ -1322,6 +1341,8 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo int ret = _parse_expression(codegen, op, p_stack_level); if (ret < 0) { + memdelete(id); + memdelete(op); return ERR_PARSE_ERROR; } @@ -1341,6 +1362,8 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo // compile the condition int ret = _parse_expression(codegen, branch.compiled_pattern, p_stack_level); if (ret < 0) { + memdelete(id); + memdelete(op); return ERR_PARSE_ERROR; } @@ -1353,6 +1376,8 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo Error err = _parse_block(codegen, branch.body, p_stack_level, p_break_addr, continue_addr); if (err) { + memdelete(id); + memdelete(op); return ERR_PARSE_ERROR; } @@ -1364,6 +1389,9 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo codegen.opcodes.write[break_addr + 1] = codegen.opcodes.size(); + memdelete(id); + memdelete(op); + } break; case GDScriptParser::ControlFlowNode::CF_IF: { @@ -1575,13 +1603,13 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo return OK; } -Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready) { +Error GDScriptCompiler::_parse_function(Ref<GDScript> p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready) { Vector<int> bytecode; CodeGen codegen; codegen.class_node = p_class; - codegen.script = p_script; + codegen.script = p_script.ptr(); codegen.function_node = p_func; codegen.stack_max = 0; codegen.current_line = 0; @@ -1825,7 +1853,7 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser return OK; } -Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state) { +Error GDScriptCompiler::_parse_class_level(Ref<GDScript> p_script, Ref<GDScript> p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state) { if (p_class->owner && p_class->owner->owner) { // Owner is not root @@ -1859,7 +1887,7 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner p_script->initializer = NULL; p_script->subclasses.clear(); - p_script->_owner = p_owner; + p_script->_owner = p_owner.ptr(); p_script->tool = p_class->tool; p_script->name = p_class->name; @@ -1966,7 +1994,7 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner StringName name = p_class->_signals[i].name; - GDScript *c = p_script; + GDScript *c = p_script.ptr(); while (c) { @@ -2026,7 +2054,7 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner return OK; } -Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) { +Error GDScriptCompiler::_parse_class_blocks(Ref<GDScript> p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) { //parse methods bool has_initializer = false; @@ -2131,7 +2159,7 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa return OK; } -void GDScriptCompiler::_make_scripts(const GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) { +void GDScriptCompiler::_make_scripts(const Ref<GDScript> p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) { Map<StringName, Ref<GDScript> > old_subclasses; @@ -2150,20 +2178,20 @@ void GDScriptCompiler::_make_scripts(const GDScript *p_script, const GDScriptPar subclass.instance(); } - subclass->_owner = const_cast<GDScript *>(p_script); + subclass->_owner = const_cast<GDScript *>(p_script.ptr()); class_map.insert(name, subclass); _make_scripts(subclass.ptr(), p_class->subclasses[i], p_keep_state); } } -Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_script, bool p_keep_state) { +Error GDScriptCompiler::compile(const GDScriptParser *p_parser, Ref<GDScript> p_script, bool p_keep_state) { err_line = -1; err_column = -1; error = ""; parser = p_parser; - main_script = p_script; + main_script = p_script.ptr(); const GDScriptParser::Node *root = parser->get_parse_tree(); ERR_FAIL_COND_V(root->type != GDScriptParser::Node::TYPE_CLASS, ERR_INVALID_DATA); diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h index 55f5e2b48e..23c6450aa7 100644 --- a/modules/gdscript/gdscript_compiler.h +++ b/modules/gdscript/gdscript_compiler.h @@ -148,17 +148,17 @@ class GDScriptCompiler { int _parse_assign_right_expression(CodeGen &codegen, const GDScriptParser::OperatorNode *p_expression, int p_stack_level); int _parse_expression(CodeGen &codegen, const GDScriptParser::Node *p_expression, int p_stack_level, bool p_root = false, bool p_initializer = false); Error _parse_block(CodeGen &codegen, const GDScriptParser::BlockNode *p_block, int p_stack_level = 0, int p_break_addr = -1, int p_continue_addr = -1); - Error _parse_function(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready = false); - Error _parse_class_level(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state); - Error _parse_class_blocks(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state); - void _make_scripts(const GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state); + Error _parse_function(Ref<GDScript> p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready = false); + Error _parse_class_level(Ref<GDScript> p_script, Ref<GDScript> p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state); + Error _parse_class_blocks(Ref<GDScript> p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state); + void _make_scripts(const Ref<GDScript> p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state); int err_line; int err_column; StringName source; String error; public: - Error compile(const GDScriptParser *p_parser, GDScript *p_script, bool p_keep_state = false); + Error compile(const GDScriptParser *p_parser, Ref<GDScript> p_script, bool p_keep_state = false); String get_error() const; int get_error_line() const; diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 30400f01e9..068e8d2d92 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -31,16 +31,13 @@ #include "gdscript.h" #include "core/engine.h" -#include "editor/editor_settings.h" +#include "core/global_constants.h" +#include "core/os/file_access.h" #include "gdscript_compiler.h" -#include "global_constants.h" -#include "os/file_access.h" #ifdef TOOLS_ENABLED -#include "core/reference.h" #include "editor/editor_file_system.h" #include "editor/editor_settings.h" -#include "engine.h" #endif void GDScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const { @@ -54,17 +51,11 @@ void GDScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const { p_delimiters->push_back("\"\"\" \"\"\""); } Ref<Script> GDScriptLanguage::get_template(const String &p_class_name, const String &p_base_class_name) const { -#ifdef TOOLS_ENABLED - bool th = EDITOR_DEF("text_editor/completion/add_type_hints", false); -#else - bool th = false; -#endif - String _template = "extends %BASE%\n" "\n" "# Declare member variables here. Examples:\n" - "# var a %INT_TYPE%= 2\n" - "# var b %STRING_TYPE%= \"text\"\n" + "# var a%INT_TYPE% = 2\n" + "# var b%STRING_TYPE% = \"text\"\n" "\n" "# Called when the node enters the scene tree for the first time.\n" "func _ready()%VOID_RETURN%:\n" @@ -76,9 +67,9 @@ Ref<Script> GDScriptLanguage::get_template(const String &p_class_name, const Str #ifdef TOOLS_ENABLED if (EDITOR_DEF("text_editor/completion/add_type_hints", false)) { - _template = _template.replace("%INT_TYPE%", ": int "); - _template = _template.replace("%STRING_TYPE%", ": String "); - _template = _template.replace("%FLOAT_TYPE%", " : float"); + _template = _template.replace("%INT_TYPE%", ": int"); + _template = _template.replace("%STRING_TYPE%", ": String"); + _template = _template.replace("%FLOAT_TYPE%", ": float"); _template = _template.replace("%VOID_RETURN%", " -> void"); } else { _template = _template.replace("%INT_TYPE%", ""); @@ -466,7 +457,7 @@ String GDScriptLanguage::make_function(const String &p_class, const String &p_na if (th) { String type = p_args[i].get_slice(":", 1); if (!type.empty() && type != "var") { - s += " : " + type; + s += ": " + type; } } } @@ -1113,6 +1104,7 @@ static bool _guess_expression_type(const GDScriptCompletionContext &p_context, c } break; } } break; + default: {} } // It may have found a null, but that's never useful @@ -1189,6 +1181,7 @@ static bool _guess_identifier_type(const GDScriptCompletionContext &p_context, c c.line = op->line; c.block = blk; if (_guess_expression_type(p_context, op->arguments[1], r_type)) { + r_type.type.is_meta_type = false; return true; } } @@ -1221,7 +1214,7 @@ static bool _guess_identifier_type(const GDScriptCompletionContext &p_context, c int def_from = p_context.function->arguments.size() - p_context.function->default_values.size(); if (i >= def_from) { - int def_idx = def_from - i; + int def_idx = i - def_from; if (p_context.function->default_values[def_idx]->type == GDScriptParser::Node::TYPE_OPERATOR) { const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(p_context.function->default_values[def_idx]); if (op->arguments.size() < 2) { @@ -1315,37 +1308,38 @@ static bool _guess_identifier_type(const GDScriptCompletionContext &p_context, c return false; } - // Check ClassDB - if (ClassDB::class_exists(p_identifier)) { - r_type.type.has_type = true; - r_type.type.kind = GDScriptParser::DataType::NATIVE; - r_type.type.native_type = p_identifier; - if (Engine::get_singleton()->has_singleton(p_identifier)) { - r_type.type.is_meta_type = false; - r_type.value = Engine::get_singleton()->get_singleton_object(p_identifier); - } else { - r_type.type.is_meta_type = true; - int idx = GDScriptLanguage::get_singleton()->get_global_map()[p_identifier]; - r_type.value = GDScriptLanguage::get_singleton()->get_global_array()[idx]; + for (int i = 0; i < 2; i++) { + StringName target_id; + switch (i) { + case 0: + // Check ClassDB + target_id = p_identifier; + break; + case 1: + // ClassDB again for underscore-prefixed classes + target_id = String("_") + p_identifier; + break; } - return true; - } - // ClassDB again for underscore-prefixed classes - StringName under_id = String("_") + p_identifier; - if (ClassDB::class_exists(under_id)) { - r_type.type.has_type = true; - r_type.type.kind = GDScriptParser::DataType::NATIVE; - r_type.type.native_type = p_identifier; - if (Engine::get_singleton()->has_singleton(p_identifier)) { - r_type.type.is_meta_type = false; - r_type.value = Engine::get_singleton()->get_singleton_object(p_identifier); - } else { - r_type.type.is_meta_type = true; - int idx = GDScriptLanguage::get_singleton()->get_global_map()[p_identifier]; - r_type.value = GDScriptLanguage::get_singleton()->get_global_array()[idx]; + if (ClassDB::class_exists(target_id)) { + r_type.type.has_type = true; + r_type.type.kind = GDScriptParser::DataType::NATIVE; + r_type.type.native_type = target_id; + if (Engine::get_singleton()->has_singleton(target_id)) { + r_type.type.is_meta_type = false; + r_type.value = Engine::get_singleton()->get_singleton_object(target_id); + } else { + r_type.type.is_meta_type = true; + const Map<StringName, int>::Element *target_elem = GDScriptLanguage::get_singleton()->get_global_map().find(target_id); + // Check because classes like EditorNode are in ClassDB by now, but unknown to GDScript + if (!target_elem) { + return false; + } + int idx = target_elem->get(); + r_type.value = GDScriptLanguage::get_singleton()->get_global_array()[idx]; + } + return true; } - return true; } // Check autoload singletons @@ -1376,11 +1370,11 @@ static bool _guess_identifier_type_from_base(const GDScriptCompletionContext &p_ for (int i = 0; i < base_type.class_type->variables.size(); i++) { GDScriptParser::ClassNode::Member m = base_type.class_type->variables[i]; if (m.identifier == p_identifier) { - if (m.data_type.has_type) { - r_type.type = m.data_type; - return true; - } if (m.expression) { + if (p_context.line == m.expression->line) { + // Variable used in the same expression + return false; + } if (_guess_expression_type(p_context, m.expression, r_type)) { return true; } @@ -1389,6 +1383,10 @@ static bool _guess_identifier_type_from_base(const GDScriptCompletionContext &p_ return true; } } + if (m.data_type.has_type) { + r_type.type = m.data_type; + return true; + } return false; } } @@ -2002,7 +2000,8 @@ static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context if (!_static) { List<MethodInfo> methods; - ClassDB::get_method_list(type, &methods, false, true); + bool is_autocompleting_getters = GLOBAL_GET("debug/gdscript/completion/autocomplete_setters_and_getters").booleanize(); + ClassDB::get_method_list(type, &methods, false, !is_autocompleting_getters); for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) { if (E->get().name.begins_with("_")) { continue; @@ -2109,8 +2108,8 @@ static void _find_identifiers(const GDScriptCompletionContext &p_context, bool p "and", "in", "not", "or", "false", "PI", "TAU", "INF", "NAN", "self", "true", "as", "assert", "breakpoint", "class", "extends", "is", "func", "preload", "setget", "signal", "tool", "yield", "const", "enum", "export", "onready", "static", "var", "break", "continue", "if", "elif", - "else", "for", "pass", "return", "match", "while", "remote", "sync", "master", "slave", - "remotesync", "mastersync", "slavesync", + "else", "for", "pass", "return", "match", "while", "remote", "sync", "master", "puppet", "slave", + "remotesync", "mastersync", "puppetsync", 0 }; @@ -2596,7 +2595,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base } method_hint += arg; if (use_type_hint && mi.arguments[i].type != Variant::NIL) { - method_hint += " : "; + method_hint += ": "; if (mi.arguments[i].type == Variant::OBJECT && mi.arguments[i].class_name != StringName()) { method_hint += mi.arguments[i].class_name.operator String(); } else { @@ -3304,7 +3303,7 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol } } else { /* - // Because get_integer_constant_enum and get_integer_constant dont work on @GlobalScope + // Because get_integer_constant_enum and get_integer_constant don't work on @GlobalScope // We cannot determine the exact nature of the identifier here // Otherwise these codes would work StringName enumName = ClassDB::get_integer_constant_enum("@GlobalScope", p_symbol, true); @@ -3352,6 +3351,7 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol return OK; } } break; + default: {} } return ERR_CANT_RESOLVE; diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp index bae3f48923..31115a4bd9 100644 --- a/modules/gdscript/gdscript_function.cpp +++ b/modules/gdscript/gdscript_function.cpp @@ -30,9 +30,9 @@ #include "gdscript_function.h" +#include "core/os/os.h" #include "gdscript.h" #include "gdscript_functions.h" -#include "os/os.h" Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_instance, GDScript *p_script, Variant &self, Variant *p_stack, String &r_error) const { @@ -155,6 +155,7 @@ String GDScriptFunction::_get_call_error(const Variant::CallError &p_err, const return err_text; } +#ifdef DEBUG_ENABLED static String _get_var_type(const Variant *p_type) { String basestr; @@ -164,7 +165,6 @@ static String _get_var_type(const Variant *p_type) { if (!bobj) { basestr = "null instance"; } else { -#ifdef DEBUG_ENABLED if (ObjectDB::instance_validate(bobj)) { if (bobj->get_script_instance()) basestr = bobj->get_class() + " (" + bobj->get_script_instance()->get_script()->get_path().get_file() + ")"; @@ -173,10 +173,6 @@ static String _get_var_type(const Variant *p_type) { } else { basestr = "previously freed instance"; } - -#else - basestr = "Object"; -#endif } } else { @@ -185,12 +181,14 @@ static String _get_var_type(const Variant *p_type) { return basestr; } +#endif #if defined(__GNUC__) #define OPCODES_TABLE \ static const void *switch_table_ops[] = { \ &&OPCODE_OPERATOR, \ &&OPCODE_EXTENDS_TEST, \ + &&OPCODE_IS_BUILTIN, \ &&OPCODE_SET, \ &&OPCODE_GET, \ &&OPCODE_SET_NAMED, \ @@ -277,7 +275,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #endif uint32_t alloca_size = 0; - GDScript *_class; + GDScript *script; int ip = 0; int line = _initial_line; @@ -288,7 +286,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a line = p_state->line; ip = p_state->ip; alloca_size = p_state->stack.size(); - _class = p_state->_class; + script = p_state->script.ptr(); p_instance = p_state->instance; defarg = p_state->defarg; self = p_state->self; @@ -370,9 +368,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } else { self = p_instance->owner; } - _class = p_instance->script.ptr(); + script = p_instance->script.ptr(); } else { - _class = _script; + script = this->_script.ptr(); } } @@ -397,7 +395,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #define GET_VARIANT_PTR(m_v, m_code_ofs) \ Variant *m_v; \ - m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, _class, self, stack, err_text); \ + m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, script, self, stack, err_text); \ if (unlikely(!m_v)) \ OPCODE_BREAK; @@ -406,7 +404,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #define CHECK_SPACE(m_space) #define GET_VARIANT_PTR(m_v, m_code_ofs) \ Variant *m_v; \ - m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, _class, self, stack, err_text); + m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, script, self, stack, err_text); #endif @@ -421,8 +419,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a profile.call_count++; profile.frame_call_count++; } -#endif bool exit_ok = false; +#endif #ifdef DEBUG_ENABLED OPCODE_WHILE(ip < _code_size) { @@ -536,6 +534,21 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } DISPATCH_OPCODE; + OPCODE(OPCODE_IS_BUILTIN) { + + CHECK_SPACE(4); + + GET_VARIANT_PTR(value, 1); + Variant::Type var_type = (Variant::Type)_code_ptr[ip + 2]; + GET_VARIANT_PTR(dst, 3); + + GD_ERR_BREAK(var_type < 0 || var_type >= Variant::VARIANT_MAX); + + *dst = value->get_type() == var_type; + ip += 4; + } + DISPATCH_OPCODE; + OPCODE(OPCODE_SET) { CHECK_SPACE(3); @@ -666,8 +679,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GET_VARIANT_PTR(src, 2); bool valid; +#ifndef DEBUG_ENABLED + ClassDB::set_property(p_instance->owner, *index, *src, &valid); +#else bool ok = ClassDB::set_property(p_instance->owner, *index, *src, &valid); -#ifdef DEBUG_ENABLED if (!ok) { err_text = "Internal error setting property: " + String(*index); OPCODE_BREAK; @@ -687,9 +702,11 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count); const StringName *index = &_global_names_ptr[indexname]; GET_VARIANT_PTR(dst, 2); - bool ok = ClassDB::get_property(p_instance->owner, *index, *dst); -#ifdef DEBUG_ENABLED +#ifndef DEBUG_ENABLED + ClassDB::get_property(p_instance->owner, *index, *dst); +#else + bool ok = ClassDB::get_property(p_instance->owner, *index, *dst); if (!ok) { err_text = "Internal error getting property: " + String(*index); OPCODE_BREAK; @@ -736,13 +753,13 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a OPCODE(OPCODE_ASSIGN_TYPED_BUILTIN) { CHECK_SPACE(4); - Variant::Type var_type = (Variant::Type)_code_ptr[ip + 1]; GET_VARIANT_PTR(dst, 2); GET_VARIANT_PTR(src, 3); +#ifdef DEBUG_ENABLED + Variant::Type var_type = (Variant::Type)_code_ptr[ip + 1]; GD_ERR_BREAK(var_type < 0 || var_type >= Variant::VARIANT_MAX); -#ifdef DEBUG_ENABLED if (src->get_type() != var_type) { if (Variant::can_convert_strict(src->get_type(), var_type)) { Variant::CallError ce; @@ -766,14 +783,14 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a OPCODE(OPCODE_ASSIGN_TYPED_NATIVE) { CHECK_SPACE(4); - GET_VARIANT_PTR(type, 1); GET_VARIANT_PTR(dst, 2); GET_VARIANT_PTR(src, 3); #ifdef DEBUG_ENABLED + GET_VARIANT_PTR(type, 1); GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(type->operator Object *()); GD_ERR_BREAK(!nc); - if (!src->get_type() != Variant::OBJECT && !src->get_type() != Variant::NIL) { + 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; @@ -795,11 +812,11 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a OPCODE(OPCODE_ASSIGN_TYPED_SCRIPT) { CHECK_SPACE(4); - GET_VARIANT_PTR(type, 1); GET_VARIANT_PTR(dst, 2); GET_VARIANT_PTR(src, 3); #ifdef DEBUG_ENABLED + GET_VARIANT_PTR(type, 1); Script *base_type = Object::cast_to<Script>(type->operator Object *()); GD_ERR_BREAK(!base_type); @@ -1168,7 +1185,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GET_VARIANT_PTR(dst, argc + 3); - const GDScript *gds = _script; + const GDScript *gds = _script.ptr(); const Map<StringName, GDScriptFunction *>::Element *E = NULL; while (gds->base.ptr()) { @@ -1239,11 +1256,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a gdfs->state.stack_size = _stack_size; gdfs->state.self = self; gdfs->state.alloca_size = alloca_size; - gdfs->state._class = _class; + gdfs->state.script = _script; gdfs->state.ip = ip + ipofs; gdfs->state.line = line; gdfs->state.instance_id = (p_instance && p_instance->get_owner()) ? p_instance->get_owner()->get_instance_id() : 0; - gdfs->state.script_id = _class->get_instance_id(); //gdfs->state.result_pos=ip+ipofs-1; gdfs->state.defarg = defarg; gdfs->state.instance = p_instance; @@ -1266,10 +1282,11 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a OPCODE_BREAK; } #endif + Object *obj = argobj->operator Object *(); String signal = argname->operator String(); -#ifdef DEBUG_ENABLED +#ifdef DEBUG_ENABLED if (!obj) { err_text = "First argument of yield() is null."; OPCODE_BREAK; @@ -1286,17 +1303,19 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a OPCODE_BREAK; } -#endif Error err = obj->connect(signal, gdfs.ptr(), "_signal_callback", varray(gdfs), Object::CONNECT_ONESHOT); -#ifdef DEBUG_ENABLED if (err != OK) { err_text = "Error connecting to signal: " + signal + " during yield()."; OPCODE_BREAK; } +#else + obj->connect(signal, gdfs.ptr(), "_signal_callback", varray(gdfs), Object::CONNECT_ONESHOT); #endif } +#ifdef DEBUG_ENABLED exit_ok = true; +#endif OPCODE_BREAK; } @@ -1373,7 +1392,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a CHECK_SPACE(2); GET_VARIANT_PTR(r, 1); retvalue = *r; +#ifdef DEBUG_ENABLED exit_ok = true; +#endif OPCODE_BREAK; } @@ -1445,9 +1466,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a OPCODE(OPCODE_ASSERT) { CHECK_SPACE(2); - GET_VARIANT_PTR(test, 1); #ifdef DEBUG_ENABLED + GET_VARIANT_PTR(test, 1); bool result = test->booleanize(); if (!result) { @@ -1502,8 +1523,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a DISPATCH_OPCODE; OPCODE(OPCODE_END) { - +#ifdef DEBUG_ENABLED exit_ok = true; +#endif OPCODE_BREAK; } @@ -1526,8 +1548,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a String err_file; if (p_instance) err_file = p_instance->script->path; - else if (_class) - err_file = _class->path; + else if (script) + err_file = script->path; if (err_file == "") err_file = "<built-in>"; String err_func = name; @@ -1742,15 +1764,20 @@ GDScriptFunction::~GDScriptFunction() { Variant GDScriptFunctionState::_signal_callback(const Variant **p_args, int p_argcount, Variant::CallError &r_error) { #ifdef DEBUG_ENABLED - if (state.instance_id && !ObjectDB::get_instance(state.instance_id)) { - ERR_EXPLAIN("Resumed after yield, but class instance is gone"); + // Checking this first since it's faster + if (!state.script.is_valid()) { + ERR_EXPLAIN("Resumed after yield, but script is gone"); ERR_FAIL_V(Variant()); } - if (state.script_id && !ObjectDB::get_instance(state.script_id)) { - ERR_EXPLAIN("Resumed after yield, but script is gone"); + if (state.instance_id && !ObjectDB::get_instance(state.instance_id)) { + ERR_EXPLAIN("Resumed after yield, but class instance is gone"); ERR_FAIL_V(Variant()); } +#else + if (!is_valid()) { + return Variant(); + } #endif Variant arg; @@ -1818,12 +1845,12 @@ bool GDScriptFunctionState::is_valid(bool p_extended_check) const { return false; if (p_extended_check) { + //script gone? (checking this first since it's faster) + if (!state.script.is_valid()) + return false; //class instance gone? if (state.instance_id && !ObjectDB::get_instance(state.instance_id)) return false; - //script gone? - if (state.script_id && !ObjectDB::get_instance(state.script_id)) - return false; } return true; @@ -1833,13 +1860,14 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) { ERR_FAIL_COND_V(!function, Variant()); #ifdef DEBUG_ENABLED - if (state.instance_id && !ObjectDB::get_instance(state.instance_id)) { - ERR_EXPLAIN("Resumed after yield, but class instance is gone"); + // Checking this first since it's faster + if (!state.script.is_valid()) { + ERR_EXPLAIN("Resumed after yield, but script is gone"); ERR_FAIL_V(Variant()); } - if (state.script_id && !ObjectDB::get_instance(state.script_id)) { - ERR_EXPLAIN("Resumed after yield, but script is gone"); + if (state.instance_id && !ObjectDB::get_instance(state.instance_id)) { + ERR_EXPLAIN("Resumed after yield, but class instance is gone"); ERR_FAIL_V(Variant()); } #endif diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h index 3ce84290fd..a47070de4f 100644 --- a/modules/gdscript/gdscript_function.h +++ b/modules/gdscript/gdscript_function.h @@ -31,13 +31,13 @@ #ifndef GDSCRIPT_FUNCTION_H #define GDSCRIPT_FUNCTION_H -#include "os/thread.h" -#include "pair.h" -#include "reference.h" -#include "script_language.h" -#include "self_list.h" -#include "string_db.h" -#include "variant.h" +#include "core/os/thread.h" +#include "core/pair.h" +#include "core/reference.h" +#include "core/script_language.h" +#include "core/self_list.h" +#include "core/string_db.h" +#include "core/variant.h" class GDScriptInstance; class GDScript; @@ -136,6 +136,7 @@ public: enum Opcode { OPCODE_OPERATOR, OPCODE_EXTENDS_TEST, + OPCODE_IS_BUILTIN, OPCODE_SET, OPCODE_GET, OPCODE_SET_NAMED, @@ -224,7 +225,7 @@ private: bool _static; MultiplayerAPI::RPCMode rpc_mode; - GDScript *_script; + Ref<GDScript> _script; StringName name; Vector<Variant> constants; @@ -271,15 +272,13 @@ private: public: struct CallState { - ObjectID instance_id; //by debug only - ObjectID script_id; - + ObjectID instance_id; GDScriptInstance *instance; Vector<uint8_t> stack; int stack_size; Variant self; uint32_t alloca_size; - GDScript *_class; + Ref<GDScript> script; int ip; int line; int defarg; @@ -298,7 +297,7 @@ public: int get_default_argument_addr(int p_idx) const; GDScriptDataType get_return_type() const; GDScriptDataType get_argument_type(int p_idx) const; - GDScript *get_script() const { return _script; } + GDScript *get_script() const { return const_cast<GDScript *>(_script.ptr()); } StringName get_source() const { return source; } void debug_get_stack_member_state(int p_line, List<Pair<StringName, int> > *r_stackvars) const; diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp index d9c20868bd..2f31d59c46 100644 --- a/modules/gdscript/gdscript_functions.cpp +++ b/modules/gdscript/gdscript_functions.cpp @@ -30,15 +30,15 @@ #include "gdscript_functions.h" -#include "class_db.h" -#include "func_ref.h" +#include "core/class_db.h" +#include "core/func_ref.h" +#include "core/io/json.h" +#include "core/io/marshalls.h" +#include "core/math/math_funcs.h" +#include "core/os/os.h" +#include "core/reference.h" +#include "core/variant_parser.h" #include "gdscript.h" -#include "io/json.h" -#include "io/marshalls.h" -#include "math_funcs.h" -#include "os/os.h" -#include "reference.h" -#include "variant_parser.h" const char *GDScriptFunctions::get_func_name(Function p_func) { @@ -106,6 +106,8 @@ const char *GDScriptFunctions::get_func_name(Function p_func) { "printerr", "printraw", "print_debug", + "push_error", + "push_warning", "var2str", "str2var", "var2bytes", @@ -707,13 +709,40 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ ScriptLanguage *script = GDScriptLanguage::get_singleton(); if (script->debug_get_stack_level_count() > 0) { - str += "\n\t"; - str += "At: " + script->debug_get_stack_level_source(0) + ":" + itos(script->debug_get_stack_level_line(0)); // + " in function '" + script->debug_get_stack_level_function(0) + "'"; + str += "\n At: " + script->debug_get_stack_level_source(0) + ":" + itos(script->debug_get_stack_level_line(0)) + ":" + script->debug_get_stack_level_function(0) + "()"; } print_line(str); r_ret = Variant(); } break; + case PUSH_ERROR: { + VALIDATE_ARG_COUNT(1); + if (p_args[0]->get_type() != Variant::STRING) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::STRING; + r_ret = Variant(); + break; + } + + String message = *p_args[0]; + ERR_PRINTS(message); + r_ret = Variant(); + } break; + case PUSH_WARNING: { + VALIDATE_ARG_COUNT(1); + if (p_args[0]->get_type() != Variant::STRING) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::STRING; + r_ret = Variant(); + break; + } + + String message = *p_args[0]; + WARN_PRINTS(message); + r_ret = Variant(); + } break; case VAR_TO_STR: { VALIDATE_ARG_COUNT(1); String vars; @@ -1458,7 +1487,7 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) { return mi; } break; case MATH_ATAN2: { - MethodInfo mi("atan2", PropertyInfo(Variant::REAL, "x"), PropertyInfo(Variant::REAL, "y")); + MethodInfo mi("atan2", PropertyInfo(Variant::REAL, "y"), PropertyInfo(Variant::REAL, "x")); mi.return_val.type = Variant::REAL; return mi; } break; @@ -1754,11 +1783,25 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) { return mi; } break; + case PUSH_ERROR: { + + MethodInfo mi(Variant::NIL, "push_error", PropertyInfo(Variant::STRING, "message")); + mi.return_val.type = Variant::NIL; + return mi; + + } break; + case PUSH_WARNING: { + + MethodInfo mi(Variant::NIL, "push_warning", PropertyInfo(Variant::STRING, "message")); + mi.return_val.type = Variant::NIL; + return mi; + + } break; case VAR_TO_STR: { + MethodInfo mi("var2str", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT)); mi.return_val.type = Variant::STRING; return mi; - } break; case STR_TO_VAR: { @@ -1768,10 +1811,10 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) { return mi; } break; case VAR_TO_BYTES: { + MethodInfo mi("var2bytes", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT)); mi.return_val.type = Variant::POOL_BYTE_ARRAY; return mi; - } break; case BYTES_TO_VAR: { @@ -1853,7 +1896,7 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) { } break; case GET_STACK: { MethodInfo mi("get_stack"); - mi.return_val.type = Variant::NIL; + mi.return_val.type = Variant::ARRAY; return mi; } break; diff --git a/modules/gdscript/gdscript_functions.h b/modules/gdscript/gdscript_functions.h index a29f06e839..33d5f27230 100644 --- a/modules/gdscript/gdscript_functions.h +++ b/modules/gdscript/gdscript_functions.h @@ -31,7 +31,7 @@ #ifndef GDSCRIPT_FUNCTIONS_H #define GDSCRIPT_FUNCTIONS_H -#include "variant.h" +#include "core/variant.h" class GDScriptFunctions { public: @@ -97,6 +97,8 @@ public: TEXT_PRINTERR, TEXT_PRINTRAW, TEXT_PRINT_DEBUG, + PUSH_ERROR, + PUSH_WARNING, VAR_TO_STR, STR_TO_VAR, VAR_TO_BYTES, @@ -117,7 +119,6 @@ public: LEN, IS_INSTANCE_VALID, FUNC_MAX - }; static const char *get_func_name(Function p_func); diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index a3f5e1819e..1d26700fa0 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -32,14 +32,13 @@ #include "core/core_string_names.h" #include "core/engine.h" +#include "core/io/resource_loader.h" +#include "core/os/file_access.h" +#include "core/print_string.h" #include "core/project_settings.h" #include "core/reference.h" +#include "core/script_language.h" #include "gdscript.h" -#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> T *GDScriptParser::alloc_node() { @@ -57,7 +56,9 @@ T *GDScriptParser::alloc_node() { return t; } +#ifdef DEBUG_ENABLED static String _find_function_name(const GDScriptParser::OperatorNode *p_call); +#endif // DEBUG_ENABLED bool GDScriptParser::_end_statement() { @@ -82,8 +83,11 @@ bool GDScriptParser::_enter_indent_block(BlockNode *p_block) { } tokenizer->advance(); - if (tokenizer->get_token() != GDScriptTokenizer::TK_NEWLINE) { + if (tokenizer->get_token() == GDScriptTokenizer::TK_EOF) { + return false; + } + if (tokenizer->get_token() != GDScriptTokenizer::TK_NEWLINE) { // be more python-like int current = tab_level.back()->get(); tab_level.push_back(current); @@ -93,10 +97,11 @@ bool GDScriptParser::_enter_indent_block(BlockNode *p_block) { } while (true) { - if (tokenizer->get_token() != GDScriptTokenizer::TK_NEWLINE) { return false; //wtf + } else if (tokenizer->get_token(1) == GDScriptTokenizer::TK_EOF) { + return false; } else if (tokenizer->get_token(1) != GDScriptTokenizer::TK_NEWLINE) { int indent = tokenizer->get_token_line_indent(); @@ -638,9 +643,21 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s expr = op; } else { - - _set_error("Static constant '" + identifier.operator String() + "' not present in built-in type " + Variant::get_type_name(bi_type) + "."); - return NULL; + // Object is a special case + bool valid = false; + if (bi_type == Variant::OBJECT) { + int object_constant = ClassDB::get_integer_constant("Object", identifier, &valid); + if (valid) { + ConstantNode *cn = alloc_node<ConstantNode>(); + cn->value = object_constant; + cn->datatype = _type_from_variant(cn->value); + expr = cn; + } + } + if (!valid) { + _set_error("Static constant '" + identifier.operator String() + "' not present in built-in type " + Variant::get_type_name(bi_type) + "."); + return NULL; + } } } else { @@ -662,7 +679,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s if (tokenizer->get_token() == GDScriptTokenizer::TK_BUILT_IN_TYPE) { Variant::Type ct = tokenizer->get_token_type(); - if (p_parsing_constant == false) { + if (!p_parsing_constant) { if (ct == Variant::ARRAY) { if (tokenizer->get_token(2) == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) { ArrayNode *arr = alloc_node<ArrayNode>(); @@ -732,7 +749,6 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s while (!bfn && b) { if (b->variables.has(identifier)) { IdentifierNode *id = alloc_node<IdentifierNode>(); - LocalVarNode *lv = b->variables[identifier]; id->name = identifier; id->declared_block = b; id->line = id_line; @@ -740,6 +756,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s bfn = true; #ifdef DEBUG_ENABLED + LocalVarNode *lv = b->variables[identifier]; switch (tokenizer->get_token()) { case GDScriptTokenizer::TK_OP_ASSIGN_ADD: case GDScriptTokenizer::TK_OP_ASSIGN_BIT_AND: @@ -861,6 +878,20 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s op->arguments.push_back(subexpr); expr=op;*/ + } else if (tokenizer->get_token() == GDScriptTokenizer::TK_PR_IS && tokenizer->get_token(1) == GDScriptTokenizer::TK_BUILT_IN_TYPE) { + // 'is' operator with built-in type + OperatorNode *op = alloc_node<OperatorNode>(); + op->op = OperatorNode::OP_IS_BUILTIN; + op->arguments.push_back(expr); + + tokenizer->advance(); + + TypeNode *tn = alloc_node<TypeNode>(); + tn->vtype = tokenizer->get_token_type(); + op->arguments.push_back(tn); + tokenizer->advance(); + + expr = op; } else if (tokenizer->get_token() == GDScriptTokenizer::TK_BRACKET_OPEN) { // array tokenizer->advance(); @@ -1071,6 +1102,15 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s expr = op; + } else if (tokenizer->get_token() == GDScriptTokenizer::TK_BUILT_IN_TYPE && expression.size() > 0 && expression[expression.size() - 1].is_op && expression[expression.size() - 1].op == OperatorNode::OP_IS) { + Expression e = expression[expression.size() - 1]; + e.op = OperatorNode::OP_IS_BUILTIN; + expression.write[expression.size() - 1] = e; + + TypeNode *tn = alloc_node<TypeNode>(); + tn->vtype = tokenizer->get_token_type(); + expr = tn; + tokenizer->advance(); } else { //find list [ or find dictionary { @@ -1329,6 +1369,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s switch (expression[i].op) { case OperatorNode::OP_IS: + case OperatorNode::OP_IS_BUILTIN: priority = -1; break; //before anything @@ -2455,7 +2496,7 @@ void GDScriptParser::_generate_pattern(PatternNode *p_pattern, Node *p_node_to_m Node *condition = NULL; - // chech for has, then for pattern + // check for has, then for pattern IdentifierNode *has = alloc_node<IdentifierNode>(); has->name = "has"; @@ -3915,14 +3956,15 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "FLAGS") { - //current_export.hint=PROPERTY_HINT_ALL_FLAGS; tokenizer->advance(); if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) { + ERR_EXPLAIN("Exporting bit flags hint requires string constants."); + WARN_DEPRECATED break; } if (tokenizer->get_token() != GDScriptTokenizer::TK_COMMA) { - _set_error("Expected ')' or ',' in bit flags hint."); + _set_error("Expected ',' in bit flags hint."); return; } @@ -4311,6 +4353,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { current_export.type = Variant::INT; current_export.hint = is_flags ? PROPERTY_HINT_FLAGS : PROPERTY_HINT_ENUM; + current_export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE; Dictionary enum_values = constant; List<Variant> keys; @@ -4358,10 +4401,10 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { tokenizer->advance(); } - if (tokenizer->get_token() != GDScriptTokenizer::TK_PR_VAR && tokenizer->get_token() != GDScriptTokenizer::TK_PR_ONREADY && tokenizer->get_token() != GDScriptTokenizer::TK_PR_REMOTE && tokenizer->get_token() != GDScriptTokenizer::TK_PR_MASTER && tokenizer->get_token() != GDScriptTokenizer::TK_PR_SLAVE && tokenizer->get_token() != GDScriptTokenizer::TK_PR_SYNC && tokenizer->get_token() != GDScriptTokenizer::TK_PR_REMOTESYNC && tokenizer->get_token() != GDScriptTokenizer::TK_PR_MASTERSYNC && tokenizer->get_token() != GDScriptTokenizer::TK_PR_SLAVESYNC) { + if (tokenizer->get_token() != GDScriptTokenizer::TK_PR_VAR && tokenizer->get_token() != GDScriptTokenizer::TK_PR_ONREADY && tokenizer->get_token() != GDScriptTokenizer::TK_PR_REMOTE && tokenizer->get_token() != GDScriptTokenizer::TK_PR_MASTER && tokenizer->get_token() != GDScriptTokenizer::TK_PR_PUPPET && tokenizer->get_token() != GDScriptTokenizer::TK_PR_SYNC && tokenizer->get_token() != GDScriptTokenizer::TK_PR_REMOTESYNC && tokenizer->get_token() != GDScriptTokenizer::TK_PR_MASTERSYNC && tokenizer->get_token() != GDScriptTokenizer::TK_PR_PUPPETSYNC && tokenizer->get_token() != GDScriptTokenizer::TK_PR_SLAVE) { current_export = PropertyInfo(); - _set_error("Expected 'var', 'onready', 'remote', 'master', 'slave', 'sync', 'remotesync', 'mastersync', 'slavesync'."); + _set_error("Expected 'var', 'onready', 'remote', 'master', 'puppet', 'sync', 'remotesync', 'mastersync', 'puppetsync'."); return; } @@ -4418,7 +4461,11 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { rpc_mode = MultiplayerAPI::RPC_MODE_MASTER; continue; } break; - case GDScriptTokenizer::TK_PR_SLAVE: { + case GDScriptTokenizer::TK_PR_SLAVE: +#ifdef DEBUG_ENABLED + _add_warning(GDScriptWarning::DEPRECATED_KEYWORD, tokenizer->get_token_line(), "slave", "puppet"); +#endif + case GDScriptTokenizer::TK_PR_PUPPET: { //may be fallthrough from export, ignore if so tokenizer->advance(); @@ -4435,7 +4482,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } } - rpc_mode = MultiplayerAPI::RPC_MODE_SLAVE; + rpc_mode = MultiplayerAPI::RPC_MODE_PUPPET; continue; } break; case GDScriptTokenizer::TK_PR_REMOTESYNC: @@ -4451,7 +4498,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { return; } - rpc_mode = MultiplayerAPI::RPC_MODE_SYNC; + rpc_mode = MultiplayerAPI::RPC_MODE_REMOTESYNC; continue; } break; case GDScriptTokenizer::TK_PR_MASTERSYNC: { @@ -4469,7 +4516,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { rpc_mode = MultiplayerAPI::RPC_MODE_MASTERSYNC; continue; } break; - case GDScriptTokenizer::TK_PR_SLAVESYNC: { + case GDScriptTokenizer::TK_PR_PUPPETSYNC: { //may be fallthrough from export, ignore if so tokenizer->advance(); @@ -4481,7 +4528,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { return; } - rpc_mode = MultiplayerAPI::RPC_MODE_SLAVESYNC; + rpc_mode = MultiplayerAPI::RPC_MODE_PUPPETSYNC; continue; } break; case GDScriptTokenizer::TK_PR_VAR: { @@ -4511,14 +4558,14 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { member.rpc_mode = rpc_mode; if (current_class->constant_expressions.has(member.identifier)) { - _set_error("A constant named '" + String(member.identifier) + "' alread exists in this class (at line: " + + _set_error("A constant named '" + String(member.identifier) + "' already exists in this class (at line: " + itos(current_class->constant_expressions[member.identifier].expression->line) + ")."); return; } for (int i = 0; i < current_class->variables.size(); i++) { if (current_class->variables[i].identifier == member.identifier) { - _set_error("Variable '" + String(member.identifier) + "' alread exists in this class (at line: " + + _set_error("Variable '" + String(member.identifier) + "' already exists in this class (at line: " + itos(current_class->variables[i].line) + ")."); return; } @@ -4725,14 +4772,14 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { int line = tokenizer->get_token_line(); if (current_class->constant_expressions.has(const_id)) { - _set_error("Constant '" + String(const_id) + "' alread exists in this class (at line: " + + _set_error("Constant '" + String(const_id) + "' already exists in this class (at line: " + itos(current_class->constant_expressions[const_id].expression->line) + ")."); return; } for (int i = 0; i < current_class->variables.size(); i++) { if (current_class->variables[i].identifier == const_id) { - _set_error("A variable named '" + String(const_id) + "' alread exists in this class (at line: " + + _set_error("A variable named '" + String(const_id) + "' already exists in this class (at line: " + itos(current_class->variables[i].line) + ")."); return; } @@ -4786,7 +4833,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { case GDScriptTokenizer::TK_PR_ENUM: { //multiple constant declarations.. - int last_assign = -1; // Incremented by 1 right before the assingment. + int last_assign = -1; // Incremented by 1 right before the assignment. String enum_name; Dictionary enum_dict; @@ -4823,6 +4870,20 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { StringName const_id = tokenizer->get_token_literal(); + if (current_class->constant_expressions.has(const_id)) { + _set_error("A constant named '" + String(const_id) + "' already exists in this class (at line: " + + itos(current_class->constant_expressions[const_id].expression->line) + ")."); + return; + } + + for (int i = 0; i < current_class->variables.size(); i++) { + if (current_class->variables[i].identifier == const_id) { + _set_error("A variable named '" + String(const_id) + "' already exists in this class (at line: " + + itos(current_class->variables[i].line) + ")."); + return; + } + } + tokenizer->advance(); if (tokenizer->get_token() == GDScriptTokenizer::TK_OP_ASSIGN) { @@ -5021,7 +5082,7 @@ void GDScriptParser::_determine_inheritance(ClassNode *p_class) { if (found) continue; if (p->constant_expressions.has(base)) { - if (!p->constant_expressions[base].expression->type == Node::TYPE_CONSTANT) { + if (p->constant_expressions[base].expression->type != Node::TYPE_CONSTANT) { _set_error("Could not resolve constant '" + base + "'.", p_class->line); return; } @@ -5161,6 +5222,8 @@ String GDScriptParser::DataType::to_string() const { } return class_type->name.operator String(); } break; + case UNRESOLVED: { + } break; } return "Unresolved"; @@ -5733,7 +5796,10 @@ bool GDScriptParser::_is_type_compatible(const DataType &p_container, const Data expr_native = base->base_type.native_type; expr_script = base->base_type.script_type; } - } + } break; + case DataType::BUILTIN: // Already handled above + case DataType::UNRESOLVED: // Not allowed, see above + break; } switch (p_container.kind) { @@ -5776,7 +5842,10 @@ bool GDScriptParser::_is_type_compatible(const DataType &p_container, const Data expr_class = expr_class->base_type.class_type; } return false; - } + } break; + case DataType::BUILTIN: // Already handled above + case DataType::UNRESOLVED: // Not allowed, see above + break; } return false; @@ -5793,6 +5862,13 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { case Node::TYPE_CONSTANT: { node_type = _type_from_variant(static_cast<ConstantNode *>(p_node)->value); } break; + case Node::TYPE_TYPE: { + TypeNode *tn = static_cast<TypeNode *>(p_node); + node_type.has_type = true; + node_type.is_meta_type = true; + node_type.kind = DataType::BUILTIN; + node_type.builtin_type = tn->vtype; + } break; case Node::TYPE_ARRAY: { node_type.has_type = true; node_type.kind = DataType::BUILTIN; @@ -5896,7 +5972,8 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { // yield can return anything node_type.has_type = false; } break; - case OperatorNode::OP_IS: { + case OperatorNode::OP_IS: + case OperatorNode::OP_IS_BUILTIN: { if (op->arguments.size() != 2) { _set_error("Parser bug: binary operation without 2 arguments.", op->line); @@ -5913,8 +5990,11 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { } type_type.is_meta_type = false; // Test the actual type if (!_is_type_compatible(type_type, value_type) && !_is_type_compatible(value_type, type_type)) { - // TODO: Make this a warning? - _set_error("A value of type '" + value_type.to_string() + "' will never be an instance of '" + type_type.to_string() + "'.", op->line); + if (op->op == OperatorNode::OP_IS) { + _set_error("A value of type '" + value_type.to_string() + "' will never be an instance of '" + type_type.to_string() + "'.", op->line); + } else { + _set_error("A value of type '" + value_type.to_string() + "' will never be of type '" + type_type.to_string() + "'.", op->line); + } return DataType(); } } @@ -6159,6 +6239,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { case Variant::COLOR: { error = index_type.builtin_type != Variant::INT && index_type.builtin_type != Variant::STRING; } break; + default: {} } } if (error) { @@ -6276,6 +6357,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { } } } break; + default: {} } p_node->set_datatype(_resolve_type(node_type, p_node->line)); @@ -6628,9 +6710,15 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat } } + bool rets = false; return_type.has_type = true; return_type.kind = DataType::BUILTIN; - return_type.builtin_type = Variant::get_method_return_type(base_type.builtin_type, callee_name); + return_type.builtin_type = Variant::get_method_return_type(base_type.builtin_type, callee_name, &rets); + // If the method returns, but it might return any type, (Variant::NIL), pretend we don't know the type. + // At least make sure we know that it returns + if (rets && return_type.builtin_type == Variant::NIL) { + return_type.has_type = false; + } break; } @@ -6993,7 +7081,6 @@ GDScriptParser::DataType GDScriptParser::_reduce_identifier_type(const DataType 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; @@ -7165,6 +7252,12 @@ void GDScriptParser::_check_class_level_types(ClassNode *p_class) { expr.is_constant = true; c.type = expr; c.expression->set_datatype(expr); + + DataType tmp; + if (_get_member_type(p_class->base_type, E->key(), tmp)) { + _set_error("Member '" + String(E->key()) + "' already exists in parent class.", c.expression->line); + return; + } } // Function declarations @@ -7207,7 +7300,7 @@ void GDScriptParser::_check_class_level_types(ClassNode *p_class) { return; } - // Replace assigment with implict conversion + // Replace assignment with implict conversion BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>(); convert->line = v.line; convert->function = GDScriptFunctions::TYPE_CONVERT; @@ -7585,7 +7678,7 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { lv->line); return; } - // Replace assigment with implict conversion + // Replace assignment with implict conversion BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>(); convert->line = lv->line; convert->function = GDScriptFunctions::TYPE_CONVERT; @@ -7713,7 +7806,7 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { op->line); return; } - // Replace assigment with implict conversion + // Replace assignment with implict conversion BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>(); convert->line = op->line; convert->function = GDScriptFunctions::TYPE_CONVERT; @@ -7750,7 +7843,7 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { // Figure out function name for warning String func_name = _find_function_name(op); if (func_name.empty()) { - func_name == "<undetected name>"; + func_name = "<undetected name>"; } _add_warning(GDScriptWarning::RETURN_VALUE_DISCARDED, op->line, func_name); } diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index dbe523a0b9..8121fb7f85 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -31,11 +31,11 @@ #ifndef GDSCRIPT_PARSER_H #define GDSCRIPT_PARSER_H +#include "core/map.h" +#include "core/object.h" +#include "core/script_language.h" #include "gdscript_functions.h" #include "gdscript_tokenizer.h" -#include "map.h" -#include "object.h" -#include "script_language.h" struct GDScriptDataType; struct GDScriptWarning; @@ -88,6 +88,8 @@ public: case CLASS: { return class_type == other.class_type; } break; + case UNRESOLVED: { + } break; } return false; } @@ -345,6 +347,7 @@ public: OP_PARENT_CALL, OP_YIELD, OP_IS, + OP_IS_BUILTIN, //indexing operator OP_INDEX, OP_INDEX_NAMED, @@ -552,7 +555,6 @@ private: CompletionType completion_type; StringName completion_cursor; - bool completion_static; Variant::Type completion_built_in_constant; Node *completion_node; ClassNode *completion_class; diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp index 6e7842f190..c37142b3c1 100644 --- a/modules/gdscript/gdscript_tokenizer.cpp +++ b/modules/gdscript/gdscript_tokenizer.cpp @@ -30,10 +30,10 @@ #include "gdscript_tokenizer.h" +#include "core/io/marshalls.h" +#include "core/map.h" +#include "core/print_string.h" #include "gdscript_functions.h" -#include "io/marshalls.h" -#include "map.h" -#include "print_string.h" const char *GDScriptTokenizer::token_names[TK_MAX] = { "Empty", @@ -112,10 +112,11 @@ const char *GDScriptTokenizer::token_names[TK_MAX] = { "rpc", "sync", "master", + "puppet", "slave", "remotesync", "mastersync", - "slavesync", + "puppetsync", "'['", "']'", "'{'", @@ -127,8 +128,8 @@ const char *GDScriptTokenizer::token_names[TK_MAX] = { "'.'", "'?'", "':'", - "'->'", "'$'", + "'->'", "'\\n'", "PI", "TAU", @@ -210,10 +211,11 @@ static const _kws _keyword_list[] = { { GDScriptTokenizer::TK_PR_REMOTE, "remote" }, { GDScriptTokenizer::TK_PR_MASTER, "master" }, { GDScriptTokenizer::TK_PR_SLAVE, "slave" }, + { GDScriptTokenizer::TK_PR_PUPPET, "puppet" }, { GDScriptTokenizer::TK_PR_SYNC, "sync" }, { GDScriptTokenizer::TK_PR_REMOTESYNC, "remotesync" }, { GDScriptTokenizer::TK_PR_MASTERSYNC, "mastersync" }, - { GDScriptTokenizer::TK_PR_SLAVESYNC, "slavesync" }, + { GDScriptTokenizer::TK_PR_PUPPETSYNC, "puppetsync" }, { GDScriptTokenizer::TK_PR_CONST, "const" }, { GDScriptTokenizer::TK_PR_ENUM, "enum" }, //controlflow @@ -258,11 +260,11 @@ bool GDScriptTokenizer::is_token_literal(int p_offset, bool variable_safe) const case TK_PR_SIGNAL: case TK_PR_REMOTE: case TK_PR_MASTER: - case TK_PR_SLAVE: + case TK_PR_PUPPET: case TK_PR_SYNC: case TK_PR_REMOTESYNC: case TK_PR_MASTERSYNC: - case TK_PR_SLAVESYNC: + case TK_PR_PUPPETSYNC: return true; // Literal for non-variables only: diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h index 11a291cb2e..cc894fb101 100644 --- a/modules/gdscript/gdscript_tokenizer.h +++ b/modules/gdscript/gdscript_tokenizer.h @@ -32,11 +32,11 @@ #define GDSCRIPT_TOKENIZER_H #include "core/pair.h" +#include "core/string_db.h" +#include "core/ustring.h" +#include "core/variant.h" +#include "core/vmap.h" #include "gdscript_functions.h" -#include "string_db.h" -#include "ustring.h" -#include "variant.h" -#include "vmap.h" class GDScriptTokenizer { public: @@ -119,9 +119,10 @@ public: TK_PR_SYNC, TK_PR_MASTER, TK_PR_SLAVE, + TK_PR_PUPPET, TK_PR_REMOTESYNC, TK_PR_MASTERSYNC, - TK_PR_SLAVESYNC, + TK_PR_PUPPETSYNC, TK_BRACKET_OPEN, TK_BRACKET_CLOSE, TK_CURLY_BRACKET_OPEN, diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp index 422223370b..26dcbdcf89 100644 --- a/modules/gdscript/register_types.cpp +++ b/modules/gdscript/register_types.cpp @@ -30,12 +30,12 @@ #include "register_types.h" +#include "core/io/file_access_encrypted.h" +#include "core/io/resource_loader.h" +#include "core/os/file_access.h" #include "editor/gdscript_highlighter.h" #include "gdscript.h" #include "gdscript_tokenizer.h" -#include "io/file_access_encrypted.h" -#include "io/resource_loader.h" -#include "os/file_access.h" GDScriptLanguage *script_language_gd = NULL; ResourceFormatLoaderGDScript *resource_loader_gd = NULL; diff --git a/modules/gridmap/SCsub b/modules/gridmap/SCsub index 2ffe15cd33..62b8a0ff93 100644 --- a/modules/gridmap/SCsub +++ b/modules/gridmap/SCsub @@ -6,5 +6,3 @@ Import('env_modules') env_gridmap = env_modules.Clone() env_gridmap.add_source_files(env.modules_sources, "*.cpp") - -Export('env') diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml index d5f9563600..9b9088dd82 100644 --- a/modules/gridmap/doc_classes/GridMap.xml +++ b/modules/gridmap/doc_classes/GridMap.xml @@ -10,7 +10,7 @@ A GridMap is split into a sparse collection of octants for efficient rendering and physics processing. Every octant has the same dimensions and can contain several cells. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/tutorials/3d/using_gridmaps.html</link> + <link>https://docs.godotengine.org/en/latest/tutorials/3d/using_gridmaps.html</link> </tutorials> <demos> </demos> @@ -65,7 +65,7 @@ <argument index="2" name="z" type="int"> </argument> <description> - The orientation of the cell at the grid-based X, Y and Z coordinates. -1 is retuned if the cell is empty. + The orientation of the cell at the grid-based X, Y and Z coordinates. -1 is returned if the cell is empty. </description> </method> <method name="get_collision_layer_bit" qualifiers="const"> @@ -212,9 +212,12 @@ </member> <member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask"> </member> - <member name="theme" type="MeshLibrary" setter="set_theme" getter="get_theme"> + <member name="mesh_library" type="MeshLibrary" setter="set_mesh_library" getter="get_mesh_library"> The assigned [MeshLibrary]. </member> + <member name="theme" type="MeshLibrary" setter="set_theme" getter="get_theme"> + Deprecated, use [member mesh_library] instead. + </member> </members> <constants> <constant name="INVALID_CELL_ITEM" value="-1"> diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp index 776c18da64..a8fdf8cf1f 100644 --- a/modules/gridmap/grid_map.cpp +++ b/modules/gridmap/grid_map.cpp @@ -29,37 +29,19 @@ /*************************************************************************/ #include "grid_map.h" -#include "message_queue.h" -#include "scene/3d/light.h" -#include "scene/resources/surface_tool.h" -#include "servers/visual_server.h" -#include "io/marshalls.h" -#include "os/os.h" +#include "core/io/marshalls.h" +#include "core/message_queue.h" +#include "scene/3d/light.h" #include "scene/resources/mesh_library.h" +#include "scene/resources/surface_tool.h" #include "scene/scene_string_names.h" +#include "servers/visual_server.h" bool GridMap::_set(const StringName &p_name, const Variant &p_value) { String name = p_name; - /* } else if (name=="cells") { - PoolVector<int> cells = p_value; - int amount=cells.size(); - PoolVector<int>::Read r = cells.read(); - ERR_FAIL_COND_V(amount&1,false); // not even - cell_map.clear(); - for(int i=0;i<amount/3;i++) { - - - IndexKey ik; - ik.key=decode_uint64(&r[i*3]); - Cell cell; - cell.cell=uint32_t(r[i*+1]); - cell_map[ik]=cell; - - } - _recreate_octant_data();*/ if (name == "data") { Dictionary d = p_value; @@ -80,7 +62,9 @@ bool GridMap::_set(const StringName &p_name, const Variant &p_value) { cell_map[ik] = cell; } } + _recreate_octant_data(); + } else if (name == "baked_meshes") { clear_baked_meshes(); @@ -103,8 +87,9 @@ bool GridMap::_set(const StringName &p_name, const Variant &p_value) { _recreate_octant_data(); - } else + } else { return false; + } return true; } @@ -211,13 +196,17 @@ bool GridMap::get_collision_layer_bit(int p_bit) const { #ifndef DISABLE_DEPRECATED void GridMap::set_theme(const Ref<MeshLibrary> &p_theme) { - WARN_PRINTS("GridMap.theme/set_theme() is deprecated and will be removed in a future version. Use GridMap.mesh_library/set_mesh_library() instead."); + ERR_EXPLAIN("GridMap.theme/set_theme() is deprecated and will be removed in a future version. Use GridMap.mesh_library/set_mesh_library() instead."); + WARN_DEPRECATED + set_mesh_library(p_theme); } Ref<MeshLibrary> GridMap::get_theme() const { - WARN_PRINTS("GridMap.theme/get_theme() is deprecated and will be removed in a future version. Use GridMap.mesh_library/get_mesh_library() instead."); + ERR_EXPLAIN("GridMap.theme/get_theme() is deprecated and will be removed in a future version. Use GridMap.mesh_library/get_mesh_library() instead."); + WARN_DEPRECATED + return get_mesh_library(); } #endif // DISABLE_DEPRECATED @@ -903,7 +892,7 @@ void GridMap::_bind_methods() { ClassDB::bind_method(D_METHOD("make_baked_meshes", "gen_lightmap_uv", "lightmap_uv_texel_size"), &GridMap::make_baked_meshes, DEFVAL(false), DEFVAL(0.1)); #ifndef DISABLE_DEPRECATED - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "theme", PROPERTY_HINT_RESOURCE_TYPE, "MeshLibrary", PROPERTY_USAGE_NOEDITOR), "set_theme", "get_theme"); + ADD_PROPERTYNO(PropertyInfo(Variant::OBJECT, "theme", PROPERTY_HINT_RESOURCE_TYPE, "MeshLibrary", 0), "set_theme", "get_theme"); #endif // DISABLE_DEPRECATED ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh_library", PROPERTY_HINT_RESOURCE_TYPE, "MeshLibrary"), "set_mesh_library", "get_mesh_library"); @@ -1077,8 +1066,6 @@ void GridMap::make_baked_meshes(bool p_gen_lightmap_uv, float p_lightmap_uv_texe } } - int ofs = 0; - for (Map<OctantKey, Map<Ref<Material>, Ref<SurfaceTool> > >::Element *E = surface_map.front(); E; E = E->next()) { Ref<ArrayMesh> mesh; diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp index 90e28129cc..fae88042af 100644 --- a/modules/gridmap/grid_map_editor_plugin.cpp +++ b/modules/gridmap/grid_map_editor_plugin.cpp @@ -29,14 +29,14 @@ /*************************************************************************/ #include "grid_map_editor_plugin.h" +#include "core/os/input.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" #include "editor/plugins/spatial_editor_plugin.h" -#include "os/input.h" #include "scene/3d/camera.h" -#include "geometry.h" -#include "os/keyboard.h" +#include "core/math/geometry.h" +#include "core/os/keyboard.h" void GridMapEditor::_node_removed(Node *p_node) { @@ -597,29 +597,31 @@ bool GridMapEditor::forward_spatial_input_event(Camera *p_camera, const Ref<Inpu if (mb->get_button_index() == BUTTON_LEFT) { if (input_action == INPUT_DUPLICATE) { - //paste _duplicate_paste(); input_action = INPUT_NONE; _update_duplicate_indicator(); } else if (mb->get_shift()) { input_action = INPUT_SELECT; - } else if (mb->get_command()) + } else if (mb->get_command()) { input_action = INPUT_COPY; - else { + } else { input_action = INPUT_PAINT; set_items.clear(); } - } else if (mb->get_button_index() == BUTTON_RIGHT) + } else if (mb->get_button_index() == BUTTON_RIGHT) { if (input_action == INPUT_DUPLICATE) { - input_action = INPUT_NONE; _update_duplicate_indicator(); } else if (mb->get_shift()) { input_action = INPUT_ERASE; set_items.clear(); - } else + } else { return false; + } + } else { + return false; + } return do_input_action(p_camera, Point2(mb->get_position().x, mb->get_position().y), true); } else { @@ -645,7 +647,7 @@ bool GridMapEditor::forward_spatial_input_event(Camera *p_camera, const Ref<Inpu } set_items.clear(); input_action = INPUT_NONE; - return true; + return set_items.size() > 0; } if (mb->get_button_index() == BUTTON_LEFT && input_action != INPUT_NONE) { diff --git a/modules/gridmap/register_types.cpp b/modules/gridmap/register_types.cpp index a3ceea10af..030286d651 100644 --- a/modules/gridmap/register_types.cpp +++ b/modules/gridmap/register_types.cpp @@ -30,7 +30,7 @@ #include "register_types.h" #ifndef _3D_DISABLED -#include "class_db.h" +#include "core/class_db.h" #include "grid_map.h" #include "grid_map_editor_plugin.h" #endif diff --git a/modules/hdr/image_loader_hdr.cpp b/modules/hdr/image_loader_hdr.cpp index d592c19b97..fc6779ce86 100644 --- a/modules/hdr/image_loader_hdr.cpp +++ b/modules/hdr/image_loader_hdr.cpp @@ -30,8 +30,8 @@ #include "image_loader_hdr.h" -#include "os/os.h" -#include "print_string.h" +#include "core/os/os.h" +#include "core/print_string.h" #include "thirdparty/tinyexr/tinyexr.h" diff --git a/modules/hdr/image_loader_hdr.h b/modules/hdr/image_loader_hdr.h index 3cce483745..7175b38cc8 100644 --- a/modules/hdr/image_loader_hdr.h +++ b/modules/hdr/image_loader_hdr.h @@ -31,7 +31,7 @@ #ifndef IMAGE_LOADER_HDR_H #define IMAGE_LOADER_HDR_H -#include "io/image_loader.h" +#include "core/io/image_loader.h" /** @author Juan Linietsky <reduzio@gmail.com> diff --git a/modules/jpg/SCsub b/modules/jpg/SCsub index e72dc6a1ca..d5f87905eb 100644 --- a/modules/jpg/SCsub +++ b/modules/jpg/SCsub @@ -13,8 +13,11 @@ thirdparty_sources = [ ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env_jpg.add_source_files(env.modules_sources, thirdparty_sources) env_jpg.Append(CPPPATH=[thirdparty_dir]) +env_thirdparty = env_jpg.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + # Godot's own source files env_jpg.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/jpg/image_loader_jpegd.cpp b/modules/jpg/image_loader_jpegd.cpp index 437c0d57fa..24d40111cf 100644 --- a/modules/jpg/image_loader_jpegd.cpp +++ b/modules/jpg/image_loader_jpegd.cpp @@ -30,8 +30,8 @@ #include "image_loader_jpegd.h" -#include "os/os.h" -#include "print_string.h" +#include "core/os/os.h" +#include "core/print_string.h" #include <jpgd.h> #include <string.h> @@ -48,9 +48,9 @@ Error jpeg_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p const int image_width = decoder.get_width(); const int image_height = decoder.get_height(); - int comps = decoder.get_num_components(); - if (comps == 3) - comps = 4; //weird + const int comps = decoder.get_num_components(); + if (comps != 1 && comps != 3) + return ERR_FILE_CORRUPT; if (decoder.begin_decoding() != jpgd::JPGD_SUCCESS) return ERR_FILE_CORRUPT; @@ -73,7 +73,19 @@ Error jpeg_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p } jpgd::uint8 *pDst = pImage_data + y * dst_bpl; - memcpy(pDst, pScan_line, dst_bpl); + + if (comps == 1) { + memcpy(pDst, pScan_line, dst_bpl); + } else { + // For images with more than 1 channel pScan_line will always point to a buffer + // containing 32-bit RGBA pixels. Alpha is always 255 and we ignore it. + for (int x = 0; x < image_width; x++) { + pDst[0] = pScan_line[x * 4 + 0]; + pDst[1] = pScan_line[x * 4 + 1]; + pDst[2] = pScan_line[x * 4 + 2]; + pDst += 3; + } + } } //all good @@ -82,7 +94,7 @@ Error jpeg_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p if (comps == 1) fmt = Image::FORMAT_L8; else - fmt = Image::FORMAT_RGBA8; + fmt = Image::FORMAT_RGB8; dw = PoolVector<uint8_t>::Write(); p_image->create(image_width, image_height, 0, fmt, data); diff --git a/modules/jpg/image_loader_jpegd.h b/modules/jpg/image_loader_jpegd.h index 3e3ac5217f..b5f0637c9b 100644 --- a/modules/jpg/image_loader_jpegd.h +++ b/modules/jpg/image_loader_jpegd.h @@ -31,7 +31,7 @@ #ifndef IMAGE_LOADER_JPG_H #define IMAGE_LOADER_JPG_H -#include "io/image_loader.h" +#include "core/io/image_loader.h" /** @author Juan Linietsky <reduzio@gmail.com> diff --git a/modules/mbedtls/SCsub b/modules/mbedtls/SCsub index d11d7a7ec7..0c6c703e16 100755 --- a/modules/mbedtls/SCsub +++ b/modules/mbedtls/SCsub @@ -91,8 +91,12 @@ if env['builtin_mbedtls']: thirdparty_dir = "#thirdparty/mbedtls/library/" thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_mbed_tls.add_source_files(env.modules_sources, thirdparty_sources) + env_mbed_tls.Prepend(CPPPATH=["#thirdparty/mbedtls/include/"]) + env_thirdparty = env_mbed_tls.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + # Module sources env_mbed_tls.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/mbedtls/stream_peer_mbed_tls.cpp b/modules/mbedtls/stream_peer_mbed_tls.cpp index 884c26ddfe..5c81f32e9e 100755 --- a/modules/mbedtls/stream_peer_mbed_tls.cpp +++ b/modules/mbedtls/stream_peer_mbed_tls.cpp @@ -29,8 +29,11 @@ /*************************************************************************/ #include "stream_peer_mbed_tls.h" -#include "mbedtls/platform_util.h" -#include "os/file_access.h" + +#include "core/io/stream_peer_tcp.h" +#include "core/os/file_access.h" + +#include <mbedtls/platform_util.h> static void my_debug(void *ctx, int level, const char *file, int line, @@ -98,12 +101,16 @@ Error StreamPeerMbedTLS::_do_handshake() { int ret = 0; while ((ret = mbedtls_ssl_handshake(&ssl)) != 0) { if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + // An error occurred. ERR_PRINTS("TLS handshake error: " + itos(ret)); _print_error(ret); disconnect_from_stream(); status = STATUS_ERROR; return FAILED; - } else if (!blocking_handshake) { + } + + // Handshake is still in progress. + if (!blocking_handshake) { // Will retry via poll later return OK; } @@ -192,7 +199,12 @@ Error StreamPeerMbedTLS::put_partial_data(const uint8_t *p_data, int p_bytes, in int ret = mbedtls_ssl_write(&ssl, p_data, p_bytes); if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) { - ret = 0; // non blocking io + // Non blocking IO + ret = 0; + } else if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { + // Clean close + disconnect_from_stream(); + return ERR_FILE_EOF; } else if (ret <= 0) { _print_error(ret); disconnect_from_stream(); @@ -234,6 +246,10 @@ Error StreamPeerMbedTLS::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r int ret = mbedtls_ssl_read(&ssl, p_buffer, p_bytes); if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) { ret = 0; // non blocking io + } else if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { + // Clean close + disconnect_from_stream(); + return ERR_FILE_EOF; } else if (ret <= 0) { _print_error(ret); disconnect_from_stream(); @@ -256,9 +272,22 @@ void StreamPeerMbedTLS::poll() { int ret = mbedtls_ssl_read(&ssl, NULL, 0); - if (ret < 0 && ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) { + // Nothing to read/write (non blocking IO) + } else if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { + // Clean close (disconnect) + disconnect_from_stream(); + return; + } else if (ret < 0) { _print_error(ret); disconnect_from_stream(); + return; + } + + Ref<StreamPeerTCP> tcp = base; + if (tcp.is_valid() && tcp->get_status() != StreamPeerTCP::STATUS_CONNECTED) { + disconnect_from_stream(); + return; } } @@ -282,6 +311,12 @@ void StreamPeerMbedTLS::disconnect_from_stream() { if (status != STATUS_CONNECTED && status != STATUS_HANDSHAKING) return; + Ref<StreamPeerTCP> tcp = base; + if (tcp.is_valid() && tcp->get_status() == StreamPeerTCP::STATUS_CONNECTED) { + // We are still connected on the socket, try to send close notity. + mbedtls_ssl_close_notify(&ssl); + } + _cleanup(); } @@ -317,15 +352,13 @@ void StreamPeerMbedTLS::initialize_ssl() { mbedtls_debug_set_threshold(1); #endif - PoolByteArray cert_array = StreamPeerSSL::get_project_cert_array(); - - if (cert_array.size() > 0) - _load_certs(cert_array); - available = true; } void StreamPeerMbedTLS::finalize_ssl() { + available = false; + _create = NULL; + load_certs_func = NULL; mbedtls_x509_crt_free(&cacert); } diff --git a/modules/mbedtls/stream_peer_mbed_tls.h b/modules/mbedtls/stream_peer_mbed_tls.h index 7f4e5a4513..abf87b79cc 100755 --- a/modules/mbedtls/stream_peer_mbed_tls.h +++ b/modules/mbedtls/stream_peer_mbed_tls.h @@ -31,14 +31,14 @@ #ifndef STREAM_PEER_OPEN_SSL_H #define STREAM_PEER_OPEN_SSL_H -#include "io/stream_peer_ssl.h" - -#include "mbedtls/config.h" -#include "mbedtls/ctr_drbg.h" -#include "mbedtls/debug.h" -#include "mbedtls/entropy.h" -#include "mbedtls/net.h" -#include "mbedtls/ssl.h" +#include "core/io/stream_peer_ssl.h" + +#include <mbedtls/config.h> +#include <mbedtls/ctr_drbg.h> +#include <mbedtls/debug.h> +#include <mbedtls/entropy.h> +#include <mbedtls/net.h> +#include <mbedtls/ssl.h> #include <stdio.h> #include <stdlib.h> diff --git a/modules/mobile_vr/SCsub b/modules/mobile_vr/SCsub index b4e2edcca1..4bd184f025 100644 --- a/modules/mobile_vr/SCsub +++ b/modules/mobile_vr/SCsub @@ -1,13 +1,8 @@ #!/usr/bin/env python -import os -import methods - Import('env') Import('env_modules') env_mobile_vr = env_modules.Clone() env_mobile_vr.add_source_files(env.modules_sources, '*.cpp') - -SConscript("shaders/SCsub") diff --git a/modules/mobile_vr/mobile_vr_interface.cpp b/modules/mobile_vr/mobile_vr_interface.cpp index e2c630565f..87e4ddd900 100644 --- a/modules/mobile_vr/mobile_vr_interface.cpp +++ b/modules/mobile_vr/mobile_vr_interface.cpp @@ -297,47 +297,6 @@ bool MobileVRInterface::initialize() { mag_current_min = Vector3(0, 0, 0); mag_current_max = Vector3(0, 0, 0); - // build our shader - if (lens_shader == NULL) { - ///@TODO need to switch between GLES2 and GLES3 version, Reduz suggested moving this into our drivers and making this a core shader - // create a shader - lens_shader = new LensDistortedShaderGLES3(); - - // create our shader stuff - lens_shader->init(); - - glGenBuffers(1, &half_screen_quad); - glBindBuffer(GL_ARRAY_BUFFER, half_screen_quad); - { - /* clang-format off */ - const float qv[16] = { - 0, -1, - -1, -1, - 0, 1, - -1, 1, - 1, 1, - 1, 1, - 1, -1, - 1, -1, - }; - /* clang-format on */ - - glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 16, qv, GL_STATIC_DRAW); - } - - glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind - - glGenVertexArrays(1, &half_screen_array); - glBindVertexArray(half_screen_array); - glBindBuffer(GL_ARRAY_BUFFER, half_screen_quad); - glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, 0); - glEnableVertexAttribArray(0); - glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, ((uint8_t *)NULL) + 8); - glEnableVertexAttribArray(4); - glBindVertexArray(0); - glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind - } - // reset our orientation orientation = Basis(); @@ -360,15 +319,6 @@ void MobileVRInterface::uninitialize() { arvr_server->clear_primary_interface_if(this); } - // cleanup our shader and buffers - if (lens_shader != NULL) { - glDeleteVertexArrays(1, &half_screen_array); - glDeleteBuffers(1, &half_screen_quad); - - delete lens_shader; - lens_shader = NULL; - } - initialized = false; }; }; @@ -444,46 +394,29 @@ void MobileVRInterface::commit_for_eye(ARVRInterface::Eyes p_eye, RID p_render_t // We must have a valid render target ERR_FAIL_COND(!p_render_target.is_valid()); - // We must have an initialised shader - ERR_FAIL_COND(lens_shader != NULL); - // Because we are rendering to our device we must use our main viewport! ERR_FAIL_COND(p_screen_rect == Rect2()); - float offset_x = 0.0; - float aspect_ratio = 0.5 * p_screen_rect.size.x / p_screen_rect.size.y; + Rect2 dest = p_screen_rect; Vector2 eye_center; + // we output half a screen + dest.size.x *= 0.5; + if (p_eye == ARVRInterface::EYE_LEFT) { - offset_x = -1.0; eye_center.x = ((-intraocular_dist / 2.0) + (display_width / 4.0)) / (display_width / 2.0); } else if (p_eye == ARVRInterface::EYE_RIGHT) { + dest.position.x = dest.size.x; eye_center.x = ((intraocular_dist / 2.0) - (display_width / 4.0)) / (display_width / 2.0); } + // we don't offset the eye center vertically (yet) + eye_center.y = 0.0; // unset our render target so we are outputting to our main screen by making RasterizerStorageGLES3::system_fbo our current FBO VSG::rasterizer->set_current_render_target(RID()); - // now output to screen - // VSG::rasterizer->blit_render_target_to_screen(p_render_target, screen_rect, 0); - - // get our render target - RID eye_texture = VSG::storage->render_target_get_texture(p_render_target); - uint32_t texid = VS::get_singleton()->texture_get_texid(eye_texture); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, texid); - - lens_shader->bind(); - lens_shader->set_uniform(LensDistortedShaderGLES3::OFFSET_X, offset_x); - lens_shader->set_uniform(LensDistortedShaderGLES3::K1, k1); - lens_shader->set_uniform(LensDistortedShaderGLES3::K2, k2); - lens_shader->set_uniform(LensDistortedShaderGLES3::EYE_CENTER, eye_center); - lens_shader->set_uniform(LensDistortedShaderGLES3::UPSCALE, oversample); - lens_shader->set_uniform(LensDistortedShaderGLES3::ASPECT_RATIO, aspect_ratio); - - glBindVertexArray(half_screen_array); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - glBindVertexArray(0); + // and output + VSG::rasterizer->output_lens_distorted_to_screen(p_render_target, dest, k1, k2, eye_center, oversample); }; void MobileVRInterface::process() { @@ -506,8 +439,6 @@ MobileVRInterface::MobileVRInterface() { k1 = 0.215; k2 = 0.215; last_ticks = 0; - - lens_shader = NULL; }; MobileVRInterface::~MobileVRInterface() { diff --git a/modules/mobile_vr/mobile_vr_interface.h b/modules/mobile_vr/mobile_vr_interface.h index cee0cca90e..05b6331f94 100644 --- a/modules/mobile_vr/mobile_vr_interface.h +++ b/modules/mobile_vr/mobile_vr_interface.h @@ -34,8 +34,6 @@ #include "servers/arvr/arvr_interface.h" #include "servers/arvr/arvr_positional_tracker.h" -#include "shaders/lens_distorted.glsl.gen.h" - /** @author Bastiaan Olij <mux213@gmail.com> @@ -58,10 +56,6 @@ private: float eye_height; uint64_t last_ticks; - LensDistortedShaderGLES3 *lens_shader; - GLuint half_screen_quad; - GLuint half_screen_array; - real_t intraocular_dist; real_t display_width; real_t display_to_lens; diff --git a/modules/mobile_vr/shaders/SCsub b/modules/mobile_vr/shaders/SCsub deleted file mode 100644 index cf53c9ebe0..0000000000 --- a/modules/mobile_vr/shaders/SCsub +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env python - -Import('env') - -if 'GLES3_GLSL' in env['BUILDERS']: - env.GLES3_GLSL('lens_distorted.glsl'); - diff --git a/modules/mobile_vr/shaders/lens_distorted.glsl b/modules/mobile_vr/shaders/lens_distorted.glsl deleted file mode 100644 index 5a2975d737..0000000000 --- a/modules/mobile_vr/shaders/lens_distorted.glsl +++ /dev/null @@ -1,59 +0,0 @@ -[vertex] - -layout(location=0) in highp vec4 vertex_attrib; -layout(location=4) in vec2 uv_in; - -uniform float offset_x; - -out vec2 uv_interp; - -void main() { - - uv_interp = uv_in; - gl_Position = vec4(vertex_attrib.x + offset_x, vertex_attrib.y, 0.0, 1.0); -} - -[fragment] - -uniform sampler2D source; //texunit:0 - -uniform vec2 eye_center; -uniform float k1; -uniform float k2; -uniform float upscale; -uniform float aspect_ratio; - -in vec2 uv_interp; - -layout(location = 0) out vec4 frag_color; - -void main() { - vec2 coords = uv_interp; - vec2 offset = coords - eye_center; - - // take aspect ratio into account - offset.y /= aspect_ratio; - - // distort - vec2 offset_sq = offset * offset; - float radius_sq = offset_sq.x + offset_sq.y; - float radius_s4 = radius_sq * radius_sq; - float distortion_scale = 1.0 + (k1 * radius_sq) + (k2 * radius_s4); - offset *= distortion_scale; - - // reapply aspect ratio - offset.y *= aspect_ratio; - - // add our eye center back in - coords = offset + eye_center; - coords /= upscale; - - // and check our color - if (coords.x < -1.0 || coords.y < -1.0 || coords.x > 1.0 || coords.y > 1.0) { - frag_color = vec4(0.0, 0.0, 0.0, 1.0); - } else { - coords = (coords + vec2(1.0)) / vec2(2.0); - frag_color = textureLod(source, coords, 0.0); - } -} - diff --git a/modules/mono/SCsub b/modules/mono/SCsub index f3cf4c9c5d..0e5dd9b4cf 100644 --- a/modules/mono/SCsub +++ b/modules/mono/SCsub @@ -7,21 +7,24 @@ env_mono = env_modules.Clone() # TODO move functions to their own modules -def make_cs_files_header(src, dst): +def make_cs_files_header(src, dst, version_dst): from compat import byte_to_str with open(dst, 'w') as header: - header.write('/* This is an automatically generated file; DO NOT EDIT! OK THX */\n') - header.write('#ifndef _CS_FILES_DATA_H\n') - header.write('#define _CS_FILES_DATA_H\n\n') - header.write('#include "map.h"\n') - header.write('#include "ustring.h"\n') + header.write('/* THIS FILE IS GENERATED DO NOT EDIT */\n') + header.write('#ifndef CS_COMPRESSED_H\n') + header.write('#define CS_COMPRESSED_H\n\n') + header.write('#ifdef TOOLS_ENABLED\n\n') + header.write('#include "core/map.h"\n') + header.write('#include "core/ustring.h"\n') inserted_files = '' import os latest_mtime = 0 + cs_file_count = 0 for root, _, files in os.walk(src): files = [f for f in files if f.endswith('.cs')] for file in files: + cs_file_count += 1 filepath = os.path.join(root, file) filepath_src_rel = os.path.relpath(filepath, src) mtime = os.path.getmtime(filepath) @@ -31,21 +34,21 @@ def make_cs_files_header(src, dst): decomp_size = len(buf) import zlib buf = zlib.compress(buf) - name = os.path.splitext(os.path.normpath(filepath_src_rel))[0].strip(os.sep).replace(os.sep, '_').replace('.', '_dotto_') - header.write('\nstatic const int _cs_' + name + '_compressed_size = ' + str(len(buf)) + ';\n') + name = str(cs_file_count) + header.write('\n') + header.write('// ' + filepath_src_rel + '\n') + header.write('static const int _cs_' + name + '_compressed_size = ' + str(len(buf)) + ';\n') header.write('static const int _cs_' + name + '_uncompressed_size = ' + str(decomp_size) + ';\n') header.write('static const unsigned char _cs_' + name + '_compressed[] = { ') for i, buf_idx in enumerate(range(len(buf))): if i > 0: header.write(', ') header.write(byte_to_str(buf[buf_idx])) - inserted_files += '\tr_files.insert("' + filepath_src_rel + '", ' \ + inserted_files += '\tr_files.insert("' + filepath_src_rel.replace('\\', '\\\\') + '", ' \ 'CompressedFile(_cs_' + name + '_compressed_size, ' \ '_cs_' + name + '_uncompressed_size, ' \ '_cs_' + name + '_compressed));\n' header.write(' };\n') - glue_version = int(latest_mtime) # The latest modified time will do for now - header.write('\n#define CS_GLUE_VERSION UINT32_C(' + str(glue_version) + ')\n') header.write('\nstruct CompressedFile\n' '{\n' '\tint compressed_size;\n' '\tint uncompressed_size;\n' '\tconst unsigned char* data;\n' '\n\tCompressedFile(int p_comp_size, int p_uncomp_size, const unsigned char* p_data)\n' @@ -53,17 +56,28 @@ def make_cs_files_header(src, dst): '\t\tdata = p_data;\n' '\t}\n' '\n\tCompressedFile() {}\n' '};\n' '\nvoid get_compressed_files(Map<String, CompressedFile>& r_files)\n' '{\n' + inserted_files + '}\n' ) - header.write('#endif // _CS_FILES_DATA_H') + header.write('\n#endif // TOOLS_ENABLED\n') + header.write('\n#endif // CS_COMPRESSED_H\n') + + glue_version = int(latest_mtime) # The latest modified time will do for now + + with open(version_dst, 'w') as version_header: + version_header.write('/* THIS FILE IS GENERATED DO NOT EDIT */\n') + version_header.write('#ifndef CS_GLUE_VERSION_H\n') + version_header.write('#define CS_GLUE_VERSION_H\n\n') + version_header.write('#define CS_GLUE_VERSION UINT32_C(' + str(glue_version) + ')\n') + version_header.write('\n#endif // CS_GLUE_VERSION_H\n') env_mono.add_source_files(env.modules_sources, '*.cpp') +env_mono.add_source_files(env.modules_sources, 'glue/*.cpp') env_mono.add_source_files(env.modules_sources, 'mono_gd/*.cpp') 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') + # NOTE: It is safe to generate this file here, since this is still executed serially + make_cs_files_header('glue/Managed/Files', 'glue/cs_compressed.gen.h', 'glue/cs_glue_version.gen.h') vars = Variables() vars.Add(BoolVariable('mono_glue', 'Build with the mono glue sources', True)) @@ -72,12 +86,7 @@ vars.Update(env_mono) # Glue sources if env_mono['mono_glue']: - env_mono.add_source_files(env.modules_sources, 'glue/*.cpp') -else: - env_mono.Append(CPPDEFINES=['MONO_GLUE_DISABLED']) - -if ARGUMENTS.get('yolo_copy', False): - env_mono.Append(CPPDEFINES=['YOLO_COPY']) + env_mono.Append(CPPDEFINES=['MONO_GLUE_ENABLED']) # Configure TLS checks @@ -93,6 +102,75 @@ env_mono = conf.Finish() import os +def find_nuget_unix(): + import os.path + import sys + + hint_dirs = ['/opt/novell/mono/bin'] + if sys.platform == 'darwin': + hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin', '/usr/local/var/homebrew/linked/mono/bin'] + hint_dirs + + for hint_dir in hint_dirs: + hint_path = os.path.join(hint_dir, 'nuget') + if os.path.isfile(hint_path): + return hint_path + elif os.path.isfile(hint_path + '.exe'): + return hint_path + '.exe' + + for hint_dir in os.environ['PATH'].split(os.pathsep): + hint_dir = hint_dir.strip('"') + hint_path = os.path.join(hint_dir, 'nuget') + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + if os.path.isfile(hint_path + '.exe') and os.access(hint_path + '.exe', os.X_OK): + return hint_path + '.exe' + + return None + + +def find_nuget_windows(): + import mono_reg_utils as monoreg + + mono_root = '' + bits = env['bits'] + + if bits == '32': + if os.getenv('MONO32_PREFIX'): + mono_root = os.getenv('MONO32_PREFIX') + else: + mono_root = monoreg.find_mono_root_dir(bits) + else: + if os.getenv('MONO64_PREFIX'): + mono_root = os.getenv('MONO64_PREFIX') + else: + mono_root = monoreg.find_mono_root_dir(bits) + + if mono_root: + mono_bin_dir = os.path.join(mono_root, 'bin') + nuget_mono = os.path.join(mono_bin_dir, 'nuget.bat') + + if os.path.isfile(nuget_mono): + return nuget_mono + + # Standalone NuGet + + for hint_dir in os.environ['PATH'].split(os.pathsep): + hint_dir = hint_dir.strip('"') + hint_path = os.path.join(hint_dir, 'nuget.exe') + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + + if 'NUGET_PATH' in os.environ: + hint_path = os.environ['NUGET_PATH'] + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + hint_path = os.path.join(hint_path, 'nuget.exe') + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + + return None + + def find_msbuild_unix(filename): import os.path import sys @@ -122,6 +200,7 @@ def find_msbuild_unix(filename): def find_msbuild_windows(): import mono_reg_utils as monoreg + mono_root = '' bits = env['bits'] if bits == '32': @@ -166,14 +245,17 @@ def mono_build_solution(source, target, env): import mono_reg_utils as monoreg from shutil import copyfile - framework_path = '' + sln_path = os.path.abspath(str(source[0])) + target_path = os.path.abspath(str(target[0])) + framework_path = '' msbuild_env = os.environ.copy() # Needed when running from Developer Command Prompt for VS if 'PLATFORM' in msbuild_env: del msbuild_env['PLATFORM'] + # Find MSBuild if os.name == 'nt': msbuild_info = find_msbuild_windows() if msbuild_info is None: @@ -203,11 +285,27 @@ def mono_build_solution(source, target, env): print('MSBuild path: ' + msbuild_path) + # Find NuGet + nuget_path = find_nuget_windows() if os.name == 'nt' else find_nuget_unix() + if nuget_path is None: + raise RuntimeError('Cannot find NuGet executable') + + print('NuGet path: ' + nuget_path) + + # Do NuGet restore + + try: + subprocess.check_call([nuget_path, 'restore', sln_path]) + except subprocess.CalledProcessError: + raise RuntimeError('GodotSharpTools: NuGet restore failed') + + # Build solution + build_config = 'Release' msbuild_args = [ msbuild_path, - os.path.abspath(str(source[0])), + sln_path, '/p:Configuration=' + build_config, ] @@ -217,30 +315,31 @@ def mono_build_solution(source, target, env): try: subprocess.check_call(msbuild_args, env=msbuild_env) except subprocess.CalledProcessError: - raise RuntimeError('GodotSharpTools build failed') + raise RuntimeError('GodotSharpTools: Build failed') - src_dir = os.path.abspath(os.path.join(str(source[0]), os.pardir, 'bin', build_config)) - dst_dir = os.path.abspath(os.path.join(str(target[0]), os.pardir)) + # Copy files + + src_dir = os.path.abspath(os.path.join(sln_path, os.pardir, 'bin', build_config)) + dst_dir = os.path.abspath(os.path.join(target_path, os.pardir)) + asm_file = 'GodotSharpTools.dll' if not os.path.isdir(dst_dir): if os.path.exists(dst_dir): raise RuntimeError('Target directory is a file') os.makedirs(dst_dir) - asm_file = 'GodotSharpTools.dll' - copyfile(os.path.join(src_dir, asm_file), os.path.join(dst_dir, asm_file)) -output_dir = Dir('#bin').abspath -assemblies_output_dir = Dir(env['mono_assemblies_output_dir']).abspath + # Dependencies + copyfile(os.path.join(src_dir, "DotNet.Glob.dll"), os.path.join(dst_dir, "DotNet.Glob.dll")) -mono_sln_builder = Builder(action=mono_build_solution) -env_mono.Append(BUILDERS={'MonoBuildSolution': mono_sln_builder}) -env_mono.MonoBuildSolution( - os.path.join(assemblies_output_dir, 'GodotSharpTools.dll'), - 'editor/GodotSharpTools/GodotSharpTools.sln' -) - -if os.path.normpath(output_dir) != os.path.normpath(assemblies_output_dir): - rel_assemblies_output_dir = os.path.relpath(assemblies_output_dir, output_dir) - env_mono.Append(CPPDEFINES={'GD_MONO_EDITOR_ASSEMBLIES_DIR': rel_assemblies_output_dir}) +if env['tools']: + output_dir = Dir('#bin').abspath + editor_tools_dir = os.path.join(output_dir, 'GodotSharp', 'Tools') + + mono_sln_builder = Builder(action=mono_build_solution) + env_mono.Append(BUILDERS={'MonoBuildSolution': mono_sln_builder}) + env_mono.MonoBuildSolution( + os.path.join(editor_tools_dir, 'GodotSharpTools.dll'), + 'editor/GodotSharpTools/GodotSharpTools.sln' + ) diff --git a/modules/mono/config.py b/modules/mono/config.py index 70fd1a35f1..8427103ee7 100644 --- a/modules/mono/config.py +++ b/modules/mono/config.py @@ -5,7 +5,7 @@ import sys import subprocess from distutils.version import LooseVersion -from SCons.Script import BoolVariable, Dir, Environment, File, PathVariable, SCons, Variables +from SCons.Script import BoolVariable, Dir, Environment, File, SCons, Variables monoreg = imp.load_source('mono_reg_utils', 'modules/mono/mono_reg_utils.py') @@ -55,30 +55,20 @@ def copy_file(src_dir, dst_dir, name): copyfile(src_path, dst_path) -def custom_path_is_dir_create(key, val, env): - """Validator to check if Path is a directory, creating it if it does not exist. - Similar to PathIsDirCreate, except it uses SCons.Script.Dir() and - SCons.Script.File() in order to support the '#' top level directory token. - """ - # Dir constructor will throw an error if the path points to a file - fsDir = Dir(val) - if not fsDir.exists: - os.makedirs(fsDir.abspath) - - def configure(env): env.use_ptrcall = True env.add_module_version_string('mono') envvars = Variables() envvars.Add(BoolVariable('mono_static', 'Statically link mono', False)) - envvars.Add(PathVariable('mono_assemblies_output_dir', 'Path to the assemblies output directory', '#bin', custom_path_is_dir_create)) + envvars.Add(BoolVariable('copy_mono_root', 'Make a copy of the mono installation directory to bundle with the editor', False)) envvars.Update(env) bits = env['bits'] + tools_enabled = env['tools'] mono_static = env['mono_static'] - assemblies_output_dir = Dir(env['mono_assemblies_output_dir']).abspath + copy_mono_root = env['copy_mono_root'] mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0'] @@ -99,6 +89,8 @@ def configure(env): if not mono_root: raise RuntimeError('Mono installation directory not found') + print('Found Mono root directory: ' + mono_root) + mono_version = mono_root_try_find_mono_version(mono_root) configure_for_mono_version(env, mono_version) @@ -149,8 +141,6 @@ def configure(env): raise RuntimeError('Could not find mono shared library in: ' + mono_bin_path) copy_file(mono_bin_path, 'bin', mono_dll_name + '.dll') - - copy_file(os.path.join(mono_lib_path, 'mono', '4.5'), assemblies_output_dir, 'mscorlib.dll') else: sharedlib_ext = '.dylib' if sys.platform == 'darwin' else '.so' @@ -164,6 +154,14 @@ def configure(env): if os.getenv('MONO64_PREFIX'): mono_root = os.getenv('MONO64_PREFIX') + if not mono_root and sys.platform == 'darwin': + # Try with some known directories under OSX + hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current', '/usr/local/var/homebrew/linked/mono'] + for hint_dir in hint_dirs: + if os.path.isdir(hint_dir): + mono_root = hint_dir + break + # We can't use pkg-config to link mono statically, # but we can still use it to find the mono root directory if not mono_root and mono_static: @@ -172,6 +170,8 @@ def configure(env): raise RuntimeError('Building with mono_static=yes, but failed to find the mono prefix with pkg-config. Specify one manually') if mono_root: + print('Found Mono root directory: ' + mono_root) + mono_version = mono_root_try_find_mono_version(mono_root) configure_for_mono_version(env, mono_version) @@ -192,16 +192,14 @@ def configure(env): if sys.platform == 'darwin': env.Append(LINKFLAGS=['-Wl,-force_load,' + mono_lib_file]) - elif sys.platform == 'linux' or sys.platform == 'linux2': - env.Append(LINKFLAGS=['-Wl,-whole-archive', mono_lib_file, '-Wl,-no-whole-archive']) else: - raise RuntimeError('mono-static: Not supported on this platform') + env.Append(LINKFLAGS=['-Wl,-whole-archive', mono_lib_file, '-Wl,-no-whole-archive']) else: env.Append(LIBS=[mono_lib]) if sys.platform == 'darwin': env.Append(LIBS=['iconv', 'pthread']) - elif sys.platform == 'linux' or sys.platform == 'linux2': + else: env.Append(LIBS=['m', 'rt', 'dl', 'pthread']) if not mono_static: @@ -211,11 +209,12 @@ def configure(env): raise RuntimeError('Could not find mono shared library in: ' + mono_lib_path) copy_file(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext) - - copy_file(os.path.join(mono_lib_path, 'mono', '4.5'), assemblies_output_dir, 'mscorlib.dll') else: assert not mono_static + # TODO: Add option to force using pkg-config + print('Mono root directory not found. Using pkg-config instead') + mono_version = pkgconfig_try_find_mono_version() configure_for_mono_version(env, mono_version) @@ -223,7 +222,6 @@ def configure(env): mono_lib_path = '' mono_so_name = '' - mono_prefix = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip() tmpenv = Environment() tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH')) @@ -240,16 +238,163 @@ def configure(env): raise RuntimeError('Could not find mono shared library in: ' + str(tmpenv['LIBPATH'])) copy_file(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext) - copy_file(os.path.join(mono_prefix, 'lib', 'mono', '4.5'), assemblies_output_dir, 'mscorlib.dll') env.Append(LINKFLAGS='-rdynamic') + if not tools_enabled: + if not mono_root: + mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip() + + make_template_dir(env, mono_root) + + if copy_mono_root: + if not mono_root: + mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip() + + if tools_enabled: + copy_mono_root_files(env, mono_root) + else: + print("Ignoring option: 'copy_mono_root'. Only available for builds with 'tools' enabled.") + + +def make_template_dir(env, mono_root): + from shutil import rmtree + + platform = env['platform'] + target = env['target'] + + template_dir_name = '' + + if platform == 'windows': + template_dir_name = 'data.mono.%s.%s.%s' % (platform, env['bits'], target) + elif platform == 'osx': + template_dir_name = 'data.mono.%s.%s' % (platform, target) + elif platform == 'x11': + template_dir_name = 'data.mono.%s.%s.%s' % (platform, env['bits'], target) + else: + assert False + + output_dir = Dir('#bin').abspath + template_dir = os.path.join(output_dir, template_dir_name) + + template_mono_root_dir = os.path.join(template_dir, 'Mono') + + if os.path.isdir(template_mono_root_dir): + rmtree(template_mono_root_dir) # Clean first + + # Copy etc/mono/ + + template_mono_config_dir = os.path.join(template_mono_root_dir, 'etc', 'mono') + copy_mono_etc_dir(mono_root, template_mono_config_dir, env['platform']) + + # Copy the required shared libraries + + copy_mono_shared_libs(mono_root, template_mono_root_dir, env['platform']) + + +def copy_mono_root_files(env, mono_root): + from glob import glob + from shutil import copy + from shutil import rmtree + + if not mono_root: + raise RuntimeError('Mono installation directory not found') + + output_dir = Dir('#bin').abspath + editor_mono_root_dir = os.path.join(output_dir, 'GodotSharp', 'Mono') + + if os.path.isdir(editor_mono_root_dir): + rmtree(editor_mono_root_dir) # Clean first + + # Copy etc/mono/ + + editor_mono_config_dir = os.path.join(editor_mono_root_dir, 'etc', 'mono') + copy_mono_etc_dir(mono_root, editor_mono_config_dir, env['platform']) + + # Copy the required shared libraries + + copy_mono_shared_libs(mono_root, editor_mono_root_dir, env['platform']) + + # Copy framework assemblies + + mono_framework_dir = os.path.join(mono_root, 'lib', 'mono', '4.5') + mono_framework_facades_dir = os.path.join(mono_framework_dir, 'Facades') + + editor_mono_framework_dir = os.path.join(editor_mono_root_dir, 'lib', 'mono', '4.5') + editor_mono_framework_facades_dir = os.path.join(editor_mono_framework_dir, 'Facades') + + if not os.path.isdir(editor_mono_framework_dir): + os.makedirs(editor_mono_framework_dir) + if not os.path.isdir(editor_mono_framework_facades_dir): + os.makedirs(editor_mono_framework_facades_dir) + + for assembly in glob(os.path.join(mono_framework_dir, '*.dll')): + copy(assembly, editor_mono_framework_dir) + for assembly in glob(os.path.join(mono_framework_facades_dir, '*.dll')): + copy(assembly, editor_mono_framework_facades_dir) + + +def copy_mono_etc_dir(mono_root, target_mono_config_dir, platform): + from distutils.dir_util import copy_tree + from glob import glob + from shutil import copy + + if not os.path.isdir(target_mono_config_dir): + os.makedirs(target_mono_config_dir) + + mono_etc_dir = os.path.join(mono_root, 'etc', 'mono') + if not os.path.isdir(mono_etc_dir): + mono_etc_dir = '' + etc_hint_dirs = [] + if platform != 'windows': + etc_hint_dirs += ['/etc/mono', '/usr/local/etc/mono'] + if 'MONO_CFG_DIR' in os.environ: + etc_hint_dirs += [os.path.join(os.environ['MONO_CFG_DIR'], 'mono')] + for etc_hint_dir in etc_hint_dirs: + if os.path.isdir(etc_hint_dir): + mono_etc_dir = etc_hint_dir + break + if not mono_etc_dir: + raise RuntimeError('Mono installation etc directory not found') + + copy_tree(os.path.join(mono_etc_dir, '2.0'), os.path.join(target_mono_config_dir, '2.0')) + copy_tree(os.path.join(mono_etc_dir, '4.0'), os.path.join(target_mono_config_dir, '4.0')) + copy_tree(os.path.join(mono_etc_dir, '4.5'), os.path.join(target_mono_config_dir, '4.5')) + copy_tree(os.path.join(mono_etc_dir, 'mconfig'), os.path.join(target_mono_config_dir, 'mconfig')) + + for file in glob(os.path.join(mono_etc_dir, '*')): + if os.path.isfile(file): + copy(file, target_mono_config_dir) + + +def copy_mono_shared_libs(mono_root, target_mono_root_dir, platform): + from shutil import copy + + if platform == 'windows': + target_mono_bin_dir = os.path.join(target_mono_root_dir, 'bin') + + if not os.path.isdir(target_mono_bin_dir): + os.makedirs(target_mono_bin_dir) + + copy(os.path.join(mono_root, 'bin', 'MonoPosixHelper.dll'), os.path.join(target_mono_bin_dir, 'MonoPosixHelper.dll')) + else: + target_mono_lib_dir = os.path.join(target_mono_root_dir, 'lib') + + if not os.path.isdir(target_mono_lib_dir): + os.makedirs(target_mono_lib_dir) + + if platform == 'osx': + copy(os.path.join(mono_root, 'lib', 'libMonoPosixHelper.dylib'), os.path.join(target_mono_lib_dir, 'libMonoPosixHelper.dylib')) + elif platform == 'x11': + copy(os.path.join(mono_root, 'lib', 'libmono-btls-shared.so'), os.path.join(target_mono_lib_dir, 'libmono-btls-shared.so')) + copy(os.path.join(mono_root, 'lib', 'libMonoPosixHelper.so'), os.path.join(target_mono_lib_dir, 'libMonoPosixHelper.so')) + def configure_for_mono_version(env, mono_version): if mono_version is None: raise RuntimeError('Mono JIT compiler version not found') - print('Mono JIT compiler version: ' + str(mono_version)) - if mono_version >= LooseVersion("5.12.0"): + print('Found Mono JIT compiler version: ' + str(mono_version)) + if mono_version >= LooseVersion('5.12.0'): env.Append(CPPFLAGS=['-DHAS_PENDING_EXCEPTIONS']) @@ -282,7 +427,14 @@ def pkgconfig_try_find_mono_version(): def mono_root_try_find_mono_version(mono_root): from compat import decode_utf8 - output = subprocess.check_output([os.path.join(mono_root, 'bin', 'mono'), '--version']) + mono_bin = os.path.join(mono_root, 'bin') + if os.path.isfile(os.path.join(mono_bin, 'mono')): + mono_binary = os.path.join(mono_bin, 'mono') + elif os.path.isfile(os.path.join(mono_bin, 'mono.exe')): + mono_binary = os.path.join(mono_bin, 'mono.exe') + else: + return None + output = subprocess.check_output([mono_binary, '--version']) first_line = decode_utf8(output.splitlines()[0]) try: return LooseVersion(first_line.split()[len('Mono JIT compiler version'.split())]) diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index b8b77924f7..a048baf5d7 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -32,17 +32,17 @@ #include <mono/metadata/threads.h> -#include "os/file_access.h" -#include "os/os.h" -#include "os/thread.h" -#include "project_settings.h" +#include "core/io/json.h" +#include "core/os/file_access.h" +#include "core/os/os.h" +#include "core/os/thread.h" +#include "core/project_settings.h" #ifdef TOOLS_ENABLED #include "editor/bindings_generator.h" #include "editor/csharp_project.h" #include "editor/editor_node.h" #include "editor/godotsharp_editor.h" -#include "utils/string_utils.h" #endif #include "godotsharp_dirs.h" @@ -50,6 +50,7 @@ #include "mono_gd/gd_mono_marshal.h" #include "signal_awaiter_utils.h" #include "utils/macros.h" +#include "utils/string_utils.h" #include "utils/thread_local.h" #define CACHED_STRING_NAME(m_var) (CSharpLanguage::get_singleton()->get_string_names().m_var) @@ -107,7 +108,7 @@ void CSharpLanguage::init() { gdmono = memnew(GDMono); gdmono->initialize(); -#ifdef MONO_GLUE_DISABLED +#ifndef MONO_GLUE_ENABLED WARN_PRINT("This binary is built with `mono_glue=no` and cannot be used for scripting"); #endif @@ -138,7 +139,7 @@ void CSharpLanguage::finish() { #endif // Release gchandle bindings before finalizing mono runtime - gchandle_bindings.clear(); + script_bindings.clear(); if (gdmono) { memdelete(gdmono); @@ -370,6 +371,7 @@ bool CSharpLanguage::supports_builtin_mode() const { return false; } +#ifdef TOOLS_ENABLED static String variant_type_to_managed_name(const String &p_var_type_name) { if (p_var_type_name.empty()) @@ -432,8 +434,7 @@ static String variant_type_to_managed_name(const String &p_var_type_name) { return p_var_type_name; } -String CSharpLanguage::make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const { -#ifdef TOOLS_ENABLED +String CSharpLanguage::make_function(const String &, const String &p_name, const PoolStringArray &p_args) const { // FIXME // - Due to Godot's API limitation this just appends the function to the end of the file // - Use fully qualified name if there is ambiguity @@ -449,10 +450,12 @@ String CSharpLanguage::make_function(const String &p_class, const String &p_name s += ")\n{\n // Replace with function body.\n}\n"; return s; +} #else +String CSharpLanguage::make_function(const String &, const String &, const PoolStringArray &) const { return String(); -#endif } +#endif String CSharpLanguage::_get_indentation() const { #ifdef TOOLS_ENABLED @@ -551,22 +554,22 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec void CSharpLanguage::frame() { - const Ref<MonoGCHandle> &task_scheduler_handle = GDMonoUtils::mono_cache.task_scheduler_handle; + if (gdmono && gdmono->is_runtime_initialized() && gdmono->get_core_api_assembly() != NULL) { + const Ref<MonoGCHandle> &task_scheduler_handle = GDMonoUtils::mono_cache.task_scheduler_handle; - if (task_scheduler_handle.is_valid()) { - MonoObject *task_scheduler = task_scheduler_handle->get_target(); + if (task_scheduler_handle.is_valid()) { + MonoObject *task_scheduler = task_scheduler_handle->get_target(); - if (task_scheduler) { - GDMonoUtils::GodotTaskScheduler_Activate thunk = CACHED_METHOD_THUNK(GodotTaskScheduler, Activate); + if (task_scheduler) { + GDMonoUtils::GodotTaskScheduler_Activate thunk = CACHED_METHOD_THUNK(GodotTaskScheduler, Activate); - ERR_FAIL_NULL(thunk); - - MonoException *exc = NULL; - thunk(task_scheduler, (MonoObject **)&exc); + MonoException *exc = NULL; + thunk(task_scheduler, (MonoObject **)&exc); - if (exc) { - GDMonoUtils::debug_unhandled_exception(exc); - _UNREACHABLE_(); + if (exc) { + GDMonoUtils::debug_unhandled_exception(exc); + _UNREACHABLE_(); + } } } } @@ -822,6 +825,49 @@ void CSharpLanguage::reload_assemblies_if_needed(bool p_soft_reload) { } #endif +void CSharpLanguage::project_assembly_loaded() { + + scripts_metadata.clear(); + + String scripts_metadata_filename = "scripts_metadata."; + +#ifdef TOOLS_ENABLED + scripts_metadata_filename += Engine::get_singleton()->is_editor_hint() ? "editor" : "editor_player"; +#else +#ifdef DEBUG_ENABLED + scripts_metadata_filename += "debug"; +#else + scripts_metadata_filename += "release"; +#endif +#endif + + String scripts_metadata_path = GodotSharpDirs::get_res_metadata_dir().plus_file(scripts_metadata_filename); + + if (FileAccess::exists(scripts_metadata_path)) { + String old_json; + + Error ferr = read_all_file_utf8(scripts_metadata_path, old_json); + ERR_FAIL_COND(ferr != OK); + + Variant old_dict_var; + String err_str; + int err_line; + Error json_err = JSON::parse(old_json, old_dict_var, err_str, err_line); + if (json_err != OK) { + ERR_PRINTS("Failed to parse metadata file: '" + err_str + "' (" + String::num_int64(err_line) + ")"); + return; + } + + scripts_metadata = old_dict_var.operator Dictionary(); + + print_verbose("Successfully loaded scripts metadata"); + } else { + if (!Engine::get_singleton()->is_editor_hint()) { + ERR_PRINT("Missing scripts metadata file"); + } + } +} + void CSharpLanguage::get_recognized_extensions(List<String> *p_extensions) const { p_extensions->push_back("cs"); @@ -892,6 +938,48 @@ void CSharpLanguage::set_language_index(int p_idx) { lang_idx = p_idx; } +void CSharpLanguage::release_script_gchandle(Ref<MonoGCHandle> &p_gchandle) { + + if (!p_gchandle->is_released()) { // Do not locking unnecessarily +#ifndef NO_THREADS + get_singleton()->script_gchandle_release_lock->lock(); +#endif + + p_gchandle->release(); + +#ifndef NO_THREADS + get_singleton()->script_gchandle_release_lock->unlock(); +#endif + } +} + +void CSharpLanguage::release_script_gchandle(MonoObject *p_pinned_expected_obj, Ref<MonoGCHandle> &p_gchandle) { + + uint32_t pinned_gchandle = MonoGCHandle::new_strong_handle_pinned(p_pinned_expected_obj); // we might lock after this, so pin it + + if (!p_gchandle->is_released()) { // Do not locking unnecessarily +#ifndef NO_THREADS + get_singleton()->script_gchandle_release_lock->lock(); +#endif + + MonoObject *target = p_gchandle->get_target(); + + // We release the gchandle if it points to the MonoObject* we expect (otherwise it was + // already released and could have been replaced) or if we can't get its target MonoObject* + // (which doesn't necessarily mean it was released, and we want it released in order to + // avoid locking other threads unnecessarily). + if (target == p_pinned_expected_obj || target == NULL) { + p_gchandle->release(); + } + +#ifndef NO_THREADS + get_singleton()->script_gchandle_release_lock->unlock(); +#endif + } + + MonoGCHandle::free_handle(pinned_gchandle); +} + CSharpLanguage::CSharpLanguage() { ERR_FAIL_COND(singleton); @@ -904,9 +992,11 @@ CSharpLanguage::CSharpLanguage() { #ifdef NO_THREADS lock = NULL; gchandle_bind_lock = NULL; + script_gchandle_release_lock = NULL; #else lock = Mutex::create(); script_bind_lock = Mutex::create(); + script_gchandle_release_lock = Mutex::create(); #endif lang_idx = -1; @@ -926,6 +1016,11 @@ CSharpLanguage::~CSharpLanguage() { script_bind_lock = NULL; } + if (script_gchandle_release_lock) { + memdelete(script_gchandle_release_lock); + script_gchandle_release_lock = NULL; + } + singleton = NULL; } @@ -954,30 +1049,34 @@ void *CSharpLanguage::alloc_instance_binding_data(Object *p_object) { ERR_FAIL_NULL_V(mono_object, NULL); - // Tie managed to unmanaged - Reference *ref = Object::cast_to<Reference>(p_object); + CSharpScriptBinding script_binding; - if (ref) { - // Unsafe refcount increment. The managed instance also counts as a reference. - // This way if the unmanaged world has no references to our owner - // but the managed instance is alive, the refcount will be 1 instead of 0. - // See: _GodotSharp::_dispose_object(Object *p_object) - - ref->reference(); - } - - Ref<MonoGCHandle> gchandle = MonoGCHandle::create_strong(mono_object); + script_binding.type_name = type_name; + script_binding.wrapper_class = type_class; // cache + script_binding.gchandle = MonoGCHandle::create_strong(mono_object); #ifndef NO_THREADS script_bind_lock->lock(); #endif - void *data = (void *)gchandle_bindings.insert(p_object, gchandle); + void *data = (void *)script_bindings.insert(p_object, script_binding); #ifndef NO_THREADS script_bind_lock->unlock(); #endif + // Tie managed to unmanaged + Reference *ref = Object::cast_to<Reference>(p_object); + + if (ref) { + // Unsafe refcount increment. The managed instance also counts as a reference. + // This way if the unmanaged world has no references to our owner + // but the managed instance is alive, the refcount will be 1 instead of 0. + // See: godot_icall_Reference_Dtor(MonoObject *p_obj, Object *p_ptr) + + ref->reference(); + } + return data; } @@ -985,7 +1084,7 @@ void CSharpLanguage::free_instance_binding_data(void *p_data) { if (GDMono::get_singleton() == NULL) { #ifdef DEBUG_ENABLED - CRASH_COND(!gchandle_bindings.empty()); + CRASH_COND(!script_bindings.empty()); #endif // Mono runtime finalized, all the gchandle bindings were already released return; @@ -998,21 +1097,84 @@ void CSharpLanguage::free_instance_binding_data(void *p_data) { script_bind_lock->lock(); #endif - Map<Object *, Ref<MonoGCHandle> >::Element *data = (Map<Object *, Ref<MonoGCHandle> >::Element *)p_data; + Map<Object *, CSharpScriptBinding>::Element *data = (Map<Object *, CSharpScriptBinding>::Element *)p_data; // Set the native instance field to IntPtr.Zero, if not yet garbage collected - MonoObject *mono_object = data->value()->get_target(); + MonoObject *mono_object = data->value().gchandle->get_target(); if (mono_object) { CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, NULL); } - gchandle_bindings.erase(data); + script_bindings.erase(data); #ifndef NO_THREADS script_bind_lock->unlock(); #endif } +void CSharpLanguage::refcount_incremented_instance_binding(Object *p_object) { + + Reference *ref_owner = Object::cast_to<Reference>(p_object); + +#ifdef DEBUG_ENABLED + CRASH_COND(!ref_owner); +#endif + + void *data = p_object->get_script_instance_binding(get_language_index()); + if (!data) + return; + Ref<MonoGCHandle> &gchandle = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get().gchandle; + + if (ref_owner->reference_get_count() > 1 && gchandle->is_weak()) { // The managed side also holds a reference, hence 1 instead of 0 + // The reference count was increased after the managed side was the only one referencing our owner. + // This means the owner is being referenced again by the unmanaged side, + // so the owner must hold the managed side alive again to avoid it from being GCed. + + MonoObject *target = gchandle->get_target(); + if (!target) + return; // Called after the managed side was collected, so nothing to do here + + // Release the current weak handle and replace it with a strong handle. + uint32_t strong_gchandle = MonoGCHandle::new_strong_handle(target); + gchandle->release(); + gchandle->set_handle(strong_gchandle, MonoGCHandle::STRONG_HANDLE); + } +} + +bool CSharpLanguage::refcount_decremented_instance_binding(Object *p_object) { + + Reference *ref_owner = Object::cast_to<Reference>(p_object); + +#ifdef DEBUG_ENABLED + CRASH_COND(!ref_owner); +#endif + + int refcount = ref_owner->reference_get_count(); + + void *data = p_object->get_script_instance_binding(get_language_index()); + if (!data) + return refcount == 0; + Ref<MonoGCHandle> &gchandle = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get().gchandle; + + if (refcount == 1 && !gchandle->is_weak()) { // The managed side also holds a reference, hence 1 instead of 0 + // If owner owner is no longer referenced by the unmanaged side, + // the managed instance takes responsibility of deleting the owner when GCed. + + MonoObject *target = gchandle->get_target(); + if (!target) + return refcount == 0; // Called after the managed side was collected, so nothing to do here + + // Release the current strong handle and replace it with a weak handle. + uint32_t weak_gchandle = MonoGCHandle::new_weak_handle(target); + gchandle->release(); + gchandle->set_handle(weak_gchandle, MonoGCHandle::WEAK_HANDLE); + + return false; + } + + return refcount == 0; +} + CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpScript *p_script, const Ref<MonoGCHandle> &p_gchandle) { CSharpInstance *instance = memnew(CSharpInstance); @@ -1033,9 +1195,8 @@ CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpS } MonoObject *CSharpInstance::get_mono_object() const { -#ifdef DEBUG_ENABLED - CRASH_COND(gchandle.is_null()); -#endif + + ERR_FAIL_COND_V(gchandle.is_null(), NULL); return gchandle->get_target(); } @@ -1059,7 +1220,7 @@ bool CSharpInstance::set(const StringName &p_name, const Variant &p_value) { GDMonoProperty *property = script->script_class->get_property(p_name); if (property) { - property->set_value(mono_object, GDMonoMarshal::variant_to_mono_object(p_value)); + property->set_value(mono_object, GDMonoMarshal::variant_to_mono_object(p_value, property->get_type())); return true; } @@ -1079,7 +1240,7 @@ bool CSharpInstance::set(const StringName &p_name, const Variant &p_value) { MonoObject *ret = method->invoke(mono_object, args); - if (ret && GDMonoMarshal::unbox<MonoBoolean>(ret) == true) + if (ret && GDMonoMarshal::unbox<MonoBoolean>(ret)) return true; break; @@ -1263,10 +1424,12 @@ void CSharpInstance::call_multilevel_reversed(const StringName &p_method, const call_multilevel(p_method, p_args, p_argcount); } -void CSharpInstance::_reference_owner_unsafe() { +bool CSharpInstance::_reference_owner_unsafe() { #ifdef DEBUG_ENABLED CRASH_COND(!base_ref); + CRASH_COND(owner == NULL); + CRASH_COND(unsafe_referenced); // already referenced #endif // Unsafe refcount increment. The managed instance also counts as a reference. @@ -1275,66 +1438,144 @@ void CSharpInstance::_reference_owner_unsafe() { // See: _unreference_owner_unsafe() // May not me referenced yet, so we must use init_ref() instead of reference() - Object::cast_to<Reference>(owner)->init_ref(); + bool success = Object::cast_to<Reference>(owner)->init_ref(); + unsafe_referenced = success; + return success; } -void CSharpInstance::_unreference_owner_unsafe() { +bool CSharpInstance::_unreference_owner_unsafe() { #ifdef DEBUG_ENABLED CRASH_COND(!base_ref); + CRASH_COND(owner == NULL); #endif + if (!unsafe_referenced) + return false; // Already unreferenced + + unsafe_referenced = false; + // Called from CSharpInstance::mono_object_disposed() or ~CSharpInstance() // Unsafe refcount decrement. The managed instance also counts as a reference. // See: _reference_owner_unsafe() - if (Object::cast_to<Reference>(owner)->unreference()) { + bool die = static_cast<Reference *>(owner)->unreference(); + + if (die) { memdelete(owner); owner = NULL; } + + return die; } -void CSharpInstance::mono_object_disposed() { +MonoObject *CSharpInstance::_internal_new_managed() { +#ifdef DEBUG_ENABLED + CRASH_COND(!gchandle.is_valid()); +#endif + + CSharpLanguage::get_singleton()->release_script_gchandle(gchandle); + + ERR_FAIL_NULL_V(owner, NULL); + ERR_FAIL_COND_V(script.is_null(), NULL); if (base_ref) - _unreference_owner_unsafe(); + _reference_owner_unsafe(); + + MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, script->script_class->get_mono_ptr()); + + if (!mono_object) { + script = Ref<CSharpScript>(); + owner->set_script_instance(NULL); + ERR_EXPLAIN("Failed to allocate memory for the object"); + ERR_FAIL_V(NULL); + } + + CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, owner); + + // Construct + GDMonoMethod *ctor = script->script_class->get_method(CACHED_STRING_NAME(dotctor), 0); + ctor->invoke_raw(mono_object, NULL); + + // Tie managed to unmanaged + gchandle = MonoGCHandle::create_strong(mono_object); + + return mono_object; +} + +void CSharpInstance::mono_object_disposed(MonoObject *p_obj) { + +#ifdef DEBUG_ENABLED + CRASH_COND(base_ref); + CRASH_COND(gchandle.is_null()); +#endif + CSharpLanguage::get_singleton()->release_script_gchandle(p_obj, gchandle); +} + +void CSharpInstance::mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_owner_deleted) { + +#ifdef DEBUG_ENABLED + CRASH_COND(!base_ref); + CRASH_COND(gchandle.is_null()); +#endif + if (_unreference_owner_unsafe()) { + r_owner_deleted = true; + } else { + r_owner_deleted = false; + CSharpLanguage::get_singleton()->release_script_gchandle(p_obj, gchandle); + if (p_is_finalizer) { + // If the native instance is still alive, then it was + // referenced from another thread before the finalizer could + // unreference it and delete it, so we want to keep it. + // GC.ReRegisterForFinalize(this) is not safe because the objects + // referenced by this could have already been collected. + // Instead we will create a new managed instance here. + _internal_new_managed(); + } + } } void CSharpInstance::refcount_incremented() { +#ifdef DEBUG_ENABLED CRASH_COND(!base_ref); + CRASH_COND(owner == NULL); +#endif Reference *ref_owner = Object::cast_to<Reference>(owner); - if (ref_owner->reference_get_count() > 1) { // The managed side also holds a reference, hence 1 instead of 0 + if (ref_owner->reference_get_count() > 1 && gchandle->is_weak()) { // The managed side also holds a reference, hence 1 instead of 0 // The reference count was increased after the managed side was the only one referencing our owner. // This means the owner is being referenced again by the unmanaged side, // so the owner must hold the managed side alive again to avoid it from being GCed. // Release the current weak handle and replace it with a strong handle. - uint32_t strong_gchandle = MonoGCHandle::make_strong_handle(gchandle->get_target()); + uint32_t strong_gchandle = MonoGCHandle::new_strong_handle(gchandle->get_target()); gchandle->release(); - gchandle->set_handle(strong_gchandle); + gchandle->set_handle(strong_gchandle, MonoGCHandle::STRONG_HANDLE); } } bool CSharpInstance::refcount_decremented() { +#ifdef DEBUG_ENABLED CRASH_COND(!base_ref); + CRASH_COND(owner == NULL); +#endif Reference *ref_owner = Object::cast_to<Reference>(owner); int refcount = ref_owner->reference_get_count(); - if (refcount == 1) { // The managed side also holds a reference, hence 1 instead of 0 + if (refcount == 1 && !gchandle->is_weak()) { // The managed side also holds a reference, hence 1 instead of 0 // If owner owner is no longer referenced by the unmanaged side, // the managed instance takes responsibility of deleting the owner when GCed. // Release the current strong handle and replace it with a weak handle. - uint32_t weak_gchandle = MonoGCHandle::make_weak_handle(gchandle->get_target()); + uint32_t weak_gchandle = MonoGCHandle::new_weak_handle(gchandle->get_target()); gchandle->release(); - gchandle->set_handle(weak_gchandle); + gchandle->set_handle(weak_gchandle, MonoGCHandle::WEAK_HANDLE); return false; } @@ -1348,18 +1589,20 @@ MultiplayerAPI::RPCMode CSharpInstance::_member_get_rpc_mode(GDMonoClassMember * if (p_member->has_attribute(CACHED_CLASS(RemoteAttribute))) return MultiplayerAPI::RPC_MODE_REMOTE; - if (p_member->has_attribute(CACHED_CLASS(SyncAttribute))) - return MultiplayerAPI::RPC_MODE_SYNC; if (p_member->has_attribute(CACHED_CLASS(MasterAttribute))) return MultiplayerAPI::RPC_MODE_MASTER; + if (p_member->has_attribute(CACHED_CLASS(PuppetAttribute))) + return MultiplayerAPI::RPC_MODE_PUPPET; if (p_member->has_attribute(CACHED_CLASS(SlaveAttribute))) - return MultiplayerAPI::RPC_MODE_SLAVE; + return MultiplayerAPI::RPC_MODE_PUPPET; if (p_member->has_attribute(CACHED_CLASS(RemoteSyncAttribute))) return MultiplayerAPI::RPC_MODE_REMOTESYNC; + if (p_member->has_attribute(CACHED_CLASS(SyncAttribute))) + return MultiplayerAPI::RPC_MODE_REMOTESYNC; if (p_member->has_attribute(CACHED_CLASS(MasterSyncAttribute))) return MultiplayerAPI::RPC_MODE_MASTERSYNC; - if (p_member->has_attribute(CACHED_CLASS(SlaveSyncAttribute))) - return MultiplayerAPI::RPC_MODE_SLAVESYNC; + if (p_member->has_attribute(CACHED_CLASS(PuppetSyncAttribute))) + return MultiplayerAPI::RPC_MODE_PUPPETSYNC; return MultiplayerAPI::RPC_MODE_DISABLED; } @@ -1403,25 +1646,64 @@ MultiplayerAPI::RPCMode CSharpInstance::get_rset_mode(const StringName &p_variab void CSharpInstance::notification(int p_notification) { - MonoObject *mono_object = get_mono_object(); - if (p_notification == Object::NOTIFICATION_PREDELETE) { - if (mono_object != NULL) { // otherwise it was collected, and the finalizer already called NOTIFICATION_PREDELETE - call_notification_no_check(mono_object, p_notification); - // Set the native instance field to IntPtr.Zero - CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, NULL); + // When NOTIFICATION_PREDELETE is sent, we also take the chance to call Dispose(). + // It's safe to call Dispose() multiple times and NOTIFICATION_PREDELETE is guaranteed + // to be sent at least once, which happens right before the call to the destructor. + + if (base_ref) { + // It's not safe to proceed if the owner derives Reference and the refcount reached 0. + // At this point, Dispose() was already called (manually or from the finalizer) so + // that's not a problem. The refcount wouldn't have reached 0 otherwise, since the + // managed side references it and Dispose() needs to be called to release it. + // However, this means C# Reference scripts can't receive NOTIFICATION_PREDELETE, but + // this is likely the case with GDScript as well: https://github.com/godotengine/godot/issues/6784 + return; + } + + _call_notification(p_notification); + + MonoObject *mono_object = get_mono_object(); + ERR_FAIL_NULL(mono_object); + + GDMonoUtils::GodotObject_Dispose thunk = CACHED_METHOD_THUNK(GodotObject, Dispose); + + MonoException *exc = NULL; + thunk(mono_object, (MonoObject **)&exc); + + if (exc) { + GDMonoUtils::set_pending_exception(exc); } + return; } - call_notification_no_check(mono_object, p_notification); + _call_notification(p_notification); } -void CSharpInstance::call_notification_no_check(MonoObject *p_mono_object, int p_notification) { - Variant value = p_notification; - const Variant *args[1] = { &value }; +void CSharpInstance::_call_notification(int p_notification) { + + MonoObject *mono_object = get_mono_object(); + ERR_FAIL_NULL(mono_object); + + // Custom version of _call_multilevel, optimized for _notification + + uint32_t arg = p_notification; + void *args[1] = { &arg }; + StringName method_name = CACHED_STRING_NAME(_notification); + + GDMonoClass *top = script->script_class; + + while (top && top != script->native) { + GDMonoMethod *method = top->get_method(method_name, 1); - _call_multilevel(p_mono_object, CACHED_STRING_NAME(_notification), args, 1); + if (method) { + method->invoke_raw(mono_object, args); + return; + } + + top = top->get_parent_class(); + } } Ref<Script> CSharpInstance::get_script() const { @@ -1434,11 +1716,11 @@ ScriptLanguage *CSharpInstance::get_language() { return CSharpLanguage::get_singleton(); } -CSharpInstance::CSharpInstance() { - - owner = NULL; - base_ref = false; - ref_dying = false; +CSharpInstance::CSharpInstance() : + owner(NULL), + base_ref(false), + ref_dying(false), + unsafe_referenced(false) { } CSharpInstance::~CSharpInstance() { @@ -1447,10 +1729,7 @@ CSharpInstance::~CSharpInstance() { gchandle->release(); // Make sure it's released } - if (base_ref && !ref_dying) { // it may be called from the owner's destructor -#ifdef DEBUG_ENABLED - CRASH_COND(!owner); // dunno, just in case -#endif + if (base_ref && !ref_dying && owner) { // it may be called from the owner's destructor _unreference_owner_unsafe(); } @@ -1519,26 +1798,27 @@ bool CSharpScript::_update_exports() { exported_members_cache.clear(); exported_members_defval_cache.clear(); - // We are creating a temporary new instance of the class here to get the default value - // TODO Workaround. Should be replaced with IL opcodes analysis + // Here we create a temporary managed instance of the class to get the initial values MonoObject *tmp_object = mono_object_new(SCRIPTS_DOMAIN, script_class->get_mono_ptr()); - if (tmp_object) { - CACHED_FIELD(GodotObject, ptr)->set_value_raw(tmp_object, tmp_object); // FIXME WTF is this workaround + if (!tmp_object) { + ERR_PRINT("Failed to create temporary MonoObject"); + return false; + } - GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), 0); - MonoException *exc = NULL; - ctor->invoke(tmp_object, NULL, &exc); + uint32_t tmp_pinned_gchandle = MonoGCHandle::new_strong_handle_pinned(tmp_object); // pin it (not sure if needed) - if (exc) { - ERR_PRINT("Exception thrown from constructor of temporary MonoObject:"); - GDMonoUtils::debug_print_unhandled_exception(exc); - tmp_object = NULL; - ERR_FAIL_V(false); - } - } else { - ERR_PRINT("Failed to create temporary MonoObject"); + GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), 0); + MonoException *ctor_exc = NULL; + ctor->invoke(tmp_object, NULL, &ctor_exc); + + if (ctor_exc) { + MonoGCHandle::free_handle(tmp_pinned_gchandle); + tmp_object = NULL; + + ERR_PRINT("Exception thrown from constructor of temporary MonoObject:"); + GDMonoUtils::debug_print_unhandled_exception(ctor_exc); return false; } @@ -1599,6 +1879,21 @@ bool CSharpScript::_update_exports() { top = top->get_parent_class(); } + + // Dispose the temporary managed instance + + GDMonoUtils::GodotObject_Dispose thunk = CACHED_METHOD_THUNK(GodotObject, Dispose); + + MonoException *exc = NULL; + thunk(tmp_object, (MonoObject **)&exc); + + if (exc) { + ERR_PRINT("Exception thrown from method Dispose() of temporary MonoObject:"); + GDMonoUtils::debug_print_unhandled_exception(exc); + } + + MonoGCHandle::free_handle(tmp_pinned_gchandle); + tmp_object = NULL; } if (placeholders.size()) { @@ -1683,6 +1978,10 @@ bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Ve } #ifdef TOOLS_ENABLED +/** + * Returns false if there was an error, otherwise true. + * If there was an error, r_prop_info and r_exported are not assigned any value. + */ bool CSharpScript::_get_member_export(GDMonoClass *p_class, GDMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported) { StringName name = p_member->get_name(); @@ -1708,49 +2007,100 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, GDMonoClassMember *p Variant::Type variant_type = GDMonoMarshal::managed_to_variant_type(type); - if (p_member->has_attribute(CACHED_CLASS(ExportAttribute))) { - if (p_member->get_member_type() == GDMonoClassMember::MEMBER_TYPE_PROPERTY) { - GDMonoProperty *property = static_cast<GDMonoProperty *>(p_member); - if (!property->has_getter() || !property->has_setter()) { - ERR_PRINTS("Cannot export property because it does not provide a getter or a setter: " + p_class->get_full_name() + "." + name.operator String()); - return false; - } + if (!p_member->has_attribute(CACHED_CLASS(ExportAttribute))) { + r_prop_info = PropertyInfo(variant_type, name.operator String(), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_SCRIPT_VARIABLE); + r_exported = false; + return true; + } + + if (p_member->get_member_type() == GDMonoClassMember::MEMBER_TYPE_PROPERTY) { + GDMonoProperty *property = static_cast<GDMonoProperty *>(p_member); + if (!property->has_getter() || !property->has_setter()) { + ERR_PRINTS("Cannot export property because it does not provide a getter or a setter: " + p_class->get_full_name() + "." + name.operator String()); + return false; } + } - MonoObject *attr = p_member->get_attribute(CACHED_CLASS(ExportAttribute)); + MonoObject *attr = p_member->get_attribute(CACHED_CLASS(ExportAttribute)); - PropertyHint hint = PROPERTY_HINT_NONE; - String hint_string; + PropertyHint hint = PROPERTY_HINT_NONE; + String hint_string; - if (variant_type == Variant::NIL) { - ERR_PRINTS("Unknown type of exported member: " + p_class->get_full_name() + "." + name.operator String()); - return false; - } else if (variant_type == Variant::INT && type.type_encoding == MONO_TYPE_VALUETYPE && mono_class_is_enum(type.type_class->get_mono_ptr())) { - variant_type = Variant::INT; - hint = PROPERTY_HINT_ENUM; + if (variant_type == Variant::NIL) { + ERR_PRINTS("Unknown type of exported member: " + p_class->get_full_name() + "." + name.operator String()); + return false; + } else if (variant_type == Variant::INT && type.type_encoding == MONO_TYPE_VALUETYPE && mono_class_is_enum(type.type_class->get_mono_ptr())) { + variant_type = Variant::INT; + hint = PROPERTY_HINT_ENUM; + + Vector<MonoClassField *> fields = type.type_class->get_enum_fields(); + + MonoType *enum_basetype = mono_class_enum_basetype(type.type_class->get_mono_ptr()); + + String name_only_hint_string; + + // True: enum Foo { Bar, Baz, Quux } + // True: enum Foo { Bar = 0, Baz = 1, Quux = 2 } + // False: enum Foo { Bar = 0, Baz = 7, Quux = 5 } + bool uses_default_values = true; - Vector<MonoClassField *> fields = type.type_class->get_enum_fields(); + for (int i = 0; i < fields.size(); i++) { + MonoClassField *field = fields[i]; - for (int i = 0; i < fields.size(); i++) { - if (i > 0) - hint_string += ","; - hint_string += mono_field_get_name(fields[i]); + if (i > 0) { + hint_string += ","; + name_only_hint_string += ","; } - } else if (variant_type == Variant::OBJECT && CACHED_CLASS(GodotReference)->is_assignable_from(type.type_class)) { - hint = PROPERTY_HINT_RESOURCE_TYPE; - hint_string = NATIVE_GDMONOCLASS_NAME(type.type_class); - } else { - hint = PropertyHint(CACHED_FIELD(ExportAttribute, hint)->get_int_value(attr)); - hint_string = CACHED_FIELD(ExportAttribute, hintString)->get_string_value(attr); + + String enum_field_name = mono_field_get_name(field); + hint_string += enum_field_name; + name_only_hint_string += enum_field_name; + + // TODO: + // Instead of using mono_field_get_value_object, we can do this without boxing. Check the + // internal mono functions: ves_icall_System_Enum_GetEnumValuesAndNames and the get_enum_field. + + MonoObject *val_obj = mono_field_get_value_object(mono_domain_get(), field, NULL); + + if (val_obj == NULL) { + ERR_PRINTS("Failed to get '" + enum_field_name + "' constant enum value of exported member: " + + p_class->get_full_name() + "." + name.operator String()); + return false; + } + + bool r_error; + uint64_t val = GDMonoUtils::unbox_enum_value(val_obj, enum_basetype, r_error); + if (r_error) { + ERR_PRINTS("Failed to unbox '" + enum_field_name + "' constant enum value of exported member: " + + p_class->get_full_name() + "." + name.operator String()); + return false; + } + + if (val != i) { + uses_default_values = false; + } + + hint_string += ":"; + hint_string += String::num_uint64(val); } - r_prop_info = PropertyInfo(variant_type, name.operator String(), hint, hint_string, PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE); - r_exported = true; + if (uses_default_values) { + // If we use the format NAME:VAL, that's what the editor displays. + // That's annoying if the user is not using custom values for the enum constants. + // This may not be needed in the future if the editor is changed to not display values. + hint_string = name_only_hint_string; + } + } else if (variant_type == Variant::OBJECT && CACHED_CLASS(GodotReference)->is_assignable_from(type.type_class)) { + hint = PROPERTY_HINT_RESOURCE_TYPE; + hint_string = NATIVE_GDMONOCLASS_NAME(type.type_class); } else { - r_prop_info = PropertyInfo(variant_type, name.operator String(), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_SCRIPT_VARIABLE); - r_exported = false; + hint = PropertyHint(CACHED_FIELD(ExportAttribute, hint)->get_int_value(attr)); + hint_string = CACHED_FIELD(ExportAttribute, hintString)->get_string_value(attr); } + r_prop_info = PropertyInfo(variant_type, name.operator String(), hint, hint_string, PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE); + r_exported = true; + return true; } #endif @@ -1906,10 +2256,27 @@ bool CSharpScript::can_instance() const { #endif #ifdef TOOLS_ENABLED - return valid && (tool || ScriptServer::is_scripting_enabled()); + bool extra_cond = tool || ScriptServer::is_scripting_enabled(); #else - return valid; + bool extra_cond = true; #endif + + // FIXME Need to think this through better. + // For tool scripts, this will never fire if the class is not found. That's because we + // don't know if it's a tool script if we can't find the class to access the attributes. + if (extra_cond && !script_class) { + if (GDMono::get_singleton()->get_project_assembly() == NULL) { + // 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); + } + } + + return valid && extra_cond; } StringName CSharpScript::get_instance_base_type() const { @@ -1945,6 +2312,8 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg ERR_FAIL_V(NULL); } + uint32_t pinned_gchandle = MonoGCHandle::new_strong_handle_pinned(mono_object); // we might lock after this, so pin it + #ifndef NO_THREADS CSharpLanguage::singleton->lock->lock(); #endif @@ -1966,6 +2335,8 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg /* STEP 3, PARTY */ + MonoGCHandle::free_handle(pinned_gchandle); + //@TODO make thread safe return instance; } @@ -2011,20 +2382,6 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) { CRASH_COND(!valid); #endif - if (!script_class) { - if (GDMono::get_singleton()->get_project_assembly() == NULL) { - // 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); - } - } - - 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)) { @@ -2112,7 +2469,23 @@ Error CSharpScript::reload(bool p_keep_state) { GDMonoAssembly *project_assembly = GDMono::get_singleton()->get_project_assembly(); if (project_assembly) { - script_class = project_assembly->get_object_derived_class(name); + const Variant *script_metadata_var = CSharpLanguage::get_singleton()->get_scripts_metadata().getptr(get_path()); + if (script_metadata_var) { + Dictionary script_metadata = script_metadata_var->operator Dictionary()["class"]; + const Variant *namespace_ = script_metadata.getptr("namespace"); + const Variant *class_name = script_metadata.getptr("class_name"); + ERR_FAIL_NULL_V(namespace_, ERR_BUG); + ERR_FAIL_NULL_V(class_name, ERR_BUG); + GDMonoClass *klass = project_assembly->get_class(namespace_->operator String(), class_name->operator String()); + if (klass) { + bool obj_type = CACHED_CLASS(GodotObject)->is_assignable_from(klass); + ERR_FAIL_COND_V(!obj_type, ERR_BUG); + script_class = klass; + } + } else { + // Missing script metadata. Fallback to legacy method + script_class = project_assembly->get_object_derived_class(name); + } valid = script_class != NULL; @@ -2240,29 +2613,14 @@ int CSharpScript::get_member_line(const StringName &p_member) const { Error CSharpScript::load_source_code(const String &p_path) { - PoolVector<uint8_t> sourcef; - Error err; - FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); - ERR_FAIL_COND_V(err != OK, 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); + Error ferr = read_all_file_utf8(p_path, source); + if (ferr != OK) { + if (ferr == ERR_INVALID_DATA) { + 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(ferr); } - source = s; - #ifdef TOOLS_ENABLED source_changed_cache = true; #endif diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index 53644eafae..fc604df2a0 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -31,10 +31,10 @@ #ifndef CSHARP_SCRIPT_H #define CSHARP_SCRIPT_H -#include "io/resource_loader.h" -#include "io/resource_saver.h" -#include "script_language.h" -#include "self_list.h" +#include "core/io/resource_loader.h" +#include "core/io/resource_saver.h" +#include "core/script_language.h" +#include "core/self_list.h" #include "mono_gc_handle.h" #include "mono_gd/gd_mono.h" @@ -48,6 +48,8 @@ class CSharpLanguage; #ifdef NO_SAFE_CAST template <typename TScriptInstance, typename TScriptLanguage> TScriptInstance *cast_script_instance(ScriptInstance *p_inst) { + if (!p_inst) + return NULL; return p_inst->get_language() == TScriptLanguage::get_singleton() ? static_cast<TScriptInstance *>(p_inst) : NULL; } #else @@ -65,7 +67,7 @@ class CSharpScript : public Script { friend class CSharpInstance; friend class CSharpLanguage; - friend class CSharpScriptDepSort; + friend struct CSharpScriptDepSort; bool tool; bool valid; @@ -177,14 +179,19 @@ class CSharpInstance : public ScriptInstance { friend class CSharpScript; friend class CSharpLanguage; + Object *owner; - Ref<CSharpScript> script; - Ref<MonoGCHandle> gchandle; bool base_ref; bool ref_dying; + bool unsafe_referenced; + + Ref<CSharpScript> script; + Ref<MonoGCHandle> gchandle; + + bool _reference_owner_unsafe(); + bool _unreference_owner_unsafe(); - void _reference_owner_unsafe(); - void _unreference_owner_unsafe(); + MonoObject *_internal_new_managed(); // Do not use unless you know what you are doing friend void GDMonoInternals::tie_managed_to_unmanaged(MonoObject *, Object *); @@ -208,7 +215,8 @@ public: virtual void call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount); virtual void call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount); - void mono_object_disposed(); + void mono_object_disposed(MonoObject *p_obj); + void mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_owner_deleted); virtual void refcount_incremented(); virtual bool refcount_decremented(); @@ -217,7 +225,7 @@ public: virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const; virtual void notification(int p_notification); - void call_notification_no_check(MonoObject *p_mono_object, int p_notification); + void _call_notification(int p_notification); virtual Ref<Script> get_script() const; @@ -227,6 +235,12 @@ public: ~CSharpInstance(); }; +struct CSharpScriptBinding { + StringName type_name; + GDMonoClass *wrapper_class; + Ref<MonoGCHandle> gchandle; +}; + class CSharpLanguage : public ScriptLanguage { friend class CSharpScript; @@ -241,10 +255,11 @@ class CSharpLanguage : public ScriptLanguage { Mutex *lock; Mutex *script_bind_lock; + Mutex *script_gchandle_release_lock; Map<Ref<CSharpScript>, Map<ObjectID, List<Pair<StringName, Variant> > > > to_reload; - Map<Object *, Ref<MonoGCHandle> > gchandle_bindings; + Map<Object *, CSharpScriptBinding> script_bindings; struct StringNameCache { @@ -260,6 +275,8 @@ class CSharpLanguage : public ScriptLanguage { int lang_idx; + Dictionary scripts_metadata; + public: StringNameCache string_names; @@ -270,6 +287,9 @@ public: _FORCE_INLINE_ static CSharpLanguage *get_singleton() { return singleton; } + static void release_script_gchandle(Ref<MonoGCHandle> &p_gchandle); + static void release_script_gchandle(MonoObject *p_pinned_expected_obj, Ref<MonoGCHandle> &p_gchandle); + bool debug_break(const String &p_error, bool p_allow_continue = true); bool debug_break_parse(const String &p_file, int p_line, const String &p_error); @@ -277,6 +297,10 @@ public: void reload_assemblies_if_needed(bool p_soft_reload); #endif + void project_assembly_loaded(); + + _FORCE_INLINE_ const Dictionary &get_scripts_metadata() { return scripts_metadata; } + virtual String get_name() const; /* LANGUAGE FUNCTIONS */ @@ -346,6 +370,8 @@ public: // Don't use these. I'm watching you virtual void *alloc_instance_binding_data(Object *p_object); virtual void free_instance_binding_data(void *p_data); + virtual void refcount_incremented_instance_binding(Object *p_object); + virtual bool refcount_decremented_instance_binding(Object *p_object); #ifdef DEBUG_ENABLED Vector<StackInfo> stack_trace_get_info(MonoObject *p_stack_trace); diff --git a/modules/mono/doc_classes/GodotSharp.xml b/modules/mono/doc_classes/GodotSharp.xml index 985c66464b..921c1ca825 100644 --- a/modules/mono/doc_classes/GodotSharp.xml +++ b/modules/mono/doc_classes/GodotSharp.xml @@ -23,18 +23,44 @@ Detaches the current thread from the mono runtime. </description> </method> - <method name="is_domain_loaded"> + <method name="get_domain_id"> + <return type="int"> + </return> + <description> + </description> + </method> + <method name="get_scripts_domain_id"> + <return type="int"> + </return> + <description> + </description> + </method> + <method name="is_domain_finalizing_for_unload"> + <return type="bool"> + </return> + <argument index="0" name="domain_id" type="int"> + </argument> + <description> + Returns whether the domain is being finalized. + </description> + </method> + <method name="is_runtime_initialized"> + <return type="bool"> + </return> + <description> + </description> + </method> + <method name="is_runtime_shutting_down"> <return type="bool"> </return> <description> - Returns whether the scripts domain is loaded. </description> </method> - <method name="is_finalizing_domain"> + <method name="is_scripts_domain_loaded"> <return type="bool"> </return> <description> - Returns whether the scripts domain is being finalized. + Returns whether the scripts domain is loaded. </description> </method> </methods> diff --git a/modules/mono/editor/GodotSharpTools/Editor/GodotSharpExport.cs b/modules/mono/editor/GodotSharpTools/Editor/GodotSharpExport.cs new file mode 100644 index 0000000000..5fd708d539 --- /dev/null +++ b/modules/mono/editor/GodotSharpTools/Editor/GodotSharpExport.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; + +namespace GodotSharpTools.Editor +{ + public static class GodotSharpExport + { + public static void _ExportBegin(string[] features, bool debug, string path, int flags) + { + var featureSet = new HashSet<string>(features); + + if (PlatformHasTemplateDir(featureSet)) + { + string templateDirName = "data.mono"; + + if (featureSet.Contains("Windows")) + { + templateDirName += ".windows"; + templateDirName += featureSet.Contains("64") ? ".64" : ".32"; + } + else if (featureSet.Contains("X11")) + { + templateDirName += ".x11"; + templateDirName += featureSet.Contains("64") ? ".64" : ".32"; + } + else + { + throw new NotSupportedException("Target platform not supported"); + } + + templateDirName += debug ? ".debug" : ".release"; + + string templateDirPath = Path.Combine(GetTemplatesDir(), templateDirName); + + if (!Directory.Exists(templateDirPath)) + throw new FileNotFoundException("Data template directory not found"); + + string outputDir = new FileInfo(path).Directory.FullName; + + string outputDataDir = Path.Combine(outputDir, GetDataDirName()); + + if (Directory.Exists(outputDataDir)) + Directory.Delete(outputDataDir, recursive: true); // Clean first + + Directory.CreateDirectory(outputDataDir); + + foreach (string dir in Directory.GetDirectories(templateDirPath, "*", SearchOption.AllDirectories)) + { + Directory.CreateDirectory(Path.Combine(outputDataDir, dir.Substring(templateDirPath.Length + 1))); + } + + foreach (string file in Directory.GetFiles(templateDirPath, "*", SearchOption.AllDirectories)) + { + File.Copy(file, Path.Combine(outputDataDir, file.Substring(templateDirPath.Length + 1))); + } + } + } + + public static bool PlatformHasTemplateDir(HashSet<string> featureSet) + { + // OSX export templates are contained in a zip, so we place + // our custom template inside it and let Godot do the rest. + return !featureSet.Contains("OSX"); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + extern static string GetTemplatesDir(); + + [MethodImpl(MethodImplOptions.InternalCall)] + extern static string GetDataDirName(); + } +} diff --git a/modules/mono/editor/GodotSharpTools/Editor/MonoDevelopInstance.cs b/modules/mono/editor/GodotSharpTools/Editor/MonoDevelopInstance.cs index 303be3b732..fba4a8f65c 100644 --- a/modules/mono/editor/GodotSharpTools/Editor/MonoDevelopInstance.cs +++ b/modules/mono/editor/GodotSharpTools/Editor/MonoDevelopInstance.cs @@ -2,13 +2,23 @@ using System; using System.IO; using System.Collections.Generic; using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; namespace GodotSharpTools.Editor { public class MonoDevelopInstance { - private Process process; - private string solutionFile; + public enum EditorId + { + MonoDevelop = 0, + VisualStudioForMac = 1 + } + + readonly string solutionFile; + readonly EditorId editorId; + + Process process; public void Execute(string[] files) { @@ -16,6 +26,35 @@ namespace GodotSharpTools.Editor List<string> args = new List<string>(); + string command; + + if (Utils.OS.IsOSX()) + { + string bundleId = codeEditorBundleIds[editorId]; + + if (IsApplicationBundleInstalled(bundleId)) + { + command = "open"; + + args.Add("-b"); + args.Add(bundleId); + + // The 'open' process must wait until the application finishes + if (newWindow) + args.Add("--wait-apps"); + + args.Add("--args"); + } + else + { + command = codeEditorPaths[editorId]; + } + } + else + { + command = codeEditorPaths[editorId]; + } + args.Add("--ipc-tcp"); if (newWindow) @@ -33,25 +72,73 @@ namespace GodotSharpTools.Editor if (newWindow) { - ProcessStartInfo startInfo = new ProcessStartInfo(MonoDevelopFile, string.Join(" ", args)); - process = Process.Start(startInfo); + process = Process.Start(new ProcessStartInfo() + { + FileName = command, + Arguments = string.Join(" ", args), + UseShellExecute = false + }); } else { - Process.Start(MonoDevelopFile, string.Join(" ", args)); + Process.Start(new ProcessStartInfo() + { + FileName = command, + Arguments = string.Join(" ", args), + UseShellExecute = false + }); } } - public MonoDevelopInstance(string solutionFile) + public MonoDevelopInstance(string solutionFile, EditorId editorId) { + if (editorId == EditorId.VisualStudioForMac && !Utils.OS.IsOSX()) + throw new InvalidOperationException($"{nameof(EditorId.VisualStudioForMac)} not supported on this platform"); + this.solutionFile = solutionFile; + this.editorId = editorId; } - private static string MonoDevelopFile + [MethodImpl(MethodImplOptions.InternalCall)] + private extern static bool IsApplicationBundleInstalled(string bundleId); + + static readonly IReadOnlyDictionary<EditorId, string> codeEditorPaths; + static readonly IReadOnlyDictionary<EditorId, string> codeEditorBundleIds; + + static MonoDevelopInstance() { - get + if (Utils.OS.IsOSX()) + { + codeEditorPaths = new Dictionary<EditorId, string> + { + // Rely on PATH + { EditorId.MonoDevelop, "monodevelop" }, + { EditorId.VisualStudioForMac, "VisualStudio" } + }; + codeEditorBundleIds = new Dictionary<EditorId, string> + { + // TODO EditorId.MonoDevelop + { EditorId.VisualStudioForMac, "com.microsoft.visual-studio" } + }; + } + else if (Utils.OS.IsWindows()) + { + codeEditorPaths = new Dictionary<EditorId, string> + { + // XamarinStudio is no longer a thing, and the latest version is quite old + // MonoDevelop is available from source only on Windows. The recommendation + // is to use Visual Studio instead. Since there are no official builds, we + // will rely on custom MonoDevelop builds being added to PATH. + { EditorId.MonoDevelop, "MonoDevelop.exe" } + }; + } + else if (Utils.OS.IsUnix()) { - return "monodevelop"; + codeEditorPaths = new Dictionary<EditorId, string> + { + // Rely on PATH + { EditorId.MonoDevelop, "monodevelop" } + }; } } } diff --git a/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj b/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj index 1c8714e31d..f9e9f41977 100644 --- a/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj +++ b/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj @@ -31,6 +31,9 @@ <Reference Include="System" /> <Reference Include="Microsoft.Build" /> <Reference Include="Microsoft.Build.Framework" /> + <Reference Include="DotNet.Glob, Version=2.1.1.0, Culture=neutral, PublicKeyToken=b68cc888b4f632d1, processorArchitecture=MSIL"> + <HintPath>packages\DotNet.Glob.2.1.1\lib\net45\DotNet.Glob.dll</HintPath> + </Reference> </ItemGroup> <ItemGroup> <Compile Include="StringExtensions.cs" /> @@ -40,6 +43,11 @@ <Compile Include="Project\ProjectGenerator.cs" /> <Compile Include="Project\ProjectUtils.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="Utils\OS.cs" /> + <Compile Include="Editor\GodotSharpExport.cs" /> + </ItemGroup> + <ItemGroup> + <None Include="packages.config" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> </Project>
\ No newline at end of file diff --git a/modules/mono/editor/GodotSharpTools/GodotSharpTools.userprefs b/modules/mono/editor/GodotSharpTools/GodotSharpTools.userprefs deleted file mode 100644 index 0cbafdc20d..0000000000 --- a/modules/mono/editor/GodotSharpTools/GodotSharpTools.userprefs +++ /dev/null @@ -1,14 +0,0 @@ -<Properties StartupItem="GodotSharpTools.csproj"> - <MonoDevelop.Ide.Workspace ActiveConfiguration="Debug" /> - <MonoDevelop.Ide.Workbench ActiveDocument="Build/BuildSystem.cs"> - <Files> - <File FileName="Build/ProjectExtensions.cs" Line="1" Column="1" /> - <File FileName="Build/ProjectGenerator.cs" Line="1" Column="1" /> - <File FileName="Build/BuildSystem.cs" Line="37" Column="14" /> - </Files> - </MonoDevelop.Ide.Workbench> - <MonoDevelop.Ide.DebuggingService.Breakpoints> - <BreakpointStore /> - </MonoDevelop.Ide.DebuggingService.Breakpoints> - <MonoDevelop.Ide.DebuggingService.PinnedWatches /> -</Properties>
\ No newline at end of file diff --git a/modules/mono/editor/GodotSharpTools/Project/ProjectExtensions.cs b/modules/mono/editor/GodotSharpTools/Project/ProjectExtensions.cs index f00ec5a2ad..647d9ac81d 100644 --- a/modules/mono/editor/GodotSharpTools/Project/ProjectExtensions.cs +++ b/modules/mono/editor/GodotSharpTools/Project/ProjectExtensions.cs @@ -1,4 +1,5 @@ using System; +using DotNet.Globbing; using Microsoft.Build.Construction; namespace GodotSharpTools.Project @@ -7,7 +8,10 @@ namespace GodotSharpTools.Project { public static bool HasItem(this ProjectRootElement root, string itemType, string include) { - string includeNormalized = include.NormalizePath(); + GlobOptions globOptions = new GlobOptions(); + globOptions.Evaluation.CaseInsensitive = false; + + string normalizedInclude = include.NormalizePath(); foreach (var itemGroup in root.ItemGroups) { @@ -16,10 +20,14 @@ namespace GodotSharpTools.Project foreach (var item in itemGroup.Items) { - if (item.ItemType == itemType) + if (item.ItemType != itemType) + continue; + + var glob = Glob.Parse(item.Include.NormalizePath(), globOptions); + + if (glob.IsMatch(normalizedInclude)) { - if (item.Include.NormalizePath() == includeNormalized) - return true; + return true; } } } diff --git a/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs b/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs index 1d863e6f61..574b711b29 100644 --- a/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs +++ b/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs @@ -6,6 +6,9 @@ namespace GodotSharpTools.Project { public static class ProjectGenerator { + const string CoreApiProjectGuid = "{AEBF0036-DA76-4341-B651-A3F2856AB2FA}"; + const string EditorApiProjectGuid = "{8FBEC238-D944-4074-8548-B3B524305905}"; + public static string GenCoreApiProject(string dir, string[] compileItems) { string path = Path.Combine(dir, CoreApiProject + ".csproj"); @@ -15,6 +18,7 @@ namespace GodotSharpTools.Project mainGroup.AddProperty("DocumentationFile", Path.Combine("$(OutputPath)", "$(AssemblyName).xml")); mainGroup.SetProperty("RootNamespace", "Godot"); + mainGroup.SetProperty("ProjectGuid", CoreApiProjectGuid); GenAssemblyInfoFile(root, dir, CoreApiProject, new string[] { "[assembly: InternalsVisibleTo(\"" + EditorApiProject + "\")]" }, @@ -39,6 +43,7 @@ namespace GodotSharpTools.Project mainGroup.AddProperty("DocumentationFile", Path.Combine("$(OutputPath)", "$(AssemblyName).xml")); mainGroup.SetProperty("RootNamespace", "Godot"); + mainGroup.SetProperty("ProjectGuid", EditorApiProjectGuid); GenAssemblyInfoFile(root, dir, EditorApiProject); diff --git a/modules/mono/editor/GodotSharpTools/Project/ProjectUtils.cs b/modules/mono/editor/GodotSharpTools/Project/ProjectUtils.cs index 6889ea715f..a13f4fd6ef 100644 --- a/modules/mono/editor/GodotSharpTools/Project/ProjectUtils.cs +++ b/modules/mono/editor/GodotSharpTools/Project/ProjectUtils.cs @@ -1,5 +1,6 @@ -using System; +using System.Collections.Generic; using System.IO; +using DotNet.Globbing; using Microsoft.Build.Construction; namespace GodotSharpTools.Project @@ -10,8 +11,61 @@ namespace GodotSharpTools.Project { var dir = Directory.GetParent(projectPath).FullName; var root = ProjectRootElement.Open(projectPath); - if (root.AddItemChecked(itemType, include.RelativeToPath(dir).Replace("/", "\\"))) + var normalizedInclude = include.RelativeToPath(dir).Replace("/", "\\"); + + if (root.AddItemChecked(itemType, normalizedInclude)) root.Save(); } + + private static string[] GetAllFilesRecursive(string rootDirectory, string mask) + { + string[] files = Directory.GetFiles(rootDirectory, mask, SearchOption.AllDirectories); + + // We want relative paths + for (int i = 0; i < files.Length; i++) { + files[i] = files[i].RelativeToPath(rootDirectory); + } + + return files; + } + + public static string[] GetIncludeFiles(string projectPath, string itemType) + { + var result = new List<string>(); + var existingFiles = GetAllFilesRecursive(Path.GetDirectoryName(projectPath), "*.cs"); + + GlobOptions globOptions = new GlobOptions(); + globOptions.Evaluation.CaseInsensitive = false; + + var root = ProjectRootElement.Open(projectPath); + + foreach (var itemGroup in root.ItemGroups) + { + if (itemGroup.Condition.Length != 0) + continue; + + foreach (var item in itemGroup.Items) + { + if (item.ItemType != itemType) + continue; + + string normalizedInclude = item.Include.NormalizePath(); + + var glob = Glob.Parse(normalizedInclude, globOptions); + + // TODO Check somehow if path has no blog to avoid the following loop... + + foreach (var existingFile in existingFiles) + { + if (glob.IsMatch(existingFile)) + { + result.Add(existingFile); + } + } + } + } + + return result.ToArray(); + } } } diff --git a/modules/mono/editor/GodotSharpTools/StringExtensions.cs b/modules/mono/editor/GodotSharpTools/StringExtensions.cs index b66c86f8ce..b0436d2f18 100644 --- a/modules/mono/editor/GodotSharpTools/StringExtensions.cs +++ b/modules/mono/editor/GodotSharpTools/StringExtensions.cs @@ -36,7 +36,9 @@ namespace GodotSharpTools public static bool IsAbsolutePath(this string path) { - return path.StartsWith("/") || path.StartsWith("\\") || path.StartsWith(driveRoot); + return path.StartsWith("/", StringComparison.Ordinal) || + path.StartsWith("\\", StringComparison.Ordinal) || + path.StartsWith(driveRoot, StringComparison.Ordinal); } public static string CsvEscape(this string value, char delimiter = ',') diff --git a/modules/mono/editor/GodotSharpTools/Utils/OS.cs b/modules/mono/editor/GodotSharpTools/Utils/OS.cs new file mode 100644 index 0000000000..148e954e77 --- /dev/null +++ b/modules/mono/editor/GodotSharpTools/Utils/OS.cs @@ -0,0 +1,62 @@ +using System; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace GodotSharpTools.Utils +{ + public static class OS + { + [MethodImpl(MethodImplOptions.InternalCall)] + extern static string GetPlatformName(); + + const string HaikuName = "Haiku"; + const string OSXName = "OSX"; + const string ServerName = "Server"; + const string UWPName = "UWP"; + const string WindowsName = "Windows"; + const string X11Name = "X11"; + + public static bool IsHaiku() + { + return HaikuName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase); + } + + public static bool IsOSX() + { + return OSXName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase); + } + + public static bool IsServer() + { + return ServerName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase); + } + + public static bool IsUWP() + { + return UWPName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase); + } + + public static bool IsWindows() + { + return WindowsName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase); + } + + public static bool IsX11() + { + return X11Name.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase); + } + + static bool? IsUnixCache = null; + static readonly string[] UnixPlatforms = new string[] { HaikuName, OSXName, ServerName, X11Name }; + + public static bool IsUnix() + { + if (IsUnixCache.HasValue) + return IsUnixCache.Value; + + string osName = GetPlatformName(); + IsUnixCache = UnixPlatforms.Any(p => p.Equals(osName, StringComparison.OrdinalIgnoreCase)); + return IsUnixCache.Value; + } + } +} diff --git a/modules/mono/editor/GodotSharpTools/packages.config b/modules/mono/editor/GodotSharpTools/packages.config new file mode 100644 index 0000000000..2c7cb0bd4b --- /dev/null +++ b/modules/mono/editor/GodotSharpTools/packages.config @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="DotNet.Glob" version="2.1.1" targetFramework="net45" /> +</packages>
\ No newline at end of file diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index b97bb5e95f..710682d3aa 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -32,15 +32,16 @@ #ifdef DEBUG_METHODS_ENABLED -#include "engine.h" -#include "global_constants.h" -#include "io/compression.h" -#include "os/dir_access.h" -#include "os/file_access.h" -#include "os/os.h" -#include "ucaps.h" +#include "core/engine.h" +#include "core/global_constants.h" +#include "core/io/compression.h" +#include "core/os/dir_access.h" +#include "core/os/file_access.h" +#include "core/os/os.h" +#include "core/ucaps.h" #include "../glue/cs_compressed.gen.h" +#include "../glue/cs_glue_version.gen.h" #include "../godotsharp_defs.h" #include "../mono_gd/gd_mono_marshal.h" #include "../utils/path_utils.h" @@ -48,7 +49,7 @@ #include "csharp_project.h" #include "net_solution.h" -#define CS_INDENT " " +#define CS_INDENT " " // 4 whitespaces #define INDENT1 CS_INDENT #define INDENT2 INDENT1 INDENT1 @@ -68,23 +69,18 @@ #define CLOSE_BLOCK_L3 INDENT3 CLOSE_BLOCK #define CLOSE_BLOCK_L4 INDENT4 CLOSE_BLOCK -#define LOCAL_RET "ret" - #define CS_FIELD_MEMORYOWN "memoryOwn" #define CS_PARAM_METHODBIND "method" #define CS_PARAM_INSTANCE "ptr" #define CS_SMETHOD_GETINSTANCE "GetPtr" -#define CS_FIELD_SINGLETON "instance" -#define CS_PROP_SINGLETON "Instance" -#define CS_CLASS_SIGNALAWAITER "SignalAwaiter" #define CS_METHOD_CALL "Call" #define GLUE_HEADER_FILE "glue_header.h" #define ICALL_PREFIX "godot_icall_" #define SINGLETON_ICALL_SUFFIX "_get_singleton" -#define ICALL_GET_METHODBIND ICALL_PREFIX "ClassDB_get_method" -#define ICALL_CONNECT_SIGNAL_AWAITER ICALL_PREFIX "Object_connect_signal_awaiter" -#define ICALL_OBJECT_DTOR ICALL_PREFIX "Object_Dtor" +#define ICALL_GET_METHODBIND ICALL_PREFIX "Object_ClassDB_get_method" + +#define C_LOCAL_RET "ret" #define C_LOCAL_PTRCALL_ARGS "call_args" #define C_MACRO_OBJECT_CONSTRUCT "GODOTSHARP_INSTANCE_OBJECT" @@ -101,7 +97,7 @@ #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 BINDINGS_GENERATOR_VERSION UINT32_C(2) +#define BINDINGS_GENERATOR_VERSION UINT32_C(5) const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN = "\t%0 %1_in = %1;\n"; @@ -177,65 +173,74 @@ static String snake_to_camel_case(const String &p_identifier, bool p_input_is_up return ret; } -String BindingsGenerator::_determine_enum_prefix(const EnumInterface &p_ienum) { +int BindingsGenerator::_determine_enum_prefix(const EnumInterface &p_ienum) { CRASH_COND(p_ienum.constants.empty()); - const List<ConstantInterface>::Element *front = p_ienum.constants.front(); - int candidate_len = front->get().name.length(); + const ConstantInterface &front_iconstant = p_ienum.constants.front()->get(); + Vector<String> front_parts = front_iconstant.name.split("_", /* p_allow_empty: */ true); + int candidate_len = front_parts.size() - 1; - for (const List<ConstantInterface>::Element *E = front->next(); E; E = E->next()) { - int j = 0; - for (j = 0; j < candidate_len && j < E->get().name.length(); j++) { - if (front->get().name[j] != E->get().name[j]) - break; + if (candidate_len == 0) + return 0; + + for (const List<ConstantInterface>::Element *E = p_ienum.constants.front()->next(); E; E = E->next()) { + const ConstantInterface &iconstant = E->get(); + + Vector<String> parts = iconstant.name.split("_", /* p_allow_empty: */ true); + + int i; + for (i = 0; i < candidate_len && i < parts.size(); i++) { + if (front_parts[i] != parts[i]) { + // HARDCODED: Some Flag enums have the prefix 'FLAG_' for everything except 'FLAGS_DEFAULT' (same for 'METHOD_FLAG_' and'METHOD_FLAGS_DEFAULT'). + bool hardcoded_exc = (i == candidate_len - 1 && ((front_parts[i] == "FLAGS" && parts[i] == "FLAG") || (front_parts[i] == "FLAG" && parts[i] == "FLAGS"))); + if (!hardcoded_exc) + break; + } } - candidate_len = j; + candidate_len = i; + + if (candidate_len == 0) + return 0; } - return front->get().name.substr(0, candidate_len); + return candidate_len; } -void BindingsGenerator::_generate_header_icalls() { +void BindingsGenerator::_apply_prefix_to_enum_constants(BindingsGenerator::EnumInterface &p_ienum, int p_prefix_length) { - core_custom_icalls.clear(); + if (p_prefix_length > 0) { + for (List<ConstantInterface>::Element *E = p_ienum.constants.front(); E; E = E->next()) { + int curr_prefix_length = p_prefix_length; + + ConstantInterface &curr_const = E->get(); + + String constant_name = curr_const.name; + + Vector<String> parts = constant_name.split("_", /* p_allow_empty: */ true); + + if (parts.size() <= curr_prefix_length) + continue; + + if (parts[curr_prefix_length][0] >= '0' && parts[curr_prefix_length][0] <= '9') { + // The name of enum constants may begin with a numeric digit when strip from the enum prefix, + // so we make the prefix for this constant one word shorter in those cases. + for (curr_prefix_length = curr_prefix_length - 1; curr_prefix_length > 0; curr_prefix_length--) { + if (parts[curr_prefix_length][0] < '0' || parts[curr_prefix_length][0] > '9') + break; + } + } + + constant_name = ""; + for (int i = curr_prefix_length; i < parts.size(); i++) { + if (i > curr_prefix_length) + constant_name += "_"; + constant_name += parts[i]; + } - core_custom_icalls.push_back(InternalCall(ICALL_GET_METHODBIND, "IntPtr", "string type, string method")); - core_custom_icalls.push_back(InternalCall(ICALL_OBJECT_DTOR, "void", "object obj, IntPtr ptr")); - - core_custom_icalls.push_back(InternalCall(ICALL_CONNECT_SIGNAL_AWAITER, "Error", - "IntPtr source, string signal, IntPtr target, " CS_CLASS_SIGNALAWAITER " awaiter")); - - core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "NodePath_Ctor", "IntPtr", "string path")); - core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "NodePath_Dtor", "void", "IntPtr ptr")); - core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "NodePath_operator_String", "string", "IntPtr ptr")); - - core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "RID_Ctor", "IntPtr", "IntPtr from")); - core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "RID_Dtor", "void", "IntPtr ptr")); - - core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "String_md5_buffer", "byte[]", "string str")); - core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "String_md5_text", "string", "string str")); - core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "String_rfind", "int", "string str, string what, int from")); - core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "String_rfindn", "int", "string str, string what, int from")); - core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "String_sha256_buffer", "byte[]", "string str")); - core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "String_sha256_text", "string", "string str")); - - core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "Godot_bytes2var", "object", "byte[] bytes")); - core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "Godot_convert", "object", "object what, int type")); - core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "Godot_hash", "int", "object var")); - core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "Godot_instance_from_id", "Object", "int instance_id")); - core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "Godot_print", "void", "object[] what")); - core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "Godot_printerr", "void", "object[] what")); - core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "Godot_printraw", "void", "object[] what")); - core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "Godot_prints", "void", "object[] what")); - core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "Godot_printt", "void", "object[] what")); - core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "Godot_seed", "void", "int seed")); - core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "Godot_str", "string", "object[] what")); - core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "Godot_str2var", "object", "string str")); - core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "Godot_type_exists", "bool", "string type")); - core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "Godot_var2bytes", "byte[]", "object what")); - core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "Godot_var2str", "string", "object var")); - core_custom_icalls.push_back(InternalCall(ICALL_PREFIX "Godot_weakref", "WeakRef", "IntPtr obj")); + curr_const.proxy_name = snake_to_pascal_case(constant_name, true); + } + } } void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) { @@ -248,13 +253,8 @@ void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) { const TypeInterface *return_type = _get_type_or_placeholder(imethod.return_type); - String im_sig; - String im_unique_sig; - - if (p_itype.is_object_type) { - im_sig += "IntPtr " CS_PARAM_METHODBIND ", "; - im_unique_sig += imethod.return_type.cname.operator String() + ",IntPtr,IntPtr"; - } + String im_sig = "IntPtr " CS_PARAM_METHODBIND ", "; + String im_unique_sig = imethod.return_type.cname.operator String() + ",IntPtr,IntPtr"; im_sig += "IntPtr " CS_PARAM_INSTANCE; @@ -268,37 +268,28 @@ void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) { im_sig += " arg"; im_sig += itos(i + 1); - if (p_itype.is_object_type) { - im_unique_sig += ","; - im_unique_sig += get_unique_sig(*arg_type); - } + im_unique_sig += ","; + im_unique_sig += get_unique_sig(*arg_type); i++; } + // godot_icall_{argc}_{icallcount} String icall_method = ICALL_PREFIX; - - if (p_itype.is_object_type) { - icall_method += itos(imethod.arguments.size()) + "_" + itos(method_icalls.size()); // godot_icall_{argc}_{icallcount} - } else { - icall_method += p_itype.name + "_" + imethod.name; // godot_icall_{Type}_{method} - } + icall_method += itos(imethod.arguments.size()); + icall_method += "_"; + icall_method += itos(method_icalls.size()); InternalCall im_icall = InternalCall(p_itype.api_type, icall_method, return_type->im_type_out, im_sig, im_unique_sig); - if (p_itype.is_object_type) { - List<InternalCall>::Element *match = method_icalls.find(im_icall); + List<InternalCall>::Element *match = method_icalls.find(im_icall); - if (match) { - if (p_itype.api_type != ClassDB::API_EDITOR) - match->get().editor_only = false; - method_icalls_map.insert(&E->get(), &match->get()); - } else { - List<InternalCall>::Element *added = method_icalls.push_back(im_icall); - method_icalls_map.insert(&E->get(), &added->get()); - } + if (match) { + if (p_itype.api_type != ClassDB::API_EDITOR) + match->get().editor_only = false; + method_icalls_map.insert(&E->get(), &match->get()); } else { - List<InternalCall>::Element *added = builtin_method_icalls.push_back(im_icall); + List<InternalCall>::Element *added = method_icalls.push_back(im_icall); method_icalls_map.insert(&E->get(), &added->get()); } } @@ -332,7 +323,7 @@ void BindingsGenerator::_generate_global_constants(List<String> &p_output) { } p_output.push_back(MEMBER_BEGIN "public const int "); - p_output.push_back(iconstant.name); + p_output.push_back(iconstant.proxy_name); p_output.push_back(" = "); p_output.push_back(itos(iconstant.value)); p_output.push_back(";"); @@ -394,25 +385,8 @@ void BindingsGenerator::_generate_global_constants(List<String> &p_output) { p_output.push_back(INDENT2 "/// </summary>\n"); } - String constant_name = iconstant.name; - - if (!ienum.prefix.empty() && constant_name.begins_with(ienum.prefix)) { - constant_name = constant_name.substr(ienum.prefix.length(), constant_name.length()); - } - - if (constant_name[0] >= '0' && constant_name[0] <= '9') { - // The name of enum constants may begin with a numeric digit when strip from the enum prefix, - // so we make the prefix one word shorter in those cases. - int i = 0; - for (i = ienum.prefix.length() - 1; i >= 0; i--) { - if (ienum.prefix[i] >= 'A' && ienum.prefix[i] <= 'Z') - break; - } - constant_name = ienum.prefix.substr(i, ienum.prefix.length()) + constant_name; - } - p_output.push_back(INDENT2); - p_output.push_back(constant_name); + p_output.push_back(iconstant.proxy_name); p_output.push_back(" = "); p_output.push_back(itos(iconstant.value)); p_output.push_back(E != ienum.constants.back() ? ",\n" : "\n"); @@ -483,20 +457,6 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_output_dir, bo compile_items.push_back(output_file); } -#define GENERATE_BUILTIN_TYPE(m_name) \ - { \ - String output_file = path_join(core_dir, #m_name ".cs"); \ - Error err = _generate_cs_type(builtin_types[#m_name], output_file); \ - if (err != OK) \ - return err; \ - compile_items.push_back(output_file); \ - } - - GENERATE_BUILTIN_TYPE(NodePath); - GENERATE_BUILTIN_TYPE(RID); - -#undef GENERATE_BUILTIN_TYPE - // Generate sources from compressed files Map<String, CompressedFile> compressed_files; @@ -533,34 +493,30 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_output_dir, bo cs_icalls_content.push_back("using System;\n" "using System.Runtime.CompilerServices;\n" - "using System.Collections.Generic;\n" "\n"); cs_icalls_content.push_back("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK); cs_icalls_content.push_back(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS "\n" INDENT1 OPEN_BLOCK); - cs_icalls_content.push_back(INDENT2 "internal static ulong godot_api_hash = "); + cs_icalls_content.push_back(MEMBER_BEGIN "internal static ulong godot_api_hash = "); cs_icalls_content.push_back(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + ";\n"); - cs_icalls_content.push_back(INDENT2 "internal static uint bindings_version = "); + cs_icalls_content.push_back(MEMBER_BEGIN "internal static uint bindings_version = "); cs_icalls_content.push_back(String::num_uint64(BINDINGS_GENERATOR_VERSION) + ";\n"); - cs_icalls_content.push_back(INDENT2 "internal static uint cs_glue_version = "); + cs_icalls_content.push_back(MEMBER_BEGIN "internal static uint cs_glue_version = "); cs_icalls_content.push_back(String::num_uint64(CS_GLUE_VERSION) + ";\n"); - cs_icalls_content.push_back("\n"); -#define ADD_INTERNAL_CALL(m_icall) \ - if (!m_icall.editor_only) { \ - cs_icalls_content.push_back(INDENT2 "[MethodImpl(MethodImplOptions.InternalCall)]\n"); \ - cs_icalls_content.push_back(INDENT2 "internal extern static "); \ - cs_icalls_content.push_back(m_icall.im_type_out + " "); \ - cs_icalls_content.push_back(m_icall.name + "("); \ - cs_icalls_content.push_back(m_icall.im_sig + ");\n"); \ +#define ADD_INTERNAL_CALL(m_icall) \ + if (!m_icall.editor_only) { \ + cs_icalls_content.push_back(MEMBER_BEGIN "[MethodImpl(MethodImplOptions.InternalCall)]\n"); \ + cs_icalls_content.push_back(INDENT2 "internal extern static "); \ + cs_icalls_content.push_back(m_icall.im_type_out + " "); \ + cs_icalls_content.push_back(m_icall.name + "("); \ + cs_icalls_content.push_back(m_icall.im_sig + ");\n"); \ } for (const List<InternalCall>::Element *E = core_custom_icalls.front(); E; E = E->next()) ADD_INTERNAL_CALL(E->get()); for (const List<InternalCall>::Element *E = method_icalls.front(); E; E = E->next()) ADD_INTERNAL_CALL(E->get()); - for (const List<InternalCall>::Element *E = builtin_method_icalls.front(); E; E = E->next()) - ADD_INTERNAL_CALL(E->get()); #undef ADD_INTERNAL_CALL @@ -638,7 +594,6 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_output_dir, cs_icalls_content.push_back("using System;\n" "using System.Runtime.CompilerServices;\n" - "using System.Collections.Generic;\n" "\n"); cs_icalls_content.push_back("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK); cs_icalls_content.push_back(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS_EDITOR "\n" INDENT1 OPEN_BLOCK); @@ -660,8 +615,6 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_output_dir, cs_icalls_content.push_back(m_icall.im_sig + ");\n"); \ } - // No need to add builtin_method_icalls. Builtin types are core only - for (const List<InternalCall>::Element *E = editor_custom_icalls.front(); E; E = E->next()) ADD_INTERNAL_CALL(E->get()); for (const List<InternalCall>::Element *E = method_icalls.front(); E; E = E->next()) @@ -695,25 +648,42 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_output_dir, return OK; } -// TODO: there are constants that hide inherited members. must explicitly use `new` to avoid warnings -// e.g.: warning CS0108: 'SpriteBase3D.FLAG_MAX' hides inherited member 'GeometryInstance.FLAG_MAX'. Use the new keyword if hiding was intended. +// FIXME: There are some members that hide other inherited members. +// - In the case of both members being the same kind, the new one must be declared +// explicitly as `new` to avoid the warning (and we must print a message about it). +// - In the case of both members being of a different kind, then the new one must +// be renamed to avoid the name collision (and we must print a warning about it). +// - Csc warning e.g.: +// ObjectType/LineEdit.cs(140,38): warning CS0108: 'LineEdit.FocusMode' hides inherited member 'Control.FocusMode'. Use the new keyword if hiding was intended. Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const String &p_output_file) { + CRASH_COND(!itype.is_object_type); + bool is_derived_type = itype.base_name != StringName(); + if (!is_derived_type) { + // Some Godot.Object assertions + CRASH_COND(itype.cname != name_cache.type_Object); + CRASH_COND(!itype.is_instantiable); + CRASH_COND(itype.api_type != ClassDB::API_CORE); + CRASH_COND(itype.is_reference); + CRASH_COND(itype.is_singleton); + } + List<InternalCall> &custom_icalls = itype.api_type == ClassDB::API_EDITOR ? editor_custom_icalls : core_custom_icalls; if (verbose_output) OS::get_singleton()->print(String("Generating " + itype.proxy_name + ".cs...\n").utf8()); - String ctor_method(ICALL_PREFIX + itype.proxy_name + "_Ctor"); + String ctor_method(ICALL_PREFIX + itype.proxy_name + "_Ctor"); // Used only for derived types List<String> output; output.push_back("using System;\n"); // IntPtr + output.push_back("using System.Diagnostics;\n"); // DebuggerBrowsable - if (itype.requires_collections) - output.push_back("using System.Collections.Generic;\n"); // Dictionary + output.push_back("\n#pragma warning disable CS1591 // Disable warning: " + "'Missing XML comment for publicly visible type or member'\n"); output.push_back("\nnamespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK); @@ -737,21 +707,24 @@ 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 partial class " : (is_abstract ? "abstract partial class " : "partial class ")); + if (itype.is_singleton) { + output.push_back("static partial class "); + } else { + output.push_back(itype.is_instantiable ? "partial class " : "abstract partial class "); + } output.push_back(itype.proxy_name); if (itype.is_singleton) { output.push_back("\n"); - } else if (!is_derived_type || !itype.is_object_type /* assuming only object types inherit */) { - output.push_back(" : IDisposable\n"); - } else if (obj_types.has(itype.base_name)) { - output.push_back(" : "); - output.push_back(obj_types[itype.base_name].proxy_name); - output.push_back("\n"); - } else { - ERR_PRINTS("Base type '" + itype.base_name.operator String() + "' does not exist, for class " + itype.name); - return ERR_INVALID_DATA; + } else if (is_derived_type) { + if (obj_types.has(itype.base_name)) { + output.push_back(" : "); + output.push_back(obj_types[itype.base_name].proxy_name); + output.push_back("\n"); + } else { + ERR_PRINTS("Base type '" + itype.base_name.operator String() + "' does not exist, for class " + itype.name); + return ERR_INVALID_DATA; + } } output.push_back(INDENT1 "{"); @@ -781,7 +754,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str } output.push_back(MEMBER_BEGIN "public const int "); - output.push_back(iconstant.name); + output.push_back(iconstant.proxy_name); output.push_back(" = "); output.push_back(itos(iconstant.value)); output.push_back(";"); @@ -821,25 +794,8 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str output.push_back(INDENT3 "/// </summary>\n"); } - String constant_name = iconstant.name; - - if (!ienum.prefix.empty() && constant_name.begins_with(ienum.prefix)) { - constant_name = constant_name.substr(ienum.prefix.length(), constant_name.length()); - } - - if (constant_name[0] >= '0' && constant_name[0] <= '9') { - // The name of enum constants may begin with a numeric digit when strip from the enum prefix, - // so we make the prefix one word shorter in those cases. - int i = 0; - for (i = ienum.prefix.length() - 1; i >= 0; i--) { - if (ienum.prefix[i] >= 'A' && ienum.prefix[i] <= 'Z') - break; - } - constant_name = ienum.prefix.substr(i, ienum.prefix.length()) + constant_name; - } - output.push_back(INDENT3); - output.push_back(constant_name); + output.push_back(iconstant.proxy_name); output.push_back(" = "); output.push_back(itos(iconstant.value)); output.push_back(E != ienum.constants.back() ? ",\n" : "\n"); @@ -848,9 +804,6 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str output.push_back(INDENT2 CLOSE_BLOCK); } - if (itype.enums.size()) - output.push_back("\n"); - // Add properties for (const List<PropertyInterface>::Element *E = itype.properties.front(); E; E = E->next()) { @@ -862,43 +815,9 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str ERR_FAIL_V(prop_err); } } - - if (class_doc->properties.size()) - output.push_back("\n"); } - if (!itype.is_object_type) { - output.push_back(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \"" + itype.name + "\";\n"); - output.push_back(MEMBER_BEGIN "private bool disposed = false;\n"); - output.push_back(MEMBER_BEGIN "internal IntPtr " BINDINGS_PTR_FIELD ";\n"); - - output.push_back(MEMBER_BEGIN "internal static IntPtr " CS_SMETHOD_GETINSTANCE "("); - output.push_back(itype.proxy_name); - output.push_back(" instance)\n" OPEN_BLOCK_L2 "return instance == null ? IntPtr.Zero : instance." BINDINGS_PTR_FIELD ";\n" CLOSE_BLOCK_L2); - - // Add Destructor - output.push_back(MEMBER_BEGIN "~"); - output.push_back(itype.proxy_name); - output.push_back("()\n" OPEN_BLOCK_L2 "Dispose(false);\n" CLOSE_BLOCK_L2); - - // Add the Dispose from IDisposable - output.push_back(MEMBER_BEGIN "public void Dispose()\n" OPEN_BLOCK_L2 "Dispose(true);\n" INDENT3 "GC.SuppressFinalize(this);\n" CLOSE_BLOCK_L2); - - // Add the virtual Dispose - output.push_back(MEMBER_BEGIN "protected virtual void Dispose(bool disposing)\n" OPEN_BLOCK_L2 - "if (disposed) return;\n" INDENT3 - "if (" BINDINGS_PTR_FIELD " != IntPtr.Zero)\n" OPEN_BLOCK_L3 "NativeCalls.godot_icall_"); - output.push_back(itype.proxy_name); - output.push_back("_Dtor(" BINDINGS_PTR_FIELD ");\n" INDENT5 BINDINGS_PTR_FIELD " = IntPtr.Zero;\n" CLOSE_BLOCK_L3 INDENT3 - "GC.SuppressFinalize(this);\n" INDENT3 "disposed = true;\n" CLOSE_BLOCK_L2); - - output.push_back(MEMBER_BEGIN "internal "); - output.push_back(itype.proxy_name); - output.push_back("(IntPtr " BINDINGS_PTR_FIELD ")\n" OPEN_BLOCK_L2 "this." BINDINGS_PTR_FIELD " = " BINDINGS_PTR_FIELD ";\n" CLOSE_BLOCK_L2); - - output.push_back(MEMBER_BEGIN "public IntPtr NativeInstance\n" OPEN_BLOCK_L2 - "get { return " BINDINGS_PTR_FIELD "; }\n" CLOSE_BLOCK_L2); - } else if (itype.is_singleton) { + if (itype.is_singleton) { // Add the type name and the singleton pointer as static fields output.push_back(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \""); @@ -910,21 +829,13 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str output.push_back("." ICALL_PREFIX); output.push_back(itype.name); output.push_back(SINGLETON_ICALL_SUFFIX "();\n"); - } else { + } else if (is_derived_type) { // Add member fields output.push_back(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \""); output.push_back(itype.name); output.push_back("\";\n"); - // Only the base class stores the pointer to the native object - // This pointer is expected to be and must be of type Object* - if (!is_derived_type) { - output.push_back(MEMBER_BEGIN "private bool disposed = false;\n"); - output.push_back(INDENT2 "internal IntPtr " BINDINGS_PTR_FIELD ";\n"); - output.push_back(INDENT2 "internal bool " CS_FIELD_MEMORYOWN ";\n"); - } - // Add default constructor if (itype.is_instantiable) { output.push_back(MEMBER_BEGIN "public "); @@ -949,67 +860,9 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str // Add.. em.. trick constructor. Sort of. output.push_back(MEMBER_BEGIN "internal "); output.push_back(itype.proxy_name); - if (is_derived_type) { - output.push_back("(bool " CS_FIELD_MEMORYOWN ") : base(" CS_FIELD_MEMORYOWN ") {}\n"); - } else { - output.push_back("(bool " CS_FIELD_MEMORYOWN ")\n" OPEN_BLOCK_L2 - "this." CS_FIELD_MEMORYOWN " = " CS_FIELD_MEMORYOWN ";\n" CLOSE_BLOCK_L2); - } - - // Add methods - - if (!is_derived_type) { - output.push_back(MEMBER_BEGIN "public IntPtr NativeInstance\n" OPEN_BLOCK_L2 - "get { return " BINDINGS_PTR_FIELD "; }\n" CLOSE_BLOCK_L2); - - output.push_back(MEMBER_BEGIN "internal static IntPtr " CS_SMETHOD_GETINSTANCE "(Object instance)\n" OPEN_BLOCK_L2 - "return instance == null ? IntPtr.Zero : instance." BINDINGS_PTR_FIELD ";\n" CLOSE_BLOCK_L2); - } - - if (!is_derived_type) { - // Add destructor - output.push_back(MEMBER_BEGIN "~"); - output.push_back(itype.proxy_name); - output.push_back("()\n" OPEN_BLOCK_L2 "Dispose(false);\n" CLOSE_BLOCK_L2); - - // Add the Dispose from IDisposable - output.push_back(MEMBER_BEGIN "public void Dispose()\n" OPEN_BLOCK_L2 "Dispose(true);\n" INDENT3 "GC.SuppressFinalize(this);\n" CLOSE_BLOCK_L2); - - // Add the virtual Dispose - output.push_back(MEMBER_BEGIN "protected virtual void Dispose(bool disposing)\n" OPEN_BLOCK_L2 - "if (disposed) return;\n" INDENT3 - "if (" BINDINGS_PTR_FIELD " != IntPtr.Zero)\n" OPEN_BLOCK_L3 - "if (" CS_FIELD_MEMORYOWN ")\n" OPEN_BLOCK_L4 CS_FIELD_MEMORYOWN - " = false;\n" INDENT5 BINDINGS_CLASS_NATIVECALLS "." ICALL_OBJECT_DTOR - "(this, " BINDINGS_PTR_FIELD ");\n" CLOSE_BLOCK_L4 CLOSE_BLOCK_L3 INDENT3 - "this." BINDINGS_PTR_FIELD " = IntPtr.Zero;\n" INDENT3 - "GC.SuppressFinalize(this);\n" INDENT3 "disposed = true;\n" CLOSE_BLOCK_L2); - - Map<StringName, TypeInterface>::Element *array_itype = builtin_types.find(name_cache.type_Array); - - if (!array_itype) { - ERR_PRINT("BUG: Array type interface not found!"); - return ERR_BUG; - } - - OrderedHashMap<StringName, TypeInterface>::Element object_itype = obj_types.find("Object"); - - if (!object_itype) { - ERR_PRINT("BUG: Object type interface not found!"); - return ERR_BUG; - } - - output.push_back(MEMBER_BEGIN "public " CS_CLASS_SIGNALAWAITER " ToSignal("); - output.push_back(object_itype.get().cs_type); - output.push_back(" source, string signal)\n" OPEN_BLOCK_L2 - "return new " CS_CLASS_SIGNALAWAITER "(source, signal, this);\n" CLOSE_BLOCK_L2); - } + output.push_back("(bool " CS_FIELD_MEMORYOWN ") : base(" CS_FIELD_MEMORYOWN ") {}\n"); } - Map<StringName, String>::Element *extra_member = extra_members.find(itype.cname); - if (extra_member) - output.push_back(extra_member->get()); - int method_bind_count = 0; for (const List<MethodInterface>::Element *E = itype.methods.front(); E; E = E->next()) { const MethodInterface &imethod = E->get(); @@ -1027,14 +880,17 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str custom_icalls.push_back(singleton_icall); } - if (itype.is_instantiable) { + if (is_derived_type && itype.is_instantiable) { InternalCall ctor_icall = InternalCall(itype.api_type, ctor_method, "IntPtr", itype.proxy_name + " obj"); if (!find_icall_by_name(ctor_icall.name, custom_icalls)) custom_icalls.push_back(ctor_icall); } - output.push_back(INDENT1 CLOSE_BLOCK CLOSE_BLOCK); + output.push_back(INDENT1 CLOSE_BLOCK /* class */ + CLOSE_BLOCK /* namespace */); + + output.push_back("\n#pragma warning restore CS1591\n"); return _save_file(p_output_file, output); } @@ -1172,9 +1028,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf String arguments_sig; String cs_in_statements; - String icall_params; - if (p_itype.is_object_type) - icall_params += method_bind_field + ", "; + String icall_params = method_bind_field + ", "; icall_params += sformat(p_itype.cs_in, "this"); List<String> default_args_doc; @@ -1251,9 +1105,9 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf // Generate method { - if (p_itype.is_object_type && !p_imethod.is_virtual && !p_imethod.requires_object_call) { - p_output.push_back(MEMBER_BEGIN "private static IntPtr "); - p_output.push_back(method_bind_field + " = " BINDINGS_CLASS_NATIVECALLS "." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \""); + if (!p_imethod.is_virtual && !p_imethod.requires_object_call) { + p_output.push_back(MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN "private static IntPtr "); + p_output.push_back(method_bind_field + " = Object." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \""); p_output.push_back(p_imethod.name); p_output.push_back("\");\n"); } @@ -1366,19 +1220,31 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) { List<String> output; - output.push_back("#include \"" GLUE_HEADER_FILE "\"\n" - "\n"); + output.push_back("/* THIS FILE IS GENERATED DO NOT EDIT */\n"); + output.push_back("#include \"" GLUE_HEADER_FILE "\"\n"); + output.push_back("\n#ifdef MONO_GLUE_ENABLED\n"); generated_icall_funcs.clear(); for (OrderedHashMap<StringName, TypeInterface>::Element type_elem = obj_types.front(); type_elem; type_elem = type_elem.next()) { const TypeInterface &itype = type_elem.get(); + bool is_derived_type = itype.base_name != StringName(); + + if (!is_derived_type) { + // Some Object assertions + CRASH_COND(itype.cname != name_cache.type_Object); + CRASH_COND(!itype.is_instantiable); + CRASH_COND(itype.api_type != ClassDB::API_CORE); + CRASH_COND(itype.is_reference); + CRASH_COND(itype.is_singleton); + } + List<InternalCall> &custom_icalls = itype.api_type == ClassDB::API_EDITOR ? editor_custom_icalls : core_custom_icalls; OS::get_singleton()->print(String("Generating " + itype.name + "...\n").utf8()); - String ctor_method(ICALL_PREFIX + itype.proxy_name + "_Ctor"); + String ctor_method(ICALL_PREFIX + itype.proxy_name + "_Ctor"); // Used only for derived types for (const List<MethodInterface>::Element *E = itype.methods.front(); E; E = E->next()) { const MethodInterface &imethod = E->get(); @@ -1403,7 +1269,7 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) { output.push_back("\");\n" CLOSE_BLOCK "\n"); } - if (itype.is_instantiable) { + if (is_derived_type && itype.is_instantiable) { InternalCall ctor_icall = InternalCall(itype.api_type, ctor_method, "IntPtr", itype.proxy_name + " obj"); if (!find_icall_by_name(ctor_icall.name, custom_icalls)) @@ -1420,23 +1286,21 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) { } } - output.push_back("namespace GodotSharpBindings\n" OPEN_BLOCK); + output.push_back("namespace GodotSharpBindings\n" OPEN_BLOCK "\n"); output.push_back("uint64_t get_core_api_hash() { return "); - output.push_back(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + "; }\n"); + output.push_back(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + "U; }\n"); output.push_back("#ifdef TOOLS_ENABLED\n" "uint64_t get_editor_api_hash() { return "); - output.push_back(String::num_uint64(GDMono::get_singleton()->get_api_editor_hash()) + - "; }\n#endif // TOOLS_ENABLED\n"); + output.push_back(String::num_uint64(GDMono::get_singleton()->get_api_editor_hash()) + "U; }\n"); + output.push_back("#endif // TOOLS_ENABLED\n"); output.push_back("uint32_t get_bindings_version() { return "); output.push_back(String::num_uint64(BINDINGS_GENERATOR_VERSION) + "; }\n"); - output.push_back("uint32_t get_cs_glue_version() { return "); - output.push_back(String::num_uint64(CS_GLUE_VERSION) + "; }\n"); - output.push_back("void register_generated_icalls() " OPEN_BLOCK); - output.push_back("\tgodot_register_header_icalls();"); + output.push_back("\nvoid register_generated_icalls() " OPEN_BLOCK); + output.push_back("\tgodot_register_glue_header_icalls();\n"); #define ADD_INTERNAL_CALL_REGISTRATION(m_icall) \ { \ @@ -1499,12 +1363,11 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) { output.push_back("#endif\n"); } - for (const List<InternalCall>::Element *E = builtin_method_icalls.front(); E; E = E->next()) - ADD_INTERNAL_CALL_REGISTRATION(E->get()); - #undef ADD_INTERNAL_CALL_REGISTRATION - output.push_back(CLOSE_BLOCK "}\n"); + output.push_back(CLOSE_BLOCK "\n} // namespace GodotSharpBindings\n"); + + output.push_back("\n#endif // MONO_GLUE_ENABLED\n"); Error save_err = _save_file(path_join(p_output_dir, "mono_glue.gen.cpp"), output); if (save_err != OK) @@ -1519,10 +1382,6 @@ uint32_t BindingsGenerator::get_version() { return BINDINGS_GENERATOR_VERSION; } -uint32_t BindingsGenerator::get_cs_glue_version() { - return CS_GLUE_VERSION; -} - Error BindingsGenerator::_save_file(const String &p_path, const List<String> &p_content) { FileAccessRef file = FileAccess::open(p_path, FileAccess::WRITE); @@ -1585,9 +1444,6 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte i++; } - if (!p_itype.is_object_type) - return OK; // no auto-generated icall functions for builtin types - const Map<const MethodInterface *, const InternalCall *>::Element *match = method_icalls_map.find(&p_imethod); ERR_FAIL_NULL_V(match, ERR_BUG); @@ -1622,7 +1478,7 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte } p_output.push_back("\t" + ptrcall_return_type); - p_output.push_back(" " LOCAL_RET); + p_output.push_back(" " C_LOCAL_RET); p_output.push_back(initialization + ";\n"); p_output.push_back("\tERR_FAIL_NULL_V(" CS_PARAM_INSTANCE); p_output.push_back(fail_ret); @@ -1672,7 +1528,7 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte p_output.push_back("\tVariant::CallError vcall_error;\n\t"); if (!ret_void) - p_output.push_back(LOCAL_RET " = "); + p_output.push_back(C_LOCAL_RET " = "); p_output.push_back(CS_PARAM_METHODBIND "->call(" CS_PARAM_INSTANCE ", "); p_output.push_back(p_imethod.arguments.size() ? "(const Variant**)" C_LOCAL_PTRCALL_ARGS ".ptr()" : "NULL"); @@ -1680,14 +1536,14 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte } else { p_output.push_back("\t" CS_PARAM_METHODBIND "->ptrcall(" CS_PARAM_INSTANCE ", "); p_output.push_back(p_imethod.arguments.size() ? C_LOCAL_PTRCALL_ARGS ", " : "NULL, "); - p_output.push_back(!ret_void ? "&" LOCAL_RET ");\n" : "NULL);\n"); + p_output.push_back(!ret_void ? "&" C_LOCAL_RET ");\n" : "NULL);\n"); } if (!ret_void) { if (return_type->c_out.empty()) - p_output.push_back("\treturn " LOCAL_RET ";\n"); + p_output.push_back("\treturn " C_LOCAL_RET ";\n"); else - p_output.push_back(sformat(return_type->c_out, return_type->c_type_out, LOCAL_RET, return_type->name)); + p_output.push_back(sformat(return_type->c_out, return_type->c_type_out, C_LOCAL_RET, return_type->name)); } p_output.push_back(CLOSE_BLOCK "\n"); @@ -1746,9 +1602,6 @@ const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_placehol return &placeholder_types.insert(placeholder.cname, placeholder)->get(); } -static void _create_constant_interface_from(const StringName &p_constant, const DocData::ClassDoc &p_classdoc) { -} - void BindingsGenerator::_populate_object_type_interfaces() { obj_types.clear(); @@ -1883,9 +1736,6 @@ void BindingsGenerator::_populate_object_type_interfaces() { if (virtual_method_list.find(method_info)) { // A virtual method without the virtual flag. This is a special case. - // This type of method can only be found in Object derived types. - ERR_FAIL_COND(!itype.is_object_type); - // There is no method bind, so let's fallback to Godot's object.Call(string, params) imethod.requires_object_call = true; @@ -1922,9 +1772,6 @@ void BindingsGenerator::_populate_object_type_interfaces() { imethod.return_type.cname = Variant::get_type_name(return_info.type); } - if (!itype.requires_collections && imethod.return_type.cname == name_cache.type_Dictionary) - itype.requires_collections = true; - for (int i = 0; i < argc; i++) { PropertyInfo arginfo = method_info.arguments[i]; @@ -1946,9 +1793,6 @@ void BindingsGenerator::_populate_object_type_interfaces() { iarg.name = escape_csharp_keyword(snake_to_camel_case(iarg.name)); - if (!itype.requires_collections && iarg.type.cname == name_cache.type_Dictionary) - itype.requires_collections = true; - if (m && m->has_default_argument(i)) { _default_argument_from_variant(m->get_default_argument(i), iarg); } @@ -2019,11 +1863,13 @@ void BindingsGenerator::_populate_object_type_interfaces() { EnumInterface ienum(enum_proxy_cname); const List<StringName> &constants = enum_map.get(*k); for (const List<StringName>::Element *E = constants.front(); E; E = E->next()) { - int *value = class_info->constant_map.getptr(E->get()); + const StringName &constant_cname = E->get(); + String constant_name = constant_cname.operator String(); + int *value = class_info->constant_map.getptr(constant_cname); ERR_FAIL_NULL(value); - constant_list.erase(E->get().operator String()); + constant_list.erase(constant_name); - ConstantInterface iconstant(snake_to_pascal_case(E->get(), true), *value); + ConstantInterface iconstant(constant_name, snake_to_pascal_case(constant_name, true), *value); iconstant.const_doc = NULL; for (int i = 0; i < itype.class_doc->constants.size(); i++) { @@ -2038,7 +1884,9 @@ void BindingsGenerator::_populate_object_type_interfaces() { ienum.constants.push_back(iconstant); } - ienum.prefix = _determine_enum_prefix(ienum); + int prefix_length = _determine_enum_prefix(ienum); + + _apply_prefix_to_enum_constants(ienum, prefix_length); itype.enums.push_back(ienum); @@ -2052,10 +1900,11 @@ void BindingsGenerator::_populate_object_type_interfaces() { } for (const List<String>::Element *E = constant_list.front(); E; E = E->next()) { - int *value = class_info->constant_map.getptr(E->get()); + const String &constant_name = E->get(); + int *value = class_info->constant_map.getptr(StringName(E->get())); ERR_FAIL_NULL(value); - ConstantInterface iconstant(snake_to_pascal_case(E->get(), true), *value); + ConstantInterface iconstant(constant_name, snake_to_pascal_case(constant_name, true), *value); iconstant.const_doc = NULL; for (int i = 0; i < itype.class_doc->constants.size(); i++) { @@ -2166,18 +2015,18 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { TypeInterface itype; -#define INSERT_STRUCT_TYPE(m_type, m_type_in) \ - { \ - itype = TypeInterface::create_value_type(String(#m_type)); \ - itype.c_in = "\tMARSHALLED_IN(" #m_type ", %1, %1_in);\n"; \ - itype.c_out = "\tMARSHALLED_OUT(" #m_type ", %1, ret_out)\n" \ - "\treturn mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(%2), ret_out);\n"; \ - itype.c_arg_in = "&%s_in"; \ - itype.c_type_in = m_type_in; \ - itype.cs_in = "ref %s"; \ - itype.cs_out = "return (%1)%0;"; \ - itype.im_type_out = "object"; \ - builtin_types.insert(itype.cname, itype); \ +#define INSERT_STRUCT_TYPE(m_type, m_type_in) \ + { \ + itype = TypeInterface::create_value_type(String(#m_type)); \ + itype.c_in = "\t%0 %1_in = MARSHALLED_IN(" #m_type ", %1);\n"; \ + itype.c_out = "\treturn MARSHALLED_OUT(" #m_type ", %1);\n"; \ + itype.c_arg_in = "&%s_in"; \ + itype.c_type_in = "GDMonoMarshal::M_" #m_type "*"; \ + itype.c_type_out = "GDMonoMarshal::M_" #m_type; \ + itype.cs_in = "ref %s"; \ + itype.cs_out = "return (%1)%0;"; \ + itype.im_type_out = itype.cs_type; \ + builtin_types.insert(itype.cname, itype); \ } INSERT_STRUCT_TYPE(Vector2, "real_t*") @@ -2195,26 +2044,31 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { // bool itype = TypeInterface::create_value_type(String("bool")); - itype.c_arg_in = "&%s"; - // /* MonoBoolean <---> bool - itype.c_in = "\t%0 %1_in = (%0)%1;\n"; - itype.c_out = "\treturn (%0)%1;\n"; - itype.c_type = "bool"; - // */ - itype.c_type_in = "MonoBoolean"; - itype.c_type_out = itype.c_type_in; + + { + // MonoBoolean <---> bool + itype.c_in = "\t%0 %1_in = (%0)%1;\n"; + itype.c_out = "\treturn (%0)%1;\n"; + itype.c_type = "bool"; + itype.c_type_in = "MonoBoolean"; + itype.c_type_out = itype.c_type_in; + itype.c_arg_in = "&%s_in"; + } itype.im_type_in = itype.name; itype.im_type_out = itype.name; builtin_types.insert(itype.cname, itype); // int + // C interface is the same as that of enums. Remember to apply any + // changes done here to TypeInterface::postsetup_enum_type as well itype = TypeInterface::create_value_type(String("int")); itype.c_arg_in = "&%s_in"; - // /* ptrcall only supports int64_t and uint64_t - itype.c_in = "\t%0 %1_in = (%0)%1;\n"; - itype.c_out = "\treturn (%0)%1;\n"; - itype.c_type = "int64_t"; - // */ + { + // The expected types for parameters and return value in ptrcall are 'int64_t' or 'uint64_t'. + itype.c_in = "\t%0 %1_in = (%0)%1;\n"; + itype.c_out = "\treturn (%0)%1;\n"; + itype.c_type = "int64_t"; + } itype.c_type_in = "int32_t"; itype.c_type_out = itype.c_type_in; itype.im_type_in = itype.name; @@ -2223,21 +2077,22 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { // real_t itype = TypeInterface(); + itype.name = "float"; // The name is always "float" in Variant, even with REAL_T_IS_DOUBLE. + itype.cname = itype.name; #ifdef REAL_T_IS_DOUBLE - itype.name = "double"; + itype.proxy_name = "double"; #else - itype.name = "float"; + itype.proxy_name = "float"; #endif - itype.cname = itype.name; - itype.proxy_name = itype.name; - itype.c_arg_in = "&%s_in"; - //* ptrcall only supports double - itype.c_in = "\t%0 %1_in = (%0)%1;\n"; - itype.c_out = "\treturn (%0)%1;\n"; - itype.c_type = "double"; - //*/ - itype.c_type_in = "real_t"; - itype.c_type_out = "real_t"; + { + // The expected type for parameters and return value in ptrcall is 'double'. + itype.c_in = "\t%0 %1_in = (%0)%1;\n"; + itype.c_out = "\treturn (%0)%1;\n"; + itype.c_type = "double"; + itype.c_type_in = "real_t"; + itype.c_type_out = "real_t"; + itype.c_arg_in = "&%s_in"; + } itype.cs_type = itype.proxy_name; itype.im_type_in = itype.proxy_name; itype.im_type_out = itype.proxy_name; @@ -2273,13 +2128,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.cs_out = "return new %1(%0);"; itype.im_type_in = "IntPtr"; itype.im_type_out = "IntPtr"; - _populate_builtin_type(itype, Variant::NODE_PATH); - extra_members.insert(itype.cname, MEMBER_BEGIN "public NodePath() : this(string.Empty) {}\n" MEMBER_BEGIN "public NodePath(string path)\n" OPEN_BLOCK_L2 - "this." BINDINGS_PTR_FIELD " = NativeCalls.godot_icall_NodePath_Ctor(path);\n" CLOSE_BLOCK_L2 - MEMBER_BEGIN "public static implicit operator NodePath(string from)\n" OPEN_BLOCK_L2 "return new NodePath(from);\n" CLOSE_BLOCK_L2 - MEMBER_BEGIN "public static implicit operator string(NodePath from)\n" OPEN_BLOCK_L2 - "return NativeCalls." ICALL_PREFIX "NodePath_operator_String(NodePath." CS_SMETHOD_GETINSTANCE "(from));\n" CLOSE_BLOCK_L2 - MEMBER_BEGIN "public override string ToString()\n" OPEN_BLOCK_L2 "return (string)this;\n" CLOSE_BLOCK_L2); builtin_types.insert(itype.cname, itype); // RID @@ -2296,9 +2144,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.cs_out = "return new %1(%0);"; itype.im_type_in = "IntPtr"; itype.im_type_out = "IntPtr"; - _populate_builtin_type(itype, Variant::_RID); - extra_members.insert(itype.cname, MEMBER_BEGIN "internal RID()\n" OPEN_BLOCK_L2 - "this." BINDINGS_PTR_FIELD " = IntPtr.Zero;\n" CLOSE_BLOCK_L2); builtin_types.insert(itype.cname, itype); // Variant @@ -2371,14 +2216,14 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype = TypeInterface(); itype.name = "Array"; itype.cname = itype.name; - itype.proxy_name = "Array"; + itype.proxy_name = itype.name; 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_type = BINDINGS_NAMESPACE_COLLECTIONS "." + itype.proxy_name; itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()"; - itype.cs_out = "return new Array(%0);"; + itype.cs_out = "return new " + itype.cs_type + "(%0);"; itype.im_type_in = "IntPtr"; itype.im_type_out = "IntPtr"; builtin_types.insert(itype.cname, itype); @@ -2387,14 +2232,14 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype = TypeInterface(); itype.name = "Dictionary"; itype.cname = itype.name; - itype.proxy_name = "Dictionary"; + itype.proxy_name = itype.name; itype.c_out = "\treturn memnew(Dictionary(%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_type = BINDINGS_NAMESPACE_COLLECTIONS "." + itype.proxy_name; itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()"; - itype.cs_out = "return new Dictionary(%0);"; + itype.cs_out = "return new " + itype.cs_type + "(%0);"; itype.im_type_in = "IntPtr"; itype.im_type_out = "IntPtr"; builtin_types.insert(itype.cname, itype); @@ -2413,66 +2258,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { builtin_types.insert(itype.cname, itype); } -void BindingsGenerator::_populate_builtin_type(TypeInterface &r_itype, Variant::Type vtype) { - - Variant::CallError cerror; - Variant v = Variant::construct(vtype, NULL, 0, cerror); - - List<MethodInfo> method_list; - v.get_method_list(&method_list); - method_list.sort(); - - for (List<MethodInfo>::Element *E = method_list.front(); E; E = E->next()) { - MethodInfo &mi = E->get(); - MethodInterface imethod; - - imethod.name = mi.name; - imethod.cname = imethod.name; - imethod.proxy_name = escape_csharp_keyword(snake_to_pascal_case(mi.name)); - - for (int i = 0; i < mi.arguments.size(); i++) { - ArgumentInterface iarg; - PropertyInfo pi = mi.arguments[i]; - - iarg.name = pi.name; - - if (pi.type == Variant::NIL) - iarg.type.cname = name_cache.type_Variant; - else - iarg.type.cname = Variant::get_type_name(pi.type); - - if (!r_itype.requires_collections && iarg.type.cname == name_cache.type_Dictionary) - r_itype.requires_collections = true; - - if ((mi.default_arguments.size() - mi.arguments.size() + i) >= 0) - _default_argument_from_variant(Variant::construct(pi.type, NULL, 0, cerror), iarg); - - imethod.add_argument(iarg); - } - - if (mi.return_val.type == Variant::NIL) { - if (mi.return_val.name != "") - imethod.return_type.cname = name_cache.type_Variant; - } else { - imethod.return_type.cname = Variant::get_type_name(mi.return_val.type); - } - - if (!r_itype.requires_collections && imethod.return_type.cname == name_cache.type_Dictionary) - r_itype.requires_collections = true; - - if (r_itype.class_doc) { - for (int i = 0; i < r_itype.class_doc->methods.size(); i++) { - if (r_itype.class_doc->methods[i].name == imethod.name) { - imethod.method_doc = &r_itype.class_doc->methods[i]; - break; - } - } - } - - r_itype.methods.push_back(imethod); - } -} - void BindingsGenerator::_populate_global_constants() { int global_constants_count = GlobalConstants::get_global_constant_count(); @@ -2490,8 +2275,8 @@ void BindingsGenerator::_populate_global_constants() { String constant_name = GlobalConstants::get_global_constant_name(i); const DocData::ConstantDoc *const_doc = NULL; - for (int i = 0; i < global_scope_doc.constants.size(); i++) { - const DocData::ConstantDoc &curr_const_doc = global_scope_doc.constants[i]; + for (int j = 0; j < global_scope_doc.constants.size(); j++) { + const DocData::ConstantDoc &curr_const_doc = global_scope_doc.constants[j]; if (curr_const_doc.name == constant_name) { const_doc = &curr_const_doc; @@ -2502,7 +2287,7 @@ void BindingsGenerator::_populate_global_constants() { int constant_value = GlobalConstants::get_global_constant_value(i); StringName enum_name = GlobalConstants::get_global_constant_enum(i); - ConstantInterface iconstant(snake_to_pascal_case(constant_name, true), constant_value); + ConstantInterface iconstant(constant_name, snake_to_pascal_case(constant_name, true), constant_value); iconstant.const_doc = const_doc; if (enum_name != StringName()) { @@ -2530,16 +2315,18 @@ void BindingsGenerator::_populate_global_constants() { TypeInterface::postsetup_enum_type(enum_itype); enum_types.insert(enum_itype.cname, enum_itype); - ienum.prefix = _determine_enum_prefix(ienum); + int prefix_length = _determine_enum_prefix(ienum); - // HARDCODED + // HARDCODED: The Error enum have the prefix 'ERR_' for everything except 'OK' and 'FAILED'. if (ienum.cname == name_cache.enum_Error) { - if (!ienum.prefix.empty()) { // Just in case it ever changes + if (prefix_length > 0) { // Just in case it ever changes ERR_PRINTS("Prefix for enum 'Error' is not empty"); } - ienum.prefix = "Err"; + prefix_length = 1; // 'ERR_' } + + _apply_prefix_to_enum_constants(ienum, prefix_length); } } @@ -2570,15 +2357,13 @@ void BindingsGenerator::initialize() { _populate_global_constants(); - // Populate internal calls (after populating type interfaces and global constants) + // Generate internal calls (after populating type interfaces and global constants) - _generate_header_icalls(); + core_custom_icalls.clear(); + editor_custom_icalls.clear(); for (OrderedHashMap<StringName, TypeInterface>::Element E = obj_types.front(); E; E = E.next()) _generate_method_icalls(E.get()); - - _generate_method_icalls(builtin_types["NodePath"]); - _generate_method_icalls(builtin_types["RID"]); } void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args) { diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h index 5b33a0e53f..38cf99c294 100644 --- a/modules/mono/editor/bindings_generator.h +++ b/modules/mono/editor/bindings_generator.h @@ -31,32 +31,33 @@ #ifndef BINDINGS_GENERATOR_H #define BINDINGS_GENERATOR_H -#include "class_db.h" +#include "core/class_db.h" #include "editor/doc/doc_data.h" #include "editor/editor_help.h" #ifdef DEBUG_METHODS_ENABLED -#include "ustring.h" +#include "core/ustring.h" class BindingsGenerator { struct ConstantInterface { String name; + String proxy_name; int value; const DocData::ConstantDoc *const_doc; ConstantInterface() {} - ConstantInterface(const String &p_name, int p_value) { + ConstantInterface(const String &p_name, const String &p_proxy_name, int p_value) { name = p_name; + proxy_name = p_proxy_name; value = p_value; } }; struct EnumInterface { StringName cname; - String prefix; List<ConstantInterface> constants; _FORCE_INLINE_ bool operator==(const EnumInterface &p_ienum) const { @@ -192,7 +193,7 @@ class BindingsGenerator { /** * Used only by Object-derived types. - * Determines if this type is not virtual (incomplete). + * Determines if this type is not abstract (incomplete). * e.g.: CanvasItem cannot be instantiated. */ bool is_instantiable; @@ -204,12 +205,6 @@ class BindingsGenerator { */ bool memory_own; - /** - * Determines if the file must have a using directive for System.Collections.Generic - * e.g.: When the generated class makes use of Dictionary - */ - bool requires_collections; - // !! The comments of the following fields make reference to other fields via square brackets, e.g.: [field_name] // !! When renaming those fields, make sure to rename their references in the comments @@ -229,7 +224,7 @@ class BindingsGenerator { String c_in; /** - * Determines the name of the variable that will be passed as argument to a ptrcall. + * Determines the expression that will be passed as argument to ptrcall. * By default the value equals the name of the parameter, * this varies for types that require special manipulation via [c_in]. * Formatting elements: @@ -295,7 +290,7 @@ class BindingsGenerator { /** * Type used for method signatures, both for parameters and the return type. - * Same as [proxy_name] except for variable arguments (VarArg). + * Same as [proxy_name] except for variable arguments (VarArg) and collections (which include the namespace). */ String cs_type; @@ -339,8 +334,6 @@ class BindingsGenerator { itype.proxy_name = itype.name; itype.c_type = itype.name; - itype.c_type_in = "void*"; - itype.c_type_out = "MonoObject*"; itype.cs_type = itype.proxy_name; itype.im_type_in = "ref " + itype.proxy_name; itype.im_type_out = itype.proxy_name; @@ -391,10 +384,19 @@ class BindingsGenerator { } static void postsetup_enum_type(TypeInterface &r_enum_itype) { - r_enum_itype.c_arg_in = "&%s"; - r_enum_itype.c_type = "int"; - r_enum_itype.c_type_in = "int"; - r_enum_itype.c_type_out = "int"; + // C interface is the same as that of 'int'. Remember to apply any + // changes done here to the 'int' type interface as well + + r_enum_itype.c_arg_in = "&%s_in"; + { + // The expected types for parameters and return value in ptrcall are 'int64_t' or 'uint64_t'. + r_enum_itype.c_in = "\t%0 %1_in = (%0)%1;\n"; + r_enum_itype.c_out = "\treturn (%0)%1;\n"; + r_enum_itype.c_type = "int64_t"; + } + r_enum_itype.c_type_in = "int32_t"; + r_enum_itype.c_type_out = r_enum_itype.c_type_in; + r_enum_itype.cs_type = r_enum_itype.proxy_name; r_enum_itype.cs_in = "(int)%s"; r_enum_itype.cs_out = "return (%1)%0;"; @@ -414,7 +416,6 @@ class BindingsGenerator { is_instantiable = false; memory_own = false; - requires_collections = false; c_arg_in = "%s"; @@ -463,10 +464,7 @@ class BindingsGenerator { List<EnumInterface> global_enums; List<ConstantInterface> global_constants; - Map<StringName, String> extra_members; - List<InternalCall> method_icalls; - List<InternalCall> builtin_method_icalls; Map<const MethodInterface *, const InternalCall *> method_icalls_map; List<const InternalCall *> generated_icall_funcs; @@ -523,16 +521,15 @@ class BindingsGenerator { return p_type.name; } - String _determine_enum_prefix(const EnumInterface &p_ienum); + int _determine_enum_prefix(const EnumInterface &p_ienum); + void _apply_prefix_to_enum_constants(EnumInterface &p_ienum, int p_prefix_length); - void _generate_header_icalls(); void _generate_method_icalls(const TypeInterface &p_itype); const TypeInterface *_get_type_or_null(const TypeReference &p_typeref); const TypeInterface *_get_type_or_placeholder(const TypeReference &p_typeref); void _default_argument_from_variant(const Variant &p_val, ArgumentInterface &r_iarg); - void _populate_builtin_type(TypeInterface &r_itype, Variant::Type vtype); void _populate_object_type_interfaces(); void _populate_builtin_type_interfaces(); @@ -564,7 +561,6 @@ public: Error generate_glue(const String &p_output_dir); static uint32_t get_version(); - static uint32_t get_cs_glue_version(); void initialize(); diff --git a/modules/mono/editor/csharp_project.cpp b/modules/mono/editor/csharp_project.cpp index bc95607743..ab96356d6d 100644 --- a/modules/mono/editor/csharp_project.cpp +++ b/modules/mono/editor/csharp_project.cpp @@ -30,11 +30,15 @@ #include "csharp_project.h" -#include "os/os.h" -#include "project_settings.h" +#include "core/io/json.h" +#include "core/os/os.h" +#include "core/project_settings.h" +#include "../csharp_script.h" #include "../mono_gd/gd_mono_class.h" #include "../mono_gd/gd_mono_marshal.h" +#include "../utils/string_utils.h" +#include "script_class_parser.h" namespace CSharpProject { @@ -51,7 +55,7 @@ String generate_core_api_project(const String &p_dir, const Vector<String> &p_fi MonoObject *ret = klass->get_method("GenCoreApiProject", 2)->invoke(NULL, args, &exc); if (exc) { - GDMonoUtils::debug_unhandled_exception(exc); + GDMonoUtils::debug_print_unhandled_exception(exc); ERR_FAIL_V(String()); } @@ -72,7 +76,7 @@ String generate_editor_api_project(const String &p_dir, const String &p_core_dll MonoObject *ret = klass->get_method("GenEditorApiProject", 3)->invoke(NULL, args, &exc); if (exc) { - GDMonoUtils::debug_unhandled_exception(exc); + GDMonoUtils::debug_print_unhandled_exception(exc); ERR_FAIL_V(String()); } @@ -93,7 +97,7 @@ String generate_game_project(const String &p_dir, const String &p_name, const Ve MonoObject *ret = klass->get_method("GenGameProject", 3)->invoke(NULL, args, &exc); if (exc) { - GDMonoUtils::debug_unhandled_exception(exc); + GDMonoUtils::debug_print_unhandled_exception(exc); ERR_FAIL_V(String()); } @@ -114,8 +118,114 @@ void add_item(const String &p_project_path, const String &p_item_type, const Str klass->get_method("AddItemToProjectChecked", 3)->invoke(NULL, args, &exc); if (exc) { - GDMonoUtils::debug_unhandled_exception(exc); + GDMonoUtils::debug_print_unhandled_exception(exc); ERR_FAIL(); } } + +Error generate_scripts_metadata(const String &p_project_path, const String &p_output_path) { + + _GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN) + + GDMonoClass *project_utils = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectUtils"); + + void *args[2] = { + GDMonoMarshal::mono_string_from_godot(p_project_path), + GDMonoMarshal::mono_string_from_godot("Compile") + }; + + MonoException *exc = NULL; + MonoArray *ret = (MonoArray *)project_utils->get_method("GetIncludeFiles", 2)->invoke_raw(NULL, args, &exc); + + if (exc) { + GDMonoUtils::debug_print_unhandled_exception(exc); + ERR_FAIL_V(FAILED); + } + + PoolStringArray project_files = GDMonoMarshal::mono_array_to_PoolStringArray(ret); + PoolStringArray::Read r = project_files.read(); + + Dictionary old_dict = CSharpLanguage::get_singleton()->get_scripts_metadata(); + Dictionary new_dict; + + for (int i = 0; i < project_files.size(); i++) { + const String &project_file = ("res://" + r[i]).simplify_path(); + + uint64_t modified_time = FileAccess::get_modified_time(project_file); + + const Variant *old_file_var = old_dict.getptr(project_file); + if (old_file_var) { + Dictionary old_file_dict = old_file_var->operator Dictionary(); + + if (old_file_dict["modified_time"].operator uint64_t() == modified_time) { + // No changes so no need to parse again + new_dict[project_file] = old_file_dict; + continue; + } + } + + ScriptClassParser scp; + Error err = scp.parse_file(project_file); + if (err != OK) { + ERR_PRINTS("Parse error: " + scp.get_error()); + ERR_EXPLAIN("Failed to determine namespace and class for script: " + project_file); + ERR_FAIL_V(err); + } + + Vector<ScriptClassParser::ClassDecl> classes = scp.get_classes(); + + bool found = false; + Dictionary class_dict; + + String search_name = project_file.get_file().get_basename(); + + for (int j = 0; j < classes.size(); j++) { + const ScriptClassParser::ClassDecl &class_decl = classes[j]; + + if (class_decl.base.size() == 0) + continue; // Does not inherit nor implement anything, so it can't be a script class + + String class_cmp; + + if (class_decl.nested) { + class_cmp = class_decl.name.get_slice(".", class_decl.name.get_slice_count(".") - 1); + } else { + class_cmp = class_decl.name; + } + + if (class_cmp != search_name) + continue; + + class_dict["namespace"] = class_decl.namespace_; + class_dict["class_name"] = class_decl.name; + class_dict["nested"] = class_decl.nested; + + found = true; + break; + } + + if (found) { + Dictionary file_dict; + file_dict["modified_time"] = modified_time; + file_dict["class"] = class_dict; + new_dict[project_file] = file_dict; + } + } + + if (new_dict.size()) { + String json = JSON::print(new_dict, "", false); + + Error ferr; + FileAccess *f = FileAccess::open(p_output_path, FileAccess::WRITE, &ferr); + ERR_EXPLAIN("Cannot open file for writing: " + p_output_path); + ERR_FAIL_COND_V(ferr != OK, ferr); + f->store_string(json); + f->flush(); + f->close(); + memdelete(f); + } + + return OK; +} + } // namespace CSharpProject diff --git a/modules/mono/editor/csharp_project.h b/modules/mono/editor/csharp_project.h index 381dd17e02..aeeeff50f0 100644 --- a/modules/mono/editor/csharp_project.h +++ b/modules/mono/editor/csharp_project.h @@ -31,7 +31,7 @@ #ifndef CSHARP_PROJECT_H #define CSHARP_PROJECT_H -#include "ustring.h" +#include "core/ustring.h" namespace CSharpProject { @@ -40,6 +40,9 @@ String generate_editor_api_project(const String &p_dir, const String &p_core_dll String generate_game_project(const String &p_dir, const String &p_name, const Vector<String> &p_files = Vector<String>()); void add_item(const String &p_project_path, const String &p_item_type, const String &p_include); + +Error generate_scripts_metadata(const String &p_project_path, const String &p_output_path); + } // namespace CSharpProject #endif // CSHARP_PROJECT_H diff --git a/modules/mono/editor/godotsharp_builds.cpp b/modules/mono/editor/godotsharp_builds.cpp index 2faab1718d..6216213716 100644 --- a/modules/mono/editor/godotsharp_builds.cpp +++ b/modules/mono/editor/godotsharp_builds.cpp @@ -30,13 +30,16 @@ #include "godotsharp_builds.h" +#include "core/vector.h" #include "main/main.h" +#include "../glue/cs_glue_version.gen.h" #include "../godotsharp_dirs.h" #include "../mono_gd/gd_mono_class.h" #include "../mono_gd/gd_mono_marshal.h" #include "../utils/path_utils.h" #include "bindings_generator.h" +#include "csharp_project.h" #include "godotsharp_editor.h" #define PROP_NAME_MSBUILD_MONO "MSBuild (Mono)" @@ -50,6 +53,16 @@ void godot_icall_BuildInstance_ExitCallback(MonoString *p_solution, MonoString * GodotSharpBuilds::get_singleton()->build_exit_callback(MonoBuildInfo(solution, config), p_exit_code); } +static Vector<const char *> _get_msbuild_hint_dirs() { + Vector<const char *> ret; +#ifdef OSX_ENABLED + ret.push_back("/Library/Frameworks/Mono.framework/Versions/Current/bin/"); + ret.push_back("/usr/local/var/homebrew/linked/mono/bin/"); +#endif + ret.push_back("/opt/novell/mono/bin/"); + return ret; +} + #ifdef UNIX_ENABLED String _find_build_engine_on_unix(const String &p_name) { String ret = path_which(p_name); @@ -61,15 +74,9 @@ String _find_build_engine_on_unix(const String &p_name) { if (ret_fallback.length()) return ret_fallback; - const char *locations[] = { -#ifdef OSX_ENABLED - "/Library/Frameworks/Mono.framework/Versions/Current/bin/", - "/usr/local/var/homebrew/linked/mono/bin/", -#endif - "/opt/novell/mono/bin/" - }; + static Vector<const char *> locations = _get_msbuild_hint_dirs(); - for (int i = 0; i < sizeof(locations) / sizeof(const char *); i++) { + for (int i = 0; i < locations.size(); i++) { String hint_path = locations[i] + p_name; if (FileAccess::exists(hint_path)) { @@ -88,7 +95,12 @@ MonoString *godot_icall_BuildInstance_get_MSBuildPath() { #if defined(WINDOWS_ENABLED) switch (build_tool) { case GodotSharpBuilds::MSBUILD_VS: { - static String msbuild_tools_path = MonoRegUtils::find_msbuild_tools_path(); + static String msbuild_tools_path; + + if (msbuild_tools_path.empty() || !FileAccess::exists(msbuild_tools_path)) { + // Try to search it again if it wasn't found last time or if it was removed from its location + msbuild_tools_path = MonoRegUtils::find_msbuild_tools_path(); + } if (msbuild_tools_path.length()) { if (!msbuild_tools_path.ends_with("\\")) @@ -122,15 +134,25 @@ MonoString *godot_icall_BuildInstance_get_MSBuildPath() { CRASH_NOW(); } #elif defined(UNIX_ENABLED) - static String msbuild_path = _find_build_engine_on_unix("msbuild"); - static String xbuild_path = _find_build_engine_on_unix("xbuild"); + static String msbuild_path; + static String xbuild_path; if (build_tool == GodotSharpBuilds::XBUILD) { + if (xbuild_path.empty() || !FileAccess::exists(xbuild_path)) { + // Try to search it again if it wasn't found last time or if it was removed from its location + xbuild_path = _find_build_engine_on_unix("msbuild"); + } + if (xbuild_path.empty()) { WARN_PRINT("Cannot find binary for '" PROP_NAME_XBUILD "'"); return NULL; } } else { + if (msbuild_path.empty() || !FileAccess::exists(msbuild_path)) { + // Try to search it again if it wasn't found last time or if it was removed from its location + msbuild_path = _find_build_engine_on_unix("msbuild"); + } + if (msbuild_path.empty()) { WARN_PRINT("Cannot find binary for '" PROP_NAME_MSBUILD_MONO "'"); return NULL; @@ -186,7 +208,11 @@ MonoBoolean godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows() { #endif } -void GodotSharpBuilds::_register_internal_calls() { +void GodotSharpBuilds::register_internal_calls() { + + static bool registered = false; + ERR_FAIL_COND(registered); + registered = true; mono_add_internal_call("GodotSharpTools.Build.BuildSystem::godot_icall_BuildInstance_ExitCallback", (void *)godot_icall_BuildInstance_ExitCallback); mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_MSBuildPath", (void *)godot_icall_BuildInstance_get_MSBuildPath); @@ -224,6 +250,18 @@ bool GodotSharpBuilds::build_api_sln(const String &p_name, const String &p_api_s bool GodotSharpBuilds::copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name, APIAssembly::Type p_api_type) { + // Create destination directory if needed + if (!DirAccess::exists(p_dst_dir)) { + DirAccess *da = DirAccess::create_for_path(p_dst_dir); + Error err = da->make_dir_recursive(p_dst_dir); + memdelete(da); + + if (err != OK) { + show_build_error_dialog("Failed to create destination directory for the API assemblies. Error: " + itos(err)); + return false; + } + } + String assembly_file = p_assembly_name + ".dll"; String assembly_src = p_src_dir.plus_file(assembly_file); String assembly_dst = p_dst_dir.plus_file(assembly_file); @@ -231,7 +269,7 @@ bool GodotSharpBuilds::copy_api_assembly(const String &p_src_dir, const String & if (!FileAccess::exists(assembly_dst) || FileAccess::get_modified_time(assembly_src) > FileAccess::get_modified_time(assembly_dst) || GDMono::get_singleton()->metadata_is_api_assembly_invalidated(p_api_type)) { - DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); String xml_file = p_assembly_name + ".xml"; if (da->copy(p_src_dir.plus_file(xml_file), p_dst_dir.plus_file(xml_file)) != OK) @@ -243,8 +281,6 @@ bool GodotSharpBuilds::copy_api_assembly(const String &p_src_dir, const String & Error err = da->copy(assembly_src, assembly_dst); - memdelete(da); - if (err != OK) { show_build_error_dialog("Failed to copy " + assembly_file); return false; @@ -263,17 +299,27 @@ String GodotSharpBuilds::_api_folder_name(APIAssembly::Type p_api_type) { GDMono::get_singleton()->get_api_editor_hash(); return String::num_uint64(api_hash) + "_" + String::num_uint64(BindingsGenerator::get_version()) + - "_" + String::num_uint64(BindingsGenerator::get_cs_glue_version()); + "_" + String::num_uint64(CS_GLUE_VERSION); } bool GodotSharpBuilds::make_api_sln(APIAssembly::Type p_api_type) { String api_name = p_api_type == APIAssembly::API_CORE ? API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME; + + String editor_prebuilt_api_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir(); + String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir(); + + if (FileAccess::exists(editor_prebuilt_api_dir.plus_file(api_name + ".dll"))) { + EditorProgress pr("mono_copy_prebuilt_api_assembly", "Copying prebuilt " + api_name + " assembly...", 1); + pr.step("Copying " + api_name + " assembly", 0); + return GodotSharpBuilds::copy_api_assembly(editor_prebuilt_api_dir, res_assemblies_dir, api_name, p_api_type); + } + String api_build_config = "Release"; - EditorProgress pr("mono_build_release_" + api_name, "Building " + api_name + " solution...", 4); + EditorProgress pr("mono_build_release_" + api_name, "Building " + api_name + " solution...", 3); - pr.step("Generating " + api_name + " solution"); + pr.step("Generating " + api_name + " solution", 0); String core_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir() .plus_file(_api_folder_name(APIAssembly::API_CORE)) @@ -311,34 +357,18 @@ bool GodotSharpBuilds::make_api_sln(APIAssembly::Type p_api_type) { } } - pr.step("Building " + api_name + " solution"); + pr.step("Building " + api_name + " solution", 1); if (!GodotSharpBuilds::build_api_sln(api_name, api_sln_dir, api_build_config)) return false; - pr.step("Copying " + api_name + " assembly"); - - String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir(); - - // Create assemblies directory if needed - if (!DirAccess::exists(res_assemblies_dir)) { - DirAccess *da = DirAccess::create_for_path(res_assemblies_dir); - Error err = da->make_dir_recursive(res_assemblies_dir); - memdelete(da); - - if (err != OK) { - show_build_error_dialog("Failed to create assemblies directory. Error: " + itos(err)); - return false; - } - } + pr.step("Copying " + api_name + " assembly", 2); // Copy the built assembly to the assemblies directory String api_assembly_dir = api_sln_dir.plus_file("bin").plus_file(api_build_config); if (!GodotSharpBuilds::copy_api_assembly(api_assembly_dir, res_assemblies_dir, api_name, p_api_type)) return false; - pr.step("Done"); - return true; } @@ -353,9 +383,8 @@ bool GodotSharpBuilds::build_project_blocking(const String &p_config) { if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_EDITOR)) return false; - EditorProgress pr("mono_project_debug_build", "Building project solution...", 2); - - pr.step("Building project solution"); + EditorProgress pr("mono_project_debug_build", "Building project solution...", 1); + pr.step("Building project solution", 0); MonoBuildInfo build_info(GodotSharpDirs::get_project_sln_path(), p_config); if (!GodotSharpBuilds::get_singleton()->build(build_info)) { @@ -363,13 +392,23 @@ bool GodotSharpBuilds::build_project_blocking(const String &p_config) { return false; } - pr.step("Done"); - return true; } bool GodotSharpBuilds::editor_build_callback() { + String scripts_metadata_path_editor = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata.editor"); + String scripts_metadata_path_player = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata.editor_player"); + + Error metadata_err = CSharpProject::generate_scripts_metadata(GodotSharpDirs::get_project_csproj_path(), scripts_metadata_path_editor); + ERR_FAIL_COND_V(metadata_err != OK, false); + + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Error copy_err = da->copy(scripts_metadata_path_editor, scripts_metadata_path_player); + + ERR_EXPLAIN("Failed to copy scripts metadata file"); + ERR_FAIL_COND_V(copy_err != OK, false); + return build_project_blocking("Tools"); } diff --git a/modules/mono/editor/godotsharp_builds.h b/modules/mono/editor/godotsharp_builds.h index 4afc284d45..c6dc6b6236 100644 --- a/modules/mono/editor/godotsharp_builds.h +++ b/modules/mono/editor/godotsharp_builds.h @@ -61,9 +61,6 @@ private: static GodotSharpBuilds *singleton; - friend class GDMono; - static void _register_internal_calls(); - public: enum BuildTool { MSBUILD_MONO, @@ -75,6 +72,8 @@ public: _FORCE_INLINE_ static GodotSharpBuilds *get_singleton() { return singleton; } + static void register_internal_calls(); + static void show_build_error_dialog(const String &p_message); void build_exit_callback(const MonoBuildInfo &p_build_info, int p_exit_code); diff --git a/modules/mono/editor/godotsharp_editor.cpp b/modules/mono/editor/godotsharp_editor.cpp index faeb58e5a7..9df4e10266 100644 --- a/modules/mono/editor/godotsharp_editor.cpp +++ b/modules/mono/editor/godotsharp_editor.cpp @@ -38,12 +38,17 @@ #include "../csharp_script.h" #include "../godotsharp_dirs.h" #include "../mono_gd/gd_mono.h" +#include "../mono_gd/gd_mono_marshal.h" #include "../utils/path_utils.h" #include "bindings_generator.h" #include "csharp_project.h" #include "godotsharp_export.h" #include "net_solution.h" +#ifdef OSX_ENABLED +#include "../utils/osx_utils.h" +#endif + #ifdef WINDOWS_ENABLED #include "../utils/mono_reg_utils.h" #endif @@ -103,6 +108,33 @@ bool GodotSharpEditor::_create_project_solution() { return true; } +void GodotSharpEditor::_make_api_solutions_if_needed() { + // I'm sick entirely of ProgressDialog + static bool recursion_guard = false; + if (!recursion_guard) { + recursion_guard = true; + _make_api_solutions_if_needed_impl(); + recursion_guard = false; + } +} + +void GodotSharpEditor::_make_api_solutions_if_needed_impl() { + // If the project has a solution and C# project make sure the API assemblies are present and up to date + String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir(); + + if (!FileAccess::exists(res_assemblies_dir.plus_file(API_ASSEMBLY_NAME ".dll")) || + GDMono::get_singleton()->metadata_is_api_assembly_invalidated(APIAssembly::API_CORE)) { + if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_CORE)) + return; + } + + if (!FileAccess::exists(res_assemblies_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll")) || + GDMono::get_singleton()->metadata_is_api_assembly_invalidated(APIAssembly::API_EDITOR)) { + if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_EDITOR)) + return; // Redundant? I don't think so + } +} + void GodotSharpEditor::_remove_create_sln_menu_option() { menu_popup->remove_item(menu_popup->get_item_index(MENU_CREATE_SLN)); @@ -164,11 +196,38 @@ void GodotSharpEditor::_notification(int p_notification) { void GodotSharpEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_create_project_solution"), &GodotSharpEditor::_create_project_solution); + ClassDB::bind_method(D_METHOD("_make_api_solutions_if_needed"), &GodotSharpEditor::_make_api_solutions_if_needed); ClassDB::bind_method(D_METHOD("_remove_create_sln_menu_option"), &GodotSharpEditor::_remove_create_sln_menu_option); ClassDB::bind_method(D_METHOD("_toggle_about_dialog_on_start"), &GodotSharpEditor::_toggle_about_dialog_on_start); ClassDB::bind_method(D_METHOD("_menu_option_pressed", "id"), &GodotSharpEditor::_menu_option_pressed); } +MonoBoolean godot_icall_MonoDevelopInstance_IsApplicationBundleInstalled(MonoString *p_bundle_id) { +#ifdef OSX_ENABLED + return (MonoBoolean)osx_is_app_bundle_installed(GDMonoMarshal::mono_string_to_godot(p_bundle_id)); +#else + (void)p_bundle_id; // UNUSED + ERR_FAIL_V(false); +#endif +} + +MonoString *godot_icall_Utils_OS_GetPlatformName() { + return GDMonoMarshal::mono_string_from_godot(OS::get_singleton()->get_name()); +} + +void GodotSharpEditor::register_internal_calls() { + + static bool registered = false; + ERR_FAIL_COND(registered); + registered = true; + + mono_add_internal_call("GodotSharpTools.Editor.MonoDevelopInstance::IsApplicationBundleInstalled", (void *)godot_icall_MonoDevelopInstance_IsApplicationBundleInstalled); + mono_add_internal_call("GodotSharpTools.Utils.OS::GetPlatformName", (void *)godot_icall_Utils_OS_GetPlatformName); + + GodotSharpBuilds::register_internal_calls(); + GodotSharpExport::register_internal_calls(); +} + void GodotSharpEditor::show_error_dialog(const String &p_message, const String &p_title) { error_dialog->set_title(p_title); @@ -181,8 +240,36 @@ Error GodotSharpEditor::open_in_external_editor(const Ref<Script> &p_script, int ExternalEditor editor = ExternalEditor(int(EditorSettings::get_singleton()->get("mono/editor/external_editor"))); switch (editor) { - case EDITOR_CODE: { + case EDITOR_VSCODE: { + static String vscode_path; + + if (vscode_path.empty() || !FileAccess::exists(vscode_path)) { + // Try to search it again if it wasn't found last time or if it was removed from its location + vscode_path = path_which("code"); + } + List<String> args; + +#ifdef OSX_ENABLED + // The package path is '/Applications/Visual Studio Code.app' + static const String vscode_bundle_id = "com.microsoft.VSCode"; + static bool osx_app_bundle_installed = osx_is_app_bundle_installed(vscode_bundle_id); + + if (osx_app_bundle_installed) { + args.push_back("-b"); + args.push_back(vscode_bundle_id); + + // The reusing of existing windows made by the 'open' command might not choose a wubdiw that is + // editing our folder. It's better to ask for a new window and let VSCode do the window management. + args.push_back("-n"); + + // The open process must wait until the application finishes (which is instant in VSCode's case) + args.push_back("--wait-apps"); + + args.push_back("--args"); + } +#endif + args.push_back(ProjectSettings::get_singleton()->get_resource_path()); String script_path = ProjectSettings::get_singleton()->globalize_path(p_script->get_path()); @@ -194,18 +281,47 @@ Error GodotSharpEditor::open_in_external_editor(const Ref<Script> &p_script, int args.push_back(script_path); } - static String program = path_which("code"); +#ifdef OSX_ENABLED + ERR_EXPLAIN("Cannot find code editor: VSCode"); + ERR_FAIL_COND_V(!osx_app_bundle_installed && vscode_path.empty(), ERR_FILE_NOT_FOUND); + + String command = osx_app_bundle_installed ? "/usr/bin/open" : vscode_path; +#else + ERR_EXPLAIN("Cannot find code editor: VSCode"); + ERR_FAIL_COND_V(vscode_path.empty(), ERR_FILE_NOT_FOUND); - Error err = OS::get_singleton()->execute(program.length() ? program : "code", args, false); + String command = vscode_path; +#endif + + Error err = OS::get_singleton()->execute(command, args, false); if (err != OK) { - ERR_PRINT("GodotSharp: Could not execute external editor"); + ERR_PRINT("Error when trying to execute code editor: VSCode"); return err; } } break; +#ifdef OSX_ENABLED + case EDITOR_VISUALSTUDIO_MAC: + // [[fallthrough]]; +#endif case EDITOR_MONODEVELOP: { - if (!monodevel_instance) - monodevel_instance = memnew(MonoDevelopInstance(GodotSharpDirs::get_project_sln_path())); +#ifdef OSX_ENABLED + bool is_visualstudio = editor == EDITOR_VISUALSTUDIO_MAC; + + MonoDevelopInstance **instance = is_visualstudio ? + &visualstudio_mac_instance : + &monodevelop_instance; + + MonoDevelopInstance::EditorId editor_id = is_visualstudio ? + MonoDevelopInstance::VISUALSTUDIO_FOR_MAC : + MonoDevelopInstance::MONODEVELOP; +#else + MonoDevelopInstance **instance = &monodevelop_instance; + MonoDevelopInstance::EditorId editor_id = MonoDevelopInstance::MONODEVELOP; +#endif + + if (!*instance) + *instance = memnew(MonoDevelopInstance(GodotSharpDirs::get_project_sln_path(), editor_id)); String script_path = ProjectSettings::get_singleton()->globalize_path(p_script->get_path()); @@ -213,7 +329,7 @@ Error GodotSharpEditor::open_in_external_editor(const Ref<Script> &p_script, int script_path += ";" + itos(p_line + 1) + ";" + itos(p_col); } - monodevel_instance->execute(script_path); + (*instance)->execute(script_path); } break; default: return ERR_UNAVAILABLE; @@ -231,7 +347,10 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) { singleton = this; - monodevel_instance = NULL; + monodevelop_instance = NULL; +#ifdef OSX_ENABLED + visualstudio_mac_instance = NULL; +#endif editor = p_editor; @@ -299,7 +418,10 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) { String sln_path = GodotSharpDirs::get_project_sln_path(); String csproj_path = GodotSharpDirs::get_project_csproj_path(); - if (!FileAccess::exists(sln_path) || !FileAccess::exists(csproj_path)) { + if (FileAccess::exists(sln_path) && FileAccess::exists(csproj_path)) { + // We can't use EditorProgress here. It calls Main::iterarion() and the main loop is not initialized yet. + call_deferred("_make_api_solutions_if_needed"); + } else { bottom_panel_btn->hide(); menu_popup->add_item(TTR("Create C# solution"), MENU_CREATE_SLN); } @@ -314,7 +436,18 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) { // External editor settings EditorSettings *ed_settings = EditorSettings::get_singleton(); EDITOR_DEF("mono/editor/external_editor", EDITOR_NONE); - ed_settings->add_property_hint(PropertyInfo(Variant::INT, "mono/editor/external_editor", PROPERTY_HINT_ENUM, "None,MonoDevelop,Visual Studio Code")); + + String settings_hint_str = "None"; + +#ifdef WINDOWS_ENABLED + settings_hint_str += ",MonoDevelop,Visual Studio Code"; +#elif OSX_ENABLED + settings_hint_str += ",Visual Studio,MonoDevelop,Visual Studio Code"; +#elif UNIX_ENABLED + settings_hint_str += ",MonoDevelop,Visual Studio Code"; +#endif + + ed_settings->add_property_hint(PropertyInfo(Variant::INT, "mono/editor/external_editor", PROPERTY_HINT_ENUM, settings_hint_str)); // Export plugin Ref<GodotSharpExport> godotsharp_export; @@ -328,9 +461,9 @@ GodotSharpEditor::~GodotSharpEditor() { memdelete(godotsharp_builds); - if (monodevel_instance) { - memdelete(monodevel_instance); - monodevel_instance = NULL; + if (monodevelop_instance) { + memdelete(monodevelop_instance); + monodevelop_instance = NULL; } } diff --git a/modules/mono/editor/godotsharp_editor.h b/modules/mono/editor/godotsharp_editor.h index 66da814c8b..9fb0e40132 100644 --- a/modules/mono/editor/godotsharp_editor.h +++ b/modules/mono/editor/godotsharp_editor.h @@ -50,9 +50,14 @@ class GodotSharpEditor : public Node { GodotSharpBuilds *godotsharp_builds; - MonoDevelopInstance *monodevel_instance; + MonoDevelopInstance *monodevelop_instance; +#ifdef OSX_ENABLED + MonoDevelopInstance *visualstudio_mac_instance; +#endif bool _create_project_solution(); + void _make_api_solutions_if_needed(); + void _make_api_solutions_if_needed_impl(); void _remove_create_sln_menu_option(); void _show_about_dialog(); @@ -74,12 +79,24 @@ public: enum ExternalEditor { EDITOR_NONE, +#ifdef WINDOWS_ENABLED + //EDITOR_VISUALSTUDIO, // TODO EDITOR_MONODEVELOP, - EDITOR_CODE, + EDITOR_VSCODE +#elif OSX_ENABLED + EDITOR_VISUALSTUDIO_MAC, + EDITOR_MONODEVELOP, + EDITOR_VSCODE +#elif UNIX_ENABLED + EDITOR_MONODEVELOP, + EDITOR_VSCODE +#endif }; _FORCE_INLINE_ static GodotSharpEditor *get_singleton() { return singleton; } + static void register_internal_calls(); + void show_error_dialog(const String &p_message, const String &p_title = "Error"); Error open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col); diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp index cd09e6516a..509ec961fb 100644 --- a/modules/mono/editor/godotsharp_export.cpp +++ b/modules/mono/editor/godotsharp_export.cpp @@ -30,12 +30,38 @@ #include "godotsharp_export.h" +#include "core/version.h" + #include "../csharp_script.h" #include "../godotsharp_defs.h" #include "../godotsharp_dirs.h" +#include "../mono_gd/gd_mono_class.h" +#include "../mono_gd/gd_mono_marshal.h" +#include "csharp_project.h" #include "godotsharp_builds.h" -void GodotSharpExport::_export_file(const String &p_path, const String &p_type, const Set<String> &p_features) { +static MonoString *godot_icall_GodotSharpExport_GetTemplatesDir() { + String current_version = VERSION_FULL_CONFIG; + String templates_dir = EditorSettings::get_singleton()->get_templates_dir().plus_file(current_version); + return GDMonoMarshal::mono_string_from_godot(ProjectSettings::get_singleton()->globalize_path(templates_dir)); +} + +static MonoString *godot_icall_GodotSharpExport_GetDataDirName() { + String appname = ProjectSettings::get_singleton()->get("application/config/name"); + String appname_safe = OS::get_singleton()->get_safe_dir_name(appname); + return GDMonoMarshal::mono_string_from_godot("data_" + appname_safe); +} + +void GodotSharpExport::register_internal_calls() { + static bool registered = false; + ERR_FAIL_COND(registered); + registered = true; + + mono_add_internal_call("GodotSharpTools.Editor.GodotSharpExport::GetTemplatesDir", (void *)godot_icall_GodotSharpExport_GetTemplatesDir); + mono_add_internal_call("GodotSharpTools.Editor.GodotSharpExport::GetDataDirName", (void *)godot_icall_GodotSharpExport_GetDataDirName); +} + +void GodotSharpExport::_export_file(const String &p_path, const String &p_type, const Set<String> &) { if (p_type != CSharpLanguage::get_singleton()->get_type()) return; @@ -56,62 +82,87 @@ void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug // TODO right now there is no way to stop the export process with an error ERR_FAIL_COND(!GDMono::get_singleton()->is_runtime_initialized()); - ERR_FAIL_NULL(GDMono::get_singleton()->get_tools_domain()); + ERR_FAIL_NULL(TOOLS_DOMAIN); + ERR_FAIL_NULL(GDMono::get_singleton()->get_editor_tools_assembly()); String build_config = p_debug ? "Debug" : "Release"; - ERR_FAIL_COND(!GodotSharpBuilds::build_project_blocking(build_config)); + String scripts_metadata_path = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata." + String(p_debug ? "debug" : "release")); + Error metadata_err = CSharpProject::generate_scripts_metadata(GodotSharpDirs::get_project_csproj_path(), scripts_metadata_path); + ERR_FAIL_COND(metadata_err != OK); - // Add API assemblies + ERR_FAIL_COND(!_add_file(scripts_metadata_path, scripts_metadata_path)); - String core_api_dll_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(API_ASSEMBLY_NAME ".dll"); - ERR_FAIL_COND(!_add_assembly(core_api_dll_path, core_api_dll_path)); + ERR_FAIL_COND(!GodotSharpBuilds::build_project_blocking(build_config)); - String editor_api_dll_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"); - ERR_FAIL_COND(!_add_assembly(editor_api_dll_path, editor_api_dll_path)); + // Add dependency assemblies - // Add project assembly + Map<String, String> dependencies; String project_dll_name = ProjectSettings::get_singleton()->get("application/config/name"); if (project_dll_name.empty()) { project_dll_name = "UnnamedProject"; } - String project_dll_src_path = GodotSharpDirs::get_res_temp_assemblies_base_dir().plus_file(build_config).plus_file(project_dll_name + ".dll"); - String project_dll_dst_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(project_dll_name + ".dll"); - ERR_FAIL_COND(!_add_assembly(project_dll_src_path, project_dll_dst_path)); + String project_dll_src_dir = GodotSharpDirs::get_res_temp_assemblies_base_dir().plus_file(build_config); + String project_dll_src_path = project_dll_src_dir.plus_file(project_dll_name + ".dll"); + dependencies.insert(project_dll_name, project_dll_src_path); - // Add dependencies + { + MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.ProjectExportDomain"); + ERR_FAIL_NULL(export_domain); + _GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(export_domain); - MonoDomain *prev_domain = mono_domain_get(); - MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.ProjectExportDomain"); + _GDMONO_SCOPE_DOMAIN_(export_domain); - ERR_FAIL_COND(!export_domain); - ERR_FAIL_COND(!mono_domain_set(export_domain, false)); + GDMonoAssembly *scripts_assembly = NULL; + bool load_success = GDMono::get_singleton()->load_assembly_from(project_dll_name, + project_dll_src_dir, &scripts_assembly, /* refonly: */ true); - Map<String, String> dependencies; - dependencies.insert("mscorlib", GDMono::get_singleton()->get_corlib_assembly()->get_path()); + ERR_EXPLAIN("Cannot load refonly assembly: " + project_dll_name); + ERR_FAIL_COND(!load_success); - GDMonoAssembly *scripts_assembly = GDMonoAssembly::load_from(project_dll_name, project_dll_src_path, /* refonly: */ true); + Vector<String> search_dirs; + GDMonoAssembly::fill_search_dirs(search_dirs); + Error depend_error = _get_assembly_dependencies(scripts_assembly, search_dirs, dependencies); + ERR_FAIL_COND(depend_error != OK); + } + + for (Map<String, String>::Element *E = dependencies.front(); E; E = E->next()) { + String depend_src_path = E->value(); + String depend_dst_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(depend_src_path.get_file()); + ERR_FAIL_COND(!_add_file(depend_src_path, depend_dst_path)); + } - ERR_EXPLAIN("Cannot load refonly assembly: " + project_dll_name); - ERR_FAIL_COND(!scripts_assembly); + // Mono specific export template extras (data dir) - Error depend_error = _get_assembly_dependencies(scripts_assembly, dependencies); + GDMonoClass *export_class = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Editor", "GodotSharpExport"); + ERR_FAIL_NULL(export_class); + GDMonoMethod *export_begin_method = export_class->get_method("_ExportBegin", 4); + ERR_FAIL_NULL(export_begin_method); - GDMono::get_singleton()->finalize_and_unload_domain(export_domain); - mono_domain_set(prev_domain, false); + MonoArray *features = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(String), p_features.size()); + int i = 0; + for (const Set<String>::Element *E = p_features.front(); E; E = E->next()) { + MonoString *boxed = GDMonoMarshal::mono_string_from_godot(E->get()); + mono_array_set(features, MonoString *, i, boxed); + i++; + } - ERR_FAIL_COND(depend_error != OK); + MonoBoolean debug = p_debug; + MonoString *path = GDMonoMarshal::mono_string_from_godot(p_path); + uint32_t flags = p_flags; + void *args[4] = { features, &debug, path, &flags }; + MonoException *exc = NULL; + export_begin_method->invoke_raw(NULL, args, &exc); - for (Map<String, String>::Element *E = dependencies.front(); E; E = E->next()) { - String depend_src_path = E->value(); - String depend_dst_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(depend_src_path.get_file()); - ERR_FAIL_COND(!_add_assembly(depend_src_path, depend_dst_path)); + if (exc) { + GDMonoUtils::debug_print_unhandled_exception(exc); + ERR_FAIL(); } } -bool GodotSharpExport::_add_assembly(const String &p_src_path, const String &p_dst_path) { +bool GodotSharpExport::_add_file(const String &p_src_path, const String &p_dst_path, bool p_remap) { FileAccessRef f = FileAccess::open(p_src_path, FileAccess::READ); ERR_FAIL_COND_V(!f, false); @@ -120,12 +171,12 @@ bool GodotSharpExport::_add_assembly(const String &p_src_path, const String &p_d data.resize(f->get_len()); f->get_buffer(data.ptrw(), data.size()); - add_file(p_dst_path, data, false); + add_file(p_dst_path, data, p_remap); return true; } -Error GodotSharpExport::_get_assembly_dependencies(GDMonoAssembly *p_assembly, Map<String, String> &r_dependencies) { +Error GodotSharpExport::_get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Map<String, String> &r_dependencies) { MonoImage *image = p_assembly->get_image(); @@ -134,18 +185,48 @@ Error GodotSharpExport::_get_assembly_dependencies(GDMonoAssembly *p_assembly, M mono_assembly_get_assemblyref(image, i, ref_aname); String ref_name = mono_assembly_name_get_name(ref_aname); - if (ref_name == "mscorlib" || r_dependencies.find(ref_name)) + if (r_dependencies.find(ref_name)) continue; GDMonoAssembly *ref_assembly = NULL; - if (!GDMono::get_singleton()->load_assembly(ref_name, ref_aname, &ref_assembly, /* refonly: */ true)) { - ERR_EXPLAIN("Cannot load refonly assembly: " + ref_name); + String path; + bool has_extension = ref_name.ends_with(".dll") || ref_name.ends_with(".exe"); + + for (int i = 0; i < p_search_dirs.size(); i++) { + const String &search_dir = p_search_dirs[i]; + + if (has_extension) { + path = search_dir.plus_file(ref_name); + if (FileAccess::exists(path)) { + GDMono::get_singleton()->load_assembly_from(ref_name.get_basename(), path, &ref_assembly, true); + if (ref_assembly != NULL) + break; + } + } else { + path = search_dir.plus_file(ref_name + ".dll"); + if (FileAccess::exists(path)) { + GDMono::get_singleton()->load_assembly_from(ref_name, path, &ref_assembly, true); + if (ref_assembly != NULL) + break; + } + + path = search_dir.plus_file(ref_name + ".exe"); + if (FileAccess::exists(path)) { + GDMono::get_singleton()->load_assembly_from(ref_name, path, &ref_assembly, true); + if (ref_assembly != NULL) + break; + } + } + } + + if (!ref_assembly) { + ERR_EXPLAIN("Cannot load assembly (refonly): " + ref_name); ERR_FAIL_V(ERR_CANT_RESOLVE); } r_dependencies.insert(ref_name, ref_assembly->get_path()); - Error err = _get_assembly_dependencies(ref_assembly, r_dependencies); + Error err = _get_assembly_dependencies(ref_assembly, p_search_dirs, r_dependencies); if (err != OK) return err; } diff --git a/modules/mono/editor/godotsharp_export.h b/modules/mono/editor/godotsharp_export.h index b38db9660c..10d4375567 100644 --- a/modules/mono/editor/godotsharp_export.h +++ b/modules/mono/editor/godotsharp_export.h @@ -41,15 +41,17 @@ class GodotSharpExport : public EditorExportPlugin { MonoAssemblyName *aname_prealloc; - bool _add_assembly(const String &p_src_path, const String &p_dst_path); + bool _add_file(const String &p_src_path, const String &p_dst_path, bool p_remap = false); - Error _get_assembly_dependencies(GDMonoAssembly *p_assembly, Map<String, String> &r_dependencies); + Error _get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Map<String, String> &r_dependencies); protected: virtual void _export_file(const String &p_path, const String &p_type, const Set<String> &p_features); virtual void _export_begin(const Set<String> &p_features, bool p_debug, const String &p_path, int p_flags); public: + static void register_internal_calls(); + GodotSharpExport(); ~GodotSharpExport(); }; diff --git a/modules/mono/editor/mono_bottom_panel.cpp b/modules/mono/editor/mono_bottom_panel.cpp index 9317550d28..d7bfa54aba 100644 --- a/modules/mono/editor/mono_bottom_panel.cpp +++ b/modules/mono/editor/mono_bottom_panel.cpp @@ -31,6 +31,8 @@ #include "mono_bottom_panel.h" #include "../csharp_script.h" +#include "../godotsharp_dirs.h" +#include "csharp_project.h" #include "godotsharp_editor.h" MonoBottomPanel *MonoBottomPanel::singleton = NULL; @@ -53,9 +55,9 @@ void MonoBottomPanel::_update_build_tabs_list() { build_tabs_list->add_item(item_name, tab->get_icon_texture()); - String item_tooltip = String("Solution: ") + tab->build_info.solution; - item_tooltip += String("\nConfiguration: ") + tab->build_info.configuration; - item_tooltip += String("\nStatus: "); + String item_tooltip = "Solution: " + tab->build_info.solution; + item_tooltip += "\nConfiguration: " + tab->build_info.configuration; + item_tooltip += "\nStatus: "; if (tab->build_exited) { item_tooltip += tab->build_result == MonoBuildTab::RESULT_SUCCESS ? "Succeeded" : "Errored"; @@ -63,7 +65,7 @@ void MonoBottomPanel::_update_build_tabs_list() { item_tooltip += "Running"; } - if (!tab->build_exited || !tab->build_result == MonoBuildTab::RESULT_SUCCESS) { + if (!tab->build_exited || tab->build_result == MonoBuildTab::RESULT_ERROR) { item_tooltip += "\nErrors: " + itos(tab->error_count); } @@ -148,6 +150,10 @@ void MonoBottomPanel::_errors_toggled(bool p_pressed) { void MonoBottomPanel::_build_project_pressed() { + String scripts_metadata_path = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata.editor"); + Error metadata_err = CSharpProject::generate_scripts_metadata(GodotSharpDirs::get_project_csproj_path(), scripts_metadata_path); + ERR_FAIL_COND(metadata_err != OK); + GodotSharpBuilds::get_singleton()->build_project_blocking("Tools"); MonoReloadNode::get_singleton()->restart_reload_timer(); @@ -475,14 +481,14 @@ void MonoBuildTab::_bind_methods() { } MonoBuildTab::MonoBuildTab(const MonoBuildInfo &p_build_info, const String &p_logs_dir) : - build_info(p_build_info), - logs_dir(p_logs_dir), build_exited(false), issues_list(memnew(ItemList)), error_count(0), warning_count(0), errors_visible(true), - warnings_visible(true) { + warnings_visible(true), + logs_dir(p_logs_dir), + build_info(p_build_info) { issues_list->set_v_size_flags(SIZE_EXPAND_FILL); issues_list->connect("item_activated", this, "_issue_activated"); add_child(issues_list); diff --git a/modules/mono/editor/monodevelop_instance.cpp b/modules/mono/editor/monodevelop_instance.cpp index 9f05711fd6..1d858d80bf 100644 --- a/modules/mono/editor/monodevelop_instance.cpp +++ b/modules/mono/editor/monodevelop_instance.cpp @@ -47,7 +47,7 @@ void MonoDevelopInstance::execute(const Vector<String> &p_files) { execute_method->invoke(gc_handle->get_target(), args, &exc); if (exc) { - GDMonoUtils::debug_unhandled_exception(exc); + GDMonoUtils::debug_print_unhandled_exception(exc); ERR_FAIL(); } } @@ -59,7 +59,7 @@ void MonoDevelopInstance::execute(const String &p_file) { execute(files); } -MonoDevelopInstance::MonoDevelopInstance(const String &p_solution) { +MonoDevelopInstance::MonoDevelopInstance(const String &p_solution, EditorId p_editor_id) { _GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN) @@ -67,15 +67,16 @@ MonoDevelopInstance::MonoDevelopInstance(const String &p_solution) { MonoObject *obj = mono_object_new(TOOLS_DOMAIN, klass->get_mono_ptr()); - GDMonoMethod *ctor = klass->get_method(".ctor", 1); + GDMonoMethod *ctor = klass->get_method(".ctor", 2); MonoException *exc = NULL; Variant solution = p_solution; - const Variant *args[1] = { &solution }; + Variant editor_id = p_editor_id; + const Variant *args[2] = { &solution, &editor_id }; ctor->invoke(obj, args, &exc); if (exc) { - GDMonoUtils::debug_unhandled_exception(exc); + GDMonoUtils::debug_print_unhandled_exception(exc); ERR_FAIL(); } diff --git a/modules/mono/editor/monodevelop_instance.h b/modules/mono/editor/monodevelop_instance.h index 552c10a61d..29de4a4735 100644 --- a/modules/mono/editor/monodevelop_instance.h +++ b/modules/mono/editor/monodevelop_instance.h @@ -31,7 +31,7 @@ #ifndef MONODEVELOP_INSTANCE_H #define MONODEVELOP_INSTANCE_H -#include "reference.h" +#include "core/reference.h" #include "../mono_gc_handle.h" #include "../mono_gd/gd_mono_method.h" @@ -42,10 +42,15 @@ class MonoDevelopInstance { GDMonoMethod *execute_method; public: + enum EditorId { + MONODEVELOP = 0, + VISUALSTUDIO_FOR_MAC = 1 + }; + void execute(const Vector<String> &p_files); void execute(const String &p_file); - MonoDevelopInstance(const String &p_solution); + MonoDevelopInstance(const String &p_solution, EditorId p_editor_id); }; #endif // MONODEVELOP_INSTANCE_H diff --git a/modules/mono/editor/net_solution.cpp b/modules/mono/editor/net_solution.cpp index dab96e44e9..a000debe52 100644 --- a/modules/mono/editor/net_solution.cpp +++ b/modules/mono/editor/net_solution.cpp @@ -30,8 +30,8 @@ #include "net_solution.h" -#include "os/dir_access.h" -#include "os/file_access.h" +#include "core/os/dir_access.h" +#include "core/os/file_access.h" #include "../utils/path_utils.h" #include "../utils/string_utils.h" @@ -52,7 +52,7 @@ #define PROJECT_DECLARATION "Project(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"%0\", \"%1\", \"{%2}\"\nEndProject" -#define SOLUTION_PLATFORMS_CONFIG "\t\%0|Any CPU = %0|Any CPU" +#define SOLUTION_PLATFORMS_CONFIG "\t%0|Any CPU = %0|Any CPU" #define PROJECT_PLATFORMS_CONFIG \ "\t\t{%0}.%1|Any CPU.ActiveCfg = %1|Any CPU\n" \ diff --git a/modules/mono/editor/net_solution.h b/modules/mono/editor/net_solution.h index 293e86917a..bdff24af0b 100644 --- a/modules/mono/editor/net_solution.h +++ b/modules/mono/editor/net_solution.h @@ -31,8 +31,8 @@ #ifndef NET_SOLUTION_H #define NET_SOLUTION_H -#include "map.h" -#include "ustring.h" +#include "core/map.h" +#include "core/ustring.h" struct NETSolution { String name; diff --git a/modules/mono/editor/script_class_parser.cpp b/modules/mono/editor/script_class_parser.cpp new file mode 100644 index 0000000000..bc8eed81cf --- /dev/null +++ b/modules/mono/editor/script_class_parser.cpp @@ -0,0 +1,542 @@ +#include "script_class_parser.h" + +#include "core/map.h" +#include "core/os/os.h" + +#include "../utils/string_utils.h" + +const char *ScriptClassParser::token_names[ScriptClassParser::TK_MAX] = { + "[", + "]", + "{", + "}", + ".", + ":", + ",", + "Symbol", + "Identifier", + "String", + "Number", + "<", + ">", + "EOF", + "Error" +}; + +String ScriptClassParser::get_token_name(ScriptClassParser::Token p_token) { + + ERR_FAIL_INDEX_V(p_token, TK_MAX, "<error>"); + return token_names[p_token]; +} + +ScriptClassParser::Token ScriptClassParser::get_token() { + + while (true) { + switch (code[idx]) { + case '\n': { + line++; + idx++; + break; + }; + case 0: { + return TK_EOF; + } break; + case '{': { + idx++; + return TK_CURLY_BRACKET_OPEN; + }; + case '}': { + idx++; + return TK_CURLY_BRACKET_CLOSE; + }; + case '[': { + idx++; + return TK_BRACKET_OPEN; + }; + case ']': { + idx++; + return TK_BRACKET_CLOSE; + }; + case '<': { + idx++; + return TK_OP_LESS; + }; + case '>': { + idx++; + return TK_OP_GREATER; + }; + case ':': { + idx++; + return TK_COLON; + }; + case ',': { + idx++; + return TK_COMMA; + }; + case '.': { + idx++; + return TK_PERIOD; + }; + case '#': { + //compiler directive + while (code[idx] != '\n' && code[idx] != 0) { + idx++; + } + continue; + } break; + case '/': { + switch (code[idx + 1]) { + case '*': { // block comment + idx += 2; + while (true) { + if (code[idx] == 0) { + error_str = "Unterminated comment"; + error = true; + return TK_ERROR; + } else if (code[idx] == '*' && code[idx + 1] == '/') { + idx += 2; + break; + } else if (code[idx] == '\n') { + line++; + } + + idx++; + } + + } break; + case '/': { // line comment skip + while (code[idx] != '\n' && code[idx] != 0) { + idx++; + } + + } break; + default: { + value = "/"; + idx++; + return TK_SYMBOL; + } + } + + continue; // a comment + } break; + case '\'': + case '"': { + bool verbatim = idx != 0 && code[idx - 1] == '@'; + + CharType begin_str = code[idx]; + idx++; + String tk_string = String(); + while (true) { + if (code[idx] == 0) { + error_str = "Unterminated String"; + error = true; + return TK_ERROR; + } else if (code[idx] == begin_str) { + if (verbatim && code[idx + 1] == '"') { // `""` is verbatim string's `\"` + idx += 2; // skip next `"` as well + continue; + } + + idx += 1; + break; + } else if (code[idx] == '\\' && !verbatim) { + //escaped characters... + idx++; + CharType next = code[idx]; + if (next == 0) { + error_str = "Unterminated String"; + error = true; + return TK_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 '\"': res = '\"'; break; + case '\\': + res = '\\'; + break; + default: { + res = next; + } break; + } + + tk_string += res; + + } else { + if (code[idx] == '\n') + line++; + tk_string += code[idx]; + } + idx++; + } + + value = tk_string; + + return TK_STRING; + } break; + default: { + if (code[idx] <= 32) { + idx++; + break; + } + + if ((code[idx] >= 33 && code[idx] <= 47) || (code[idx] >= 58 && code[idx] <= 63) || (code[idx] >= 91 && code[idx] <= 94) || code[idx] == 96 || (code[idx] >= 123 && code[idx] <= 127)) { + value = String::chr(code[idx]); + idx++; + return TK_SYMBOL; + } + + if (code[idx] == '-' || (code[idx] >= '0' && code[idx] <= '9')) { + //a number + const CharType *rptr; + double number = String::to_double(&code[idx], &rptr); + idx += (rptr - &code[idx]); + value = number; + return TK_NUMBER; + + } else if ((code[idx] == '@' && code[idx + 1] != '"') || code[idx] == '_' || (code[idx] >= 'A' && code[idx] <= 'Z') || (code[idx] >= 'a' && code[idx] <= 'z') || code[idx] > 127) { + String id; + + id += code[idx]; + idx++; + + while (code[idx] == '_' || (code[idx] >= 'A' && code[idx] <= 'Z') || (code[idx] >= 'a' && code[idx] <= 'z') || (code[idx] >= '0' && code[idx] <= '9') || code[idx] > 127) { + id += code[idx]; + idx++; + } + + value = id; + return TK_IDENTIFIER; + } else if (code[idx] == '@' && code[idx + 1] == '"') { + // begin of verbatim string + idx++; + } else { + error_str = "Unexpected character."; + error = true; + return TK_ERROR; + } + } + } + } +} + +Error ScriptClassParser::_skip_generic_type_params() { + + Token tk; + + while (true) { + tk = get_token(); + + if (tk == TK_IDENTIFIER) { + tk = get_token(); + + if (tk == TK_PERIOD) { + while (true) { + tk = get_token(); + + if (tk != TK_IDENTIFIER) { + error_str = "Expected " + get_token_name(TK_IDENTIFIER) + ", found: " + get_token_name(tk); + error = true; + return ERR_PARSE_ERROR; + } + + tk = get_token(); + + if (tk != TK_PERIOD) + break; + } + } + + if (tk == TK_OP_LESS) { + Error err = _skip_generic_type_params(); + if (err) + return err; + continue; + } else if (tk != TK_COMMA) { + error_str = "Unexpected token: " + get_token_name(tk); + error = true; + return ERR_PARSE_ERROR; + } + } else if (tk == TK_OP_LESS) { + error_str = "Expected " + get_token_name(TK_IDENTIFIER) + ", found " + get_token_name(TK_OP_LESS); + error = true; + return ERR_PARSE_ERROR; + } else if (tk == TK_OP_GREATER) { + return OK; + } else { + error_str = "Unexpected token: " + get_token_name(tk); + error = true; + return ERR_PARSE_ERROR; + } + } +} + +Error ScriptClassParser::_parse_type_full_name(String &r_full_name) { + + Token tk = get_token(); + + if (tk != TK_IDENTIFIER) { + error_str = "Expected " + get_token_name(TK_IDENTIFIER) + ", found: " + get_token_name(tk); + error = true; + return ERR_PARSE_ERROR; + } + + r_full_name += String(value); + + if (code[idx] != '.') // We only want to take the next token if it's a period + return OK; + + tk = get_token(); + + CRASH_COND(tk != TK_PERIOD); // Assertion + + r_full_name += "."; + + return _parse_type_full_name(r_full_name); +} + +Error ScriptClassParser::_parse_class_base(Vector<String> &r_base) { + + String name; + + Error err = _parse_type_full_name(name); + if (err) + return err; + + Token tk = get_token(); + + if (tk == TK_OP_LESS) { + // We don't add it to the base list if it's generic + Error err = _skip_generic_type_params(); + if (err) + return err; + } else if (tk == TK_COMMA) { + Error err = _parse_class_base(r_base); + if (err) + return err; + r_base.push_back(name); + } else if (tk == TK_CURLY_BRACKET_OPEN) { + r_base.push_back(name); + } else { + error_str = "Unexpected token: " + get_token_name(tk); + error = true; + return ERR_PARSE_ERROR; + } + + return OK; +} + +Error ScriptClassParser::_parse_namespace_name(String &r_name, int &r_curly_stack) { + + Token tk = get_token(); + + if (tk == TK_IDENTIFIER) { + r_name += String(value); + } else { + error_str = "Unexpected token: " + get_token_name(tk); + error = true; + return ERR_PARSE_ERROR; + } + + tk = get_token(); + + if (tk == TK_PERIOD) { + r_name += "."; + return _parse_namespace_name(r_name, r_curly_stack); + } else if (tk == TK_CURLY_BRACKET_OPEN) { + r_curly_stack++; + return OK; + } else { + error_str = "Unexpected token: " + get_token_name(tk); + error = true; + return ERR_PARSE_ERROR; + } +} + +Error ScriptClassParser::parse(const String &p_code) { + + code = p_code; + idx = 0; + line = 0; + error_str = String(); + error = false; + value = Variant(); + classes.clear(); + + Token tk = get_token(); + + Map<int, NameDecl> name_stack; + int curly_stack = 0; + int type_curly_stack = 0; + + while (!error && tk != TK_EOF) { + if (tk == TK_IDENTIFIER && String(value) == "class") { + tk = get_token(); + + if (tk == TK_IDENTIFIER) { + String name = value; + int at_level = type_curly_stack; + + ClassDecl class_decl; + + for (Map<int, NameDecl>::Element *E = name_stack.front(); E; E = E->next()) { + const NameDecl &name_decl = E->value(); + + if (name_decl.type == NameDecl::NAMESPACE_DECL) { + if (E != name_stack.front()) + class_decl.namespace_ += "."; + class_decl.namespace_ += name_decl.name; + } else { + class_decl.name += name_decl.name + "."; + } + } + + class_decl.name += name; + class_decl.nested = type_curly_stack > 0; + + bool generic = false; + + while (true) { + tk = get_token(); + + if (tk == TK_COLON) { + Error err = _parse_class_base(class_decl.base); + if (err) + return err; + + curly_stack++; + type_curly_stack++; + + break; + } else if (tk == TK_CURLY_BRACKET_OPEN) { + curly_stack++; + type_curly_stack++; + break; + } else if (tk == TK_OP_LESS && !generic) { + generic = true; + + Error err = _skip_generic_type_params(); + if (err) + return err; + } else { + error_str = "Unexpected token: " + get_token_name(tk); + error = true; + return ERR_PARSE_ERROR; + } + } + + NameDecl name_decl; + name_decl.name = name; + name_decl.type = NameDecl::CLASS_DECL; + name_stack[at_level] = name_decl; + + if (!generic) { // no generics, thanks + classes.push_back(class_decl); + } else if (OS::get_singleton()->is_stdout_verbose()) { + String full_name = class_decl.namespace_; + if (full_name.length()) + full_name += "."; + full_name += class_decl.name; + OS::get_singleton()->print(String("Ignoring generic class declaration: " + class_decl.name).utf8()); + } + } + } else if (tk == TK_IDENTIFIER && String(value) == "struct") { + String name; + int at_level = type_curly_stack; + while (true) { + tk = get_token(); + if (tk == TK_IDENTIFIER && name.empty()) { + name = String(value); + } else if (tk == TK_CURLY_BRACKET_OPEN) { + if (name.empty()) { + error_str = "Expected " + get_token_name(TK_IDENTIFIER) + " after keyword `struct`, found " + get_token_name(TK_CURLY_BRACKET_OPEN); + error = true; + return ERR_PARSE_ERROR; + } + + curly_stack++; + type_curly_stack++; + break; + } else if (tk == TK_EOF) { + error_str = "Expected " + get_token_name(TK_CURLY_BRACKET_OPEN) + " after struct decl, found " + get_token_name(TK_EOF); + error = true; + return ERR_PARSE_ERROR; + } + } + + NameDecl name_decl; + name_decl.name = name; + name_decl.type = NameDecl::STRUCT_DECL; + name_stack[at_level] = name_decl; + } else if (tk == TK_IDENTIFIER && String(value) == "namespace") { + if (type_curly_stack > 0) { + error_str = "Found namespace nested inside type."; + error = true; + return ERR_PARSE_ERROR; + } + + String name; + int at_level = curly_stack; + + Error err = _parse_namespace_name(name, curly_stack); + if (err) + return err; + + NameDecl name_decl; + name_decl.name = name; + name_decl.type = NameDecl::NAMESPACE_DECL; + name_stack[at_level] = name_decl; + } else if (tk == TK_CURLY_BRACKET_OPEN) { + curly_stack++; + } else if (tk == TK_CURLY_BRACKET_CLOSE) { + curly_stack--; + if (name_stack.has(curly_stack)) { + if (name_stack[curly_stack].type != NameDecl::NAMESPACE_DECL) + type_curly_stack--; + name_stack.erase(curly_stack); + } + } + + tk = get_token(); + } + + if (!error && tk == TK_EOF && curly_stack > 0) { + error_str = "Reached EOF with missing close curly brackets."; + error = true; + } + + if (error) + return ERR_PARSE_ERROR; + + return OK; +} + +Error ScriptClassParser::parse_file(const String &p_filepath) { + + String source; + + Error ferr = read_all_file_utf8(p_filepath, source); + if (ferr != OK) { + if (ferr == ERR_INVALID_DATA) { + ERR_EXPLAIN("File '" + p_filepath + "' contains invalid unicode (utf-8), so it was not loaded. Please ensure that scripts are saved in valid utf-8 unicode."); + } + ERR_FAIL_V(ferr); + } + + return parse(source); +} + +String ScriptClassParser::get_error() { + return error_str; +} + +Vector<ScriptClassParser::ClassDecl> ScriptClassParser::get_classes() { + return classes; +} diff --git a/modules/mono/editor/script_class_parser.h b/modules/mono/editor/script_class_parser.h new file mode 100644 index 0000000000..1e174c28a9 --- /dev/null +++ b/modules/mono/editor/script_class_parser.h @@ -0,0 +1,79 @@ +#ifndef SCRIPT_CLASS_PARSER_H +#define SCRIPT_CLASS_PARSER_H + +#include "core/ustring.h" +#include "core/variant.h" +#include "core/vector.h" + +class ScriptClassParser { + +public: + struct NameDecl { + enum Type { + NAMESPACE_DECL, + CLASS_DECL, + STRUCT_DECL + }; + + String name; + Type type; + }; + + struct ClassDecl { + String name; + String namespace_; + Vector<String> base; + bool nested; + bool has_script_attr; + }; + +private: + String code; + int idx; + int line; + String error_str; + bool error; + Variant value; + + Vector<ClassDecl> classes; + + enum Token { + TK_BRACKET_OPEN, + TK_BRACKET_CLOSE, + TK_CURLY_BRACKET_OPEN, + TK_CURLY_BRACKET_CLOSE, + TK_PERIOD, + TK_COLON, + TK_COMMA, + TK_SYMBOL, + TK_IDENTIFIER, + TK_STRING, + TK_NUMBER, + TK_OP_LESS, + TK_OP_GREATER, + TK_EOF, + TK_ERROR, + TK_MAX + }; + + static const char *token_names[TK_MAX]; + static String get_token_name(Token p_token); + + Token get_token(); + + Error _skip_generic_type_params(); + + Error _parse_type_full_name(String &r_full_name); + Error _parse_class_base(Vector<String> &r_base); + Error _parse_namespace_name(String &r_name, int &r_curly_stack); + +public: + Error parse(const String &p_code); + Error parse_file(const String &p_filepath); + + String get_error(); + + Vector<ClassDecl> get_classes(); +}; + +#endif // SCRIPT_CLASS_PARSER_H diff --git a/modules/mono/glue/cs_files/AABB.cs b/modules/mono/glue/Managed/Files/AABB.cs index 0df2e615f1..66490b5e25 100644 --- a/modules/mono/glue/cs_files/AABB.cs +++ b/modules/mono/glue/Managed/Files/AABB.cs @@ -51,24 +51,24 @@ namespace Godot src_max.z > dst_max.z; } - public AABB Expand(Vector3 to_point) + public AABB Expand(Vector3 point) { Vector3 begin = _position; Vector3 end = _position + _size; - if (to_point.x < begin.x) - begin.x = to_point.x; - if (to_point.y < begin.y) - begin.y = to_point.y; - if (to_point.z < begin.z) - begin.z = to_point.z; + if (point.x < begin.x) + begin.x = point.x; + if (point.y < begin.y) + begin.y = point.y; + if (point.z < begin.z) + begin.z = point.z; - if (to_point.x > end.x) - end.x = to_point.x; - if (to_point.y > end.y) - end.y = to_point.y; - if (to_point.z > end.z) - end.z = to_point.z; + if (point.x > end.x) + end.x = point.x; + if (point.y > end.y) + end.y = point.y; + if (point.z > end.z) + end.z = point.z; return new AABB(begin, end - begin); } @@ -347,29 +347,29 @@ namespace Godot for (int i = 0; i < 3; i++) { - real_t seg_from = from[i]; - real_t seg_to = to[i]; - real_t box_begin = _position[i]; - real_t box_end = box_begin + _size[i]; + real_t segFrom = from[i]; + real_t segTo = to[i]; + real_t boxBegin = _position[i]; + real_t boxEnd = boxBegin + _size[i]; real_t cmin, cmax; - if (seg_from < seg_to) + if (segFrom < segTo) { - if (seg_from > box_end || seg_to < box_begin) + if (segFrom > boxEnd || segTo < boxBegin) return false; - real_t length = seg_to - seg_from; - cmin = seg_from < box_begin ? (box_begin - seg_from) / length : 0f; - cmax = seg_to > box_end ? (box_end - seg_from) / length : 1f; + real_t length = segTo - segFrom; + cmin = segFrom < boxBegin ? (boxBegin - segFrom) / length : 0f; + cmax = segTo > boxEnd ? (boxEnd - segFrom) / length : 1f; } else { - if (seg_to > box_end || seg_from < box_begin) + if (segTo > boxEnd || segFrom < boxBegin) return false; - real_t length = seg_to - seg_from; - cmin = seg_from > box_end ? (box_end - seg_from) / length : 0f; - cmax = seg_to < box_begin ? (box_begin - seg_from) / length : 1f; + real_t length = segTo - segFrom; + cmin = segFrom > boxEnd ? (boxEnd - segFrom) / length : 0f; + cmax = segTo < boxBegin ? (boxBegin - segFrom) / length : 1f; } if (cmin > min) @@ -388,21 +388,21 @@ namespace Godot public AABB Merge(AABB with) { - Vector3 beg_1 = _position; - Vector3 beg_2 = with._position; - var end_1 = new Vector3(_size.x, _size.y, _size.z) + beg_1; - var end_2 = new Vector3(with._size.x, with._size.y, with._size.z) + beg_2; + Vector3 beg1 = _position; + Vector3 beg2 = with._position; + var end1 = new Vector3(_size.x, _size.y, _size.z) + beg1; + var end2 = new Vector3(with._size.x, with._size.y, with._size.z) + beg2; var min = new Vector3( - beg_1.x < beg_2.x ? beg_1.x : beg_2.x, - beg_1.y < beg_2.y ? beg_1.y : beg_2.y, - beg_1.z < beg_2.z ? beg_1.z : beg_2.z + beg1.x < beg2.x ? beg1.x : beg2.x, + beg1.y < beg2.y ? beg1.y : beg2.y, + beg1.z < beg2.z ? beg1.z : beg2.z ); var max = new Vector3( - end_1.x > end_2.x ? end_1.x : end_2.x, - end_1.y > end_2.y ? end_1.y : end_2.y, - end_1.z > end_2.z ? end_1.z : end_2.z + end1.x > end2.x ? end1.x : end2.x, + end1.y > end2.y ? end1.y : end2.y, + end1.z > end2.z ? end1.z : end2.z ); return new AABB(min, max - min); diff --git a/modules/mono/glue/cs_files/Array.cs b/modules/mono/glue/Managed/Files/Array.cs index 1ec4d7d20a..d5a35d7ae0 100644 --- a/modules/mono/glue/cs_files/Array.cs +++ b/modules/mono/glue/Managed/Files/Array.cs @@ -4,7 +4,7 @@ using System.Collections; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -namespace Godot +namespace Godot.Collections { class ArraySafeHandle : SafeHandle { @@ -30,45 +30,6 @@ namespace Godot 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; @@ -94,11 +55,6 @@ namespace Godot public void Dispose() { - Dispose(true); - } - - protected virtual void Dispose(bool disposing) - { if (disposed) return; @@ -172,7 +128,7 @@ namespace Godot for (int i = 0; i < count; i++) { - yield return godot_icall_Array_At(GetPtr(), i); + yield return this[i]; } } @@ -200,12 +156,65 @@ namespace Godot { return GetEnumerator(); } + + [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 object godot_icall_Array_At_Generic(IntPtr ptr, int index, int elemTypeEncoding, IntPtr elemTypeClass); + + [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); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Array_Generic_GetElementTypeInfo(Type elemType, out int elemTypeEncoding, out IntPtr elemTypeClass); } public class Array<T> : IList<T>, ICollection<T>, IEnumerable<T> { Array objectArray; + internal static int elemTypeEncoding; + internal static IntPtr elemTypeClass; + + static Array() + { + Array.godot_icall_Array_Generic_GetElementTypeInfo(typeof(T), out elemTypeEncoding, out elemTypeClass); + } + public Array() { objectArray = new Array(); @@ -235,7 +244,7 @@ namespace Godot { get { - return (T)objectArray[index]; + return (T)Array.godot_icall_Array_At_Generic(GetPtr(), index, elemTypeEncoding, elemTypeClass); } set { @@ -292,7 +301,7 @@ namespace Godot for (int i = 0; i < count; i++) { - array[arrayIndex] = (T)objectArray[i]; + array[arrayIndex] = (T)this[i]; arrayIndex++; } } @@ -303,7 +312,7 @@ namespace Godot for (int i = 0; i < count; i++) { - yield return (T)objectArray[i]; + yield return (T)this[i]; } } diff --git a/modules/mono/glue/cs_files/Attributes/ExportAttribute.cs b/modules/mono/glue/Managed/Files/Attributes/ExportAttribute.cs index 6adf044886..6adf044886 100644 --- a/modules/mono/glue/cs_files/Attributes/ExportAttribute.cs +++ b/modules/mono/glue/Managed/Files/Attributes/ExportAttribute.cs diff --git a/modules/mono/glue/cs_files/Attributes/GodotMethodAttribute.cs b/modules/mono/glue/Managed/Files/Attributes/GodotMethodAttribute.cs index 55848769d5..55848769d5 100644 --- a/modules/mono/glue/cs_files/Attributes/GodotMethodAttribute.cs +++ b/modules/mono/glue/Managed/Files/Attributes/GodotMethodAttribute.cs diff --git a/modules/mono/glue/cs_files/Attributes/RPCAttributes.cs b/modules/mono/glue/Managed/Files/Attributes/RPCAttributes.cs index 6bf9560bfa..2398e10135 100644 --- a/modules/mono/glue/cs_files/Attributes/RPCAttributes.cs +++ b/modules/mono/glue/Managed/Files/Attributes/RPCAttributes.cs @@ -12,6 +12,9 @@ namespace Godot public class MasterAttribute : Attribute {} [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)] + public class PuppetAttribute : Attribute {} + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)] public class SlaveAttribute : Attribute {} [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)] @@ -21,5 +24,5 @@ namespace Godot public class MasterSyncAttribute : Attribute {} [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)] - public class SlaveSyncAttribute : Attribute {} + public class PuppetSyncAttribute : Attribute {} } diff --git a/modules/mono/glue/cs_files/Attributes/SignalAttribute.cs b/modules/mono/glue/Managed/Files/Attributes/SignalAttribute.cs index 3957387be9..3957387be9 100644 --- a/modules/mono/glue/cs_files/Attributes/SignalAttribute.cs +++ b/modules/mono/glue/Managed/Files/Attributes/SignalAttribute.cs diff --git a/modules/mono/glue/cs_files/Attributes/ToolAttribute.cs b/modules/mono/glue/Managed/Files/Attributes/ToolAttribute.cs index d0437409af..d0437409af 100644 --- a/modules/mono/glue/cs_files/Attributes/ToolAttribute.cs +++ b/modules/mono/glue/Managed/Files/Attributes/ToolAttribute.cs diff --git a/modules/mono/glue/cs_files/Basis.cs b/modules/mono/glue/Managed/Files/Basis.cs index c280d32c61..a5618cb28d 100644 --- a/modules/mono/glue/cs_files/Basis.cs +++ b/modules/mono/glue/Managed/Files/Basis.cs @@ -165,6 +165,38 @@ namespace Godot ); } + internal Quat RotationQuat() + { + Basis orthonormalizedBasis = Orthonormalized(); + real_t det = orthonormalizedBasis.Determinant(); + if (det < 0) + { + // Ensure that the determinant is 1, such that result is a proper rotation matrix which can be represented by Euler angles. + orthonormalizedBasis = orthonormalizedBasis.Scaled(Vector3.NegOne); + } + + return orthonormalizedBasis.Quat(); + } + + internal void SetQuantScale(Quat quat, Vector3 scale) + { + SetDiagonal(scale); + Rotate(quat); + } + + private void Rotate(Quat quat) + { + this *= new Basis(quat); + } + + private void SetDiagonal(Vector3 diagonal) + { + _x = new Vector3(diagonal.x, 0, 0); + _y = new Vector3(0, diagonal.y, 0); + _z = new Vector3(0, 0, diagonal.z); + + } + public real_t Determinant() { return this[0, 0] * (this[1, 1] * this[2, 2] - this[2, 1] * this[1, 2]) - @@ -378,55 +410,55 @@ namespace Godot ); } - public Quat Quat() { - real_t trace = _x[0] + _y[1] + _z[2]; - - if (trace > 0.0f) { - real_t s = Mathf.Sqrt(trace + 1.0f) * 2f; - real_t inv_s = 1f / s; - return new Quat( - (_z[1] - _y[2]) * inv_s, - (_x[2] - _z[0]) * inv_s, - (_y[0] - _x[1]) * inv_s, - s * 0.25f - ); - } - - if (_x[0] > _y[1] && _x[0] > _z[2]) { - real_t s = Mathf.Sqrt(_x[0] - _y[1] - _z[2] + 1.0f) * 2f; - real_t inv_s = 1f / s; - return new Quat( - s * 0.25f, - (_x[1] + _y[0]) * inv_s, - (_x[2] + _z[0]) * inv_s, - (_z[1] - _y[2]) * inv_s - ); - } - - if (_y[1] > _z[2]) { - real_t s = Mathf.Sqrt(-_x[0] + _y[1] - _z[2] + 1.0f) * 2f; - real_t inv_s = 1f / s; - return new Quat( - (_x[1] + _y[0]) * inv_s, - s * 0.25f, - (_y[2] + _z[1]) * inv_s, - (_x[2] - _z[0]) * inv_s - ); - } else { - real_t s = Mathf.Sqrt(-_x[0] - _y[1] + _z[2] + 1.0f) * 2f; - real_t inv_s = 1f / s; - return new Quat( - (_x[2] + _z[0]) * inv_s, - (_y[2] + _z[1]) * inv_s, - s * 0.25f, - (_y[0] - _x[1]) * inv_s - ); - } - } + public Quat Quat() { + real_t trace = _x[0] + _y[1] + _z[2]; + + if (trace > 0.0f) { + real_t s = Mathf.Sqrt(trace + 1.0f) * 2f; + real_t inv_s = 1f / s; + return new Quat( + (_z[1] - _y[2]) * inv_s, + (_x[2] - _z[0]) * inv_s, + (_y[0] - _x[1]) * inv_s, + s * 0.25f + ); + } + + if (_x[0] > _y[1] && _x[0] > _z[2]) { + real_t s = Mathf.Sqrt(_x[0] - _y[1] - _z[2] + 1.0f) * 2f; + real_t inv_s = 1f / s; + return new Quat( + s * 0.25f, + (_x[1] + _y[0]) * inv_s, + (_x[2] + _z[0]) * inv_s, + (_z[1] - _y[2]) * inv_s + ); + } + + if (_y[1] > _z[2]) { + real_t s = Mathf.Sqrt(-_x[0] + _y[1] - _z[2] + 1.0f) * 2f; + real_t inv_s = 1f / s; + return new Quat( + (_x[1] + _y[0]) * inv_s, + s * 0.25f, + (_y[2] + _z[1]) * inv_s, + (_x[2] - _z[0]) * inv_s + ); + } else { + real_t s = Mathf.Sqrt(-_x[0] - _y[1] + _z[2] + 1.0f) * 2f; + real_t inv_s = 1f / s; + return new Quat( + (_x[2] + _z[0]) * inv_s, + (_y[2] + _z[1]) * inv_s, + s * 0.25f, + (_y[0] - _x[1]) * inv_s + ); + } + } public Basis(Quat quat) { - real_t s = 2.0f / quat.LengthSquared(); + real_t s = 2.0f / quat.LengthSquared; real_t xs = quat.x * s; real_t ys = quat.y * s; diff --git a/modules/mono/glue/cs_files/Color.cs b/modules/mono/glue/Managed/Files/Color.cs index 49e04b333a..88cb8524b8 100644 --- a/modules/mono/glue/cs_files/Color.cs +++ b/modules/mono/glue/Managed/Files/Color.cs @@ -370,12 +370,12 @@ namespace Godot { var txt = string.Empty; - txt += _to_hex(r); - txt += _to_hex(g); - txt += _to_hex(b); + txt += ToHex32(r); + txt += ToHex32(g); + txt += ToHex32(b); if (include_alpha) - txt = _to_hex(a) + txt; + txt = ToHex32(a) + txt; return txt; } @@ -411,7 +411,7 @@ namespace Godot r = (rgba & 0xFFFF) / 65535.0f; } - private static int _parse_col(string str, int ofs) + private static int ParseCol8(string str, int ofs) { int ig = 0; @@ -448,7 +448,7 @@ namespace Godot return ig; } - private String _to_hex(float val) + private String ToHex32(float val) { int v = Mathf.RoundToInt(Mathf.Clamp(val * 255, 0, 255)); @@ -490,17 +490,17 @@ namespace Godot if (alpha) { - if (_parse_col(color, 0) < 0) + if (ParseCol8(color, 0) < 0) return false; } int from = alpha ? 2 : 0; - if (_parse_col(color, from + 0) < 0) + if (ParseCol8(color, from + 0) < 0) return false; - if (_parse_col(color, from + 2) < 0) + if (ParseCol8(color, from + 2) < 0) return false; - if (_parse_col(color, from + 4) < 0) + if (ParseCol8(color, from + 4) < 0) return false; return true; @@ -542,7 +542,7 @@ namespace Godot if (alpha) { - a = _parse_col(rgba, 0) / 255f; + a = ParseCol8(rgba, 0) / 255f; if (a < 0) throw new ArgumentOutOfRangeException("Invalid color code. Alpha part is not valid hexadecimal: " + rgba); @@ -554,17 +554,17 @@ namespace Godot int from = alpha ? 2 : 0; - r = _parse_col(rgba, from + 0) / 255f; + r = ParseCol8(rgba, from + 0) / 255f; if (r < 0) throw new ArgumentOutOfRangeException("Invalid color code. Red part is not valid hexadecimal: " + rgba); - g = _parse_col(rgba, from + 2) / 255f; + g = ParseCol8(rgba, from + 2) / 255f; if (g < 0) throw new ArgumentOutOfRangeException("Invalid color code. Green part is not valid hexadecimal: " + rgba); - b = _parse_col(rgba, from + 4) / 255f; + b = ParseCol8(rgba, from + 4) / 255f; if (b < 0) throw new ArgumentOutOfRangeException("Invalid color code. Blue part is not valid hexadecimal: " + rgba); diff --git a/modules/mono/glue/cs_files/DebuggingUtils.cs b/modules/mono/glue/Managed/Files/DebuggingUtils.cs index b27816084e..b27816084e 100644 --- a/modules/mono/glue/cs_files/DebuggingUtils.cs +++ b/modules/mono/glue/Managed/Files/DebuggingUtils.cs diff --git a/modules/mono/glue/cs_files/Dictionary.cs b/modules/mono/glue/Managed/Files/Dictionary.cs index 30d17c2a59..7695f03cd6 100644 --- a/modules/mono/glue/cs_files/Dictionary.cs +++ b/modules/mono/glue/Managed/Files/Dictionary.cs @@ -4,7 +4,7 @@ using System.Collections; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -namespace Godot +namespace Godot.Collections { class DictionarySafeHandle : SafeHandle { @@ -34,48 +34,6 @@ namespace Godot 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; @@ -101,11 +59,6 @@ namespace Godot public void Dispose() { - Dispose(true); - } - - protected virtual void Dispose(bool disposing) - { if (disposed) return; @@ -240,8 +193,58 @@ namespace Godot { return GetEnumerator(); } - } + [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 object godot_icall_Dictionary_GetValue_Generic(IntPtr ptr, object key, int valTypeEncoding, IntPtr valTypeClass); + + [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); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Dictionary_TryGetValue_Generic(IntPtr ptr, object key, out object value, int valTypeEncoding, IntPtr valTypeClass); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Dictionary_Generic_GetValueTypeInfo(Type valueType, out int valTypeEncoding, out IntPtr valTypeClass); + } public class Dictionary<TKey, TValue> : IDictionary<TKey, TValue>, @@ -250,6 +253,14 @@ namespace Godot { Dictionary objectDict; + internal static int valTypeEncoding; + internal static IntPtr valTypeClass; + + static Dictionary() + { + Dictionary.godot_icall_Dictionary_Generic_GetValueTypeInfo(typeof(TValue), out valTypeEncoding, out valTypeClass); + } + public Dictionary() { objectDict = new Dictionary(); @@ -279,7 +290,7 @@ namespace Godot { get { - return (TValue)objectDict[key]; + return (TValue)Dictionary.godot_icall_Dictionary_GetValue_Generic(objectDict.GetPtr(), key, valTypeEncoding, valTypeClass); } set { @@ -388,7 +399,7 @@ namespace Godot public bool TryGetValue(TKey key, out TValue value) { object retValue; - bool found = objectDict.TryGetValue(key, out retValue); + bool found = Dictionary.godot_icall_Dictionary_TryGetValue_Generic(GetPtr(), key, out retValue, valTypeEncoding, valTypeClass); value = found ? (TValue)retValue : default(TValue); return found; } diff --git a/modules/mono/glue/Managed/Files/Extensions/NodeExtensions.cs b/modules/mono/glue/Managed/Files/Extensions/NodeExtensions.cs new file mode 100644 index 0000000000..366d89b1c2 --- /dev/null +++ b/modules/mono/glue/Managed/Files/Extensions/NodeExtensions.cs @@ -0,0 +1,45 @@ +namespace Godot +{ + public partial class Node + { + public T GetNode<T>(NodePath path) where T : class + { + return (T)(object)GetNode(path); + } + + public T GetNodeOrNull<T>(NodePath path) where T : class + { + return GetNode(path) as T; + } + + public T GetChild<T>(int idx) where T : class + { + return (T)(object)GetChild(idx); + } + + public T GetChildOrNull<T>(int idx) where T : class + { + return GetChild(idx) as T; + } + + public T GetOwner<T>() where T : class + { + return (T)(object)GetOwner(); + } + + public T GetOwnerOrNull<T>() where T : class + { + return GetOwner() as T; + } + + public T GetParent<T>() where T : class + { + return (T)(object)GetParent(); + } + + public T GetParentOrNull<T>() where T : class + { + return GetParent() as T; + } + } +} diff --git a/modules/mono/glue/cs_files/Extensions/ObjectExtensions.cs b/modules/mono/glue/Managed/Files/Extensions/ObjectExtensions.cs index 5c9e6609f4..9ef0959750 100644 --- a/modules/mono/glue/cs_files/Extensions/ObjectExtensions.cs +++ b/modules/mono/glue/Managed/Files/Extensions/ObjectExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; namespace Godot { @@ -11,7 +12,10 @@ namespace Godot public static WeakRef WeakRef(Object obj) { - return NativeCalls.godot_icall_Godot_weakref(Object.GetPtr(obj)); + return godot_icall_Object_weakref(Object.GetPtr(obj)); } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static WeakRef godot_icall_Object_weakref(IntPtr obj); } } diff --git a/modules/mono/glue/Managed/Files/Extensions/ResourceLoaderExtensions.cs b/modules/mono/glue/Managed/Files/Extensions/ResourceLoaderExtensions.cs new file mode 100644 index 0000000000..684d160b57 --- /dev/null +++ b/modules/mono/glue/Managed/Files/Extensions/ResourceLoaderExtensions.cs @@ -0,0 +1,10 @@ +namespace Godot +{ + public static partial class ResourceLoader + { + public static T Load<T>(string path) where T : class + { + return (T)(object)Load(path); + } + } +} diff --git a/modules/mono/glue/cs_files/GD.cs b/modules/mono/glue/Managed/Files/GD.cs index 0a5d703f27..75a35a9eea 100644 --- a/modules/mono/glue/cs_files/GD.cs +++ b/modules/mono/glue/Managed/Files/GD.cs @@ -1,11 +1,12 @@ using System; +using System.Runtime.CompilerServices; #if REAL_T_IS_DOUBLE using real_t = System.Double; #else using real_t = System.Single; #endif -// TODO: Add comments describing what this class does. It is not obvious. +// TODO: Add comments describing what this class does. It is not obvious. namespace Godot { @@ -13,12 +14,12 @@ namespace Godot { public static object Bytes2Var(byte[] bytes) { - return NativeCalls.godot_icall_Godot_bytes2var(bytes); + return godot_icall_GD_bytes2var(bytes); } public static object Convert(object what, int type) { - return NativeCalls.godot_icall_Godot_convert(what, type); + return godot_icall_GD_convert(what, type); } public static real_t Db2Linear(real_t db) @@ -46,12 +47,12 @@ namespace Godot public static int Hash(object var) { - return NativeCalls.godot_icall_Godot_hash(var); + return godot_icall_GD_hash(var); } public static Object InstanceFromId(int instanceId) { - return NativeCalls.godot_icall_Godot_instance_from_id(instanceId); + return godot_icall_GD_instance_from_id(instanceId); } public static real_t Linear2Db(real_t linear) @@ -64,14 +65,24 @@ namespace Godot return ResourceLoader.Load(path); } - public static T Load<T>(string path) where T : Godot.Resource + public static T Load<T>(string path) where T : class { - return (T) ResourceLoader.Load(path); + return ResourceLoader.Load<T>(path); + } + + public static void PushError(string message) + { + godot_icall_GD_pusherror(message); + } + + public static void PushWarning(string message) + { + godot_icall_GD_pushwarning(message); } public static void Print(params object[] what) { - NativeCalls.godot_icall_Godot_print(what); + godot_icall_GD_print(what); } public static void PrintStack() @@ -81,22 +92,22 @@ namespace Godot public static void PrintErr(params object[] what) { - NativeCalls.godot_icall_Godot_printerr(what); + godot_icall_GD_printerr(what); } public static void PrintRaw(params object[] what) { - NativeCalls.godot_icall_Godot_printraw(what); + godot_icall_GD_printraw(what); } public static void PrintS(params object[] what) { - NativeCalls.godot_icall_Godot_prints(what); + godot_icall_GD_prints(what); } public static void PrintT(params object[] what) { - NativeCalls.godot_icall_Godot_printt(what); + godot_icall_GD_printt(what); } public static int[] Range(int length) @@ -165,32 +176,83 @@ namespace Godot public static void Seed(int seed) { - NativeCalls.godot_icall_Godot_seed(seed); + godot_icall_GD_seed(seed); } public static string Str(params object[] what) { - return NativeCalls.godot_icall_Godot_str(what); + return godot_icall_GD_str(what); } public static object Str2Var(string str) { - return NativeCalls.godot_icall_Godot_str2var(str); + return godot_icall_GD_str2var(str); } public static bool TypeExists(string type) { - return NativeCalls.godot_icall_Godot_type_exists(type); + return godot_icall_GD_type_exists(type); } public static byte[] Var2Bytes(object var) { - return NativeCalls.godot_icall_Godot_var2bytes(var); + return godot_icall_GD_var2bytes(var); } public static string Var2Str(object var) { - return NativeCalls.godot_icall_Godot_var2str(var); + return godot_icall_GD_var2str(var); } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static object godot_icall_GD_bytes2var(byte[] bytes); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static object godot_icall_GD_convert(object what, int type); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static int godot_icall_GD_hash(object var); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static Object godot_icall_GD_instance_from_id(int instance_id); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_GD_print(object[] what); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_GD_printerr(object[] what); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_GD_printraw(object[] what); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_GD_prints(object[] what); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_GD_printt(object[] what); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_GD_seed(int seed); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_GD_str(object[] what); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static object godot_icall_GD_str2var(string str); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_GD_type_exists(string type); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static byte[] godot_icall_GD_var2bytes(object what); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_GD_var2str(object var); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_GD_pusherror(string type); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_GD_pushwarning(string type); } } diff --git a/modules/mono/glue/Managed/Files/GodotSynchronizationContext.cs b/modules/mono/glue/Managed/Files/GodotSynchronizationContext.cs new file mode 100644 index 0000000000..e727781d63 --- /dev/null +++ b/modules/mono/glue/Managed/Files/GodotSynchronizationContext.cs @@ -0,0 +1,25 @@ +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading; + +namespace Godot +{ + public class GodotSynchronizationContext : SynchronizationContext + { + private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> queue = new BlockingCollection<KeyValuePair<SendOrPostCallback, object>>(); + + public override void Post(SendOrPostCallback d, object state) + { + queue.Add(new KeyValuePair<SendOrPostCallback, object>(d, state)); + } + + public void ExecutePendingContinuations() + { + KeyValuePair<SendOrPostCallback, object> workItem; + while (queue.TryTake(out workItem)) + { + workItem.Key(workItem.Value); + } + } + } +} diff --git a/modules/mono/glue/Managed/Files/GodotTaskScheduler.cs b/modules/mono/glue/Managed/Files/GodotTaskScheduler.cs new file mode 100644 index 0000000000..9a40fef5a9 --- /dev/null +++ b/modules/mono/glue/Managed/Files/GodotTaskScheduler.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Godot +{ + public class GodotTaskScheduler : TaskScheduler + { + private GodotSynchronizationContext Context { get; set; } + private readonly LinkedList<Task> _tasks = new LinkedList<Task>(); + + public GodotTaskScheduler() + { + Context = new GodotSynchronizationContext(); + SynchronizationContext.SetSynchronizationContext(Context); + } + + protected sealed override void QueueTask(Task task) + { + lock (_tasks) + { + _tasks.AddLast(task); + } + } + + protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) + { + if (SynchronizationContext.Current != Context) + { + return false; + } + + if (taskWasPreviouslyQueued) + { + TryDequeue(task); + } + + return TryExecuteTask(task); + } + + protected sealed override bool TryDequeue(Task task) + { + lock (_tasks) + { + return _tasks.Remove(task); + } + } + + protected sealed override IEnumerable<Task> GetScheduledTasks() + { + lock (_tasks) + { + return _tasks.ToArray(); + } + } + + public void Activate() + { + ExecuteQueuedTasks(); + Context.ExecutePendingContinuations(); + } + + private void ExecuteQueuedTasks() + { + while (true) + { + Task task; + + lock (_tasks) + { + if (_tasks.Any()) + { + task = _tasks.First.Value; + _tasks.RemoveFirst(); + } + else + { + break; + } + } + + if (task != null) + { + if (!TryExecuteTask(task)) + { + throw new InvalidOperationException(); + } + } + } + } + } +} diff --git a/modules/mono/glue/cs_files/Interfaces/IAwaitable.cs b/modules/mono/glue/Managed/Files/Interfaces/IAwaitable.cs index 0397957d00..0397957d00 100644 --- a/modules/mono/glue/cs_files/Interfaces/IAwaitable.cs +++ b/modules/mono/glue/Managed/Files/Interfaces/IAwaitable.cs diff --git a/modules/mono/glue/cs_files/Interfaces/IAwaiter.cs b/modules/mono/glue/Managed/Files/Interfaces/IAwaiter.cs index d3be9d781c..d3be9d781c 100644 --- a/modules/mono/glue/cs_files/Interfaces/IAwaiter.cs +++ b/modules/mono/glue/Managed/Files/Interfaces/IAwaiter.cs diff --git a/modules/mono/glue/cs_files/MarshalUtils.cs b/modules/mono/glue/Managed/Files/MarshalUtils.cs index 6ad4b3dcb2..f7699a15bf 100644 --- a/modules/mono/glue/cs_files/MarshalUtils.cs +++ b/modules/mono/glue/Managed/Files/MarshalUtils.cs @@ -1,4 +1,5 @@ using System; +using Godot.Collections; namespace Godot { diff --git a/modules/mono/glue/cs_files/Mathf.cs b/modules/mono/glue/Managed/Files/Mathf.cs index a89dfe5f27..a89dfe5f27 100644 --- a/modules/mono/glue/cs_files/Mathf.cs +++ b/modules/mono/glue/Managed/Files/Mathf.cs diff --git a/modules/mono/glue/cs_files/MathfEx.cs b/modules/mono/glue/Managed/Files/MathfEx.cs index 739b7fb568..739b7fb568 100644 --- a/modules/mono/glue/cs_files/MathfEx.cs +++ b/modules/mono/glue/Managed/Files/MathfEx.cs diff --git a/modules/mono/glue/Managed/Files/NodePath.cs b/modules/mono/glue/Managed/Files/NodePath.cs new file mode 100644 index 0000000000..2c89bec87f --- /dev/null +++ b/modules/mono/glue/Managed/Files/NodePath.cs @@ -0,0 +1,147 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Godot +{ + public partial class NodePath : IDisposable + { + private bool disposed = false; + + internal IntPtr ptr; + + internal static IntPtr GetPtr(NodePath instance) + { + return instance == null ? IntPtr.Zero : instance.ptr; + } + + ~NodePath() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposed) + return; + + if (ptr != IntPtr.Zero) + { + godot_icall_NodePath_Dtor(ptr); + ptr = IntPtr.Zero; + } + + disposed = true; + } + + internal NodePath(IntPtr ptr) + { + this.ptr = ptr; + } + + public IntPtr NativeInstance + { + get { return ptr; } + } + + public NodePath() : this(string.Empty) {} + + public NodePath(string path) + { + this.ptr = godot_icall_NodePath_Ctor(path); + } + + public static implicit operator NodePath(string from) + { + return new NodePath(from); + } + + public static implicit operator string(NodePath from) + { + return godot_icall_NodePath_operator_String(NodePath.GetPtr(from)); + } + + public override string ToString() + { + return (string)this; + } + + public NodePath GetAsPropertyPath() + { + return new NodePath(godot_icall_NodePath_get_as_property_path(NodePath.GetPtr(this))); + } + + public string GetConcatenatedSubnames() + { + return godot_icall_NodePath_get_concatenated_subnames(NodePath.GetPtr(this)); + } + + public string GetName(int idx) + { + return godot_icall_NodePath_get_name(NodePath.GetPtr(this), idx); + } + + public int GetNameCount() + { + return godot_icall_NodePath_get_name_count(NodePath.GetPtr(this)); + } + + public string GetSubname(int idx) + { + return godot_icall_NodePath_get_subname(NodePath.GetPtr(this), idx); + } + + public int GetSubnameCount() + { + return godot_icall_NodePath_get_subname_count(NodePath.GetPtr(this)); + } + + public bool IsAbsolute() + { + return godot_icall_NodePath_is_absolute(NodePath.GetPtr(this)); + } + + public bool IsEmpty() + { + return godot_icall_NodePath_is_empty(NodePath.GetPtr(this)); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_NodePath_Ctor(string path); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_NodePath_Dtor(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_NodePath_operator_String(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_NodePath_get_as_property_path(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_NodePath_get_concatenated_subnames(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_NodePath_get_name(IntPtr ptr, int arg1); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static int godot_icall_NodePath_get_name_count(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_NodePath_get_subname(IntPtr ptr, int arg1); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static int godot_icall_NodePath_get_subname_count(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_NodePath_is_absolute(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_NodePath_is_empty(IntPtr ptr); + } +} diff --git a/modules/mono/glue/Managed/Files/Object.base.cs b/modules/mono/glue/Managed/Files/Object.base.cs new file mode 100644 index 0000000000..30490a715f --- /dev/null +++ b/modules/mono/glue/Managed/Files/Object.base.cs @@ -0,0 +1,88 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Godot +{ + public partial class Object : IDisposable + { + private bool disposed = false; + + private const string nativeName = "Object"; + + internal IntPtr ptr; + internal bool memoryOwn; + + public Object() : this(false) + { + if (ptr == IntPtr.Zero) + ptr = godot_icall_Object_Ctor(this); + } + + internal Object(bool memoryOwn) + { + this.memoryOwn = memoryOwn; + } + + public IntPtr NativeInstance + { + get { return ptr; } + } + + internal static IntPtr GetPtr(Object instance) + { + return instance == null ? IntPtr.Zero : instance.ptr; + } + + ~Object() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposed) + return; + + if (ptr != IntPtr.Zero) + { + if (memoryOwn) + { + memoryOwn = false; + godot_icall_Reference_Disposed(this, ptr, !disposing); + } + else + { + godot_icall_Object_Disposed(this, ptr); + } + + this.ptr = IntPtr.Zero; + } + + disposed = true; + } + + public SignalAwaiter ToSignal(Object source, string signal) + { + return new SignalAwaiter(source, signal, this); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_Object_Ctor(Object obj); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Object_Disposed(Object obj, IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Reference_Disposed(Object obj, IntPtr ptr, bool isFinalizer); + + // Used by the generated API + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_Object_ClassDB_get_method(string type, string method); + } +} diff --git a/modules/mono/glue/cs_files/Plane.cs b/modules/mono/glue/Managed/Files/Plane.cs index 9611dce11e..9611dce11e 100644 --- a/modules/mono/glue/cs_files/Plane.cs +++ b/modules/mono/glue/Managed/Files/Plane.cs diff --git a/modules/mono/glue/cs_files/Quat.cs b/modules/mono/glue/Managed/Files/Quat.cs index c69c55d997..eaa027eb69 100644 --- a/modules/mono/glue/cs_files/Quat.cs +++ b/modules/mono/glue/Managed/Files/Quat.cs @@ -11,18 +11,11 @@ namespace Godot [StructLayout(LayoutKind.Sequential)] public struct Quat : IEquatable<Quat> { - private static readonly Quat identity = new Quat(0f, 0f, 0f, 1f); - public real_t x; public real_t y; public real_t z; public real_t w; - public static Quat Identity - { - get { return identity; } - } - public real_t this[int index] { get @@ -63,6 +56,16 @@ namespace Godot } } + public real_t Length + { + get { return Mathf.Sqrt(LengthSquared); } + } + + public real_t LengthSquared + { + get { return Dot(this); } + } + public Quat CubicSlerp(Quat b, Quat preA, Quat postB, real_t t) { real_t t2 = (1.0f - t) * t * 2f; @@ -76,24 +79,20 @@ namespace Godot return x * b.x + y * b.y + z * b.z + w * b.w; } - public Quat Inverse() - { - return new Quat(-x, -y, -z, w); - } - - public real_t Length() + public Vector3 GetEuler() { - return Mathf.Sqrt(LengthSquared()); + var basis = new Basis(this); + return basis.GetEuler(); } - public real_t LengthSquared() + public Quat Inverse() { - return Dot(this); + return new Quat(-x, -y, -z, w); } public Quat Normalized() { - return this / Length(); + return this / Length; } public void Set(real_t x, real_t y, real_t z, real_t w) @@ -103,12 +102,20 @@ namespace Godot this.z = z; this.w = w; } + public void Set(Quat q) { - x = q.x; - y = q.y; - z = q.z; - w = q.w; + this = q; + } + + public void SetAxisAngle(Vector3 axis, real_t angle) + { + this = new Quat(axis, angle); + } + + public void SetEuler(Vector3 eulerYXZ) + { + this = new Quat(eulerYXZ); } public Quat Slerp(Quat b, real_t t) @@ -192,6 +199,9 @@ namespace Godot return new Vector3(q.x, q.y, q.z); } + // Static Readonly Properties + public static Quat Identity { get; } = new Quat(0f, 0f, 0f, 1f); + // Constructors public Quat(real_t x, real_t y, real_t z, real_t w) { @@ -199,15 +209,46 @@ namespace Godot this.y = y; this.z = z; this.w = w; - } + } + + public bool IsNormalized() + { + return Mathf.Abs(LengthSquared - 1) <= Mathf.Epsilon; + } + public Quat(Quat q) - { - x = q.x; - y = q.y; - z = q.z; - w = q.w; + { + this = q; + } + + public Quat(Basis basis) + { + this = basis.Quat(); } - + + public Quat(Vector3 eulerYXZ) + { + real_t half_a1 = eulerYXZ.y * (real_t)0.5; + real_t half_a2 = eulerYXZ.x * (real_t)0.5; + real_t half_a3 = eulerYXZ.z * (real_t)0.5; + + // R = Y(a1).X(a2).Z(a3) convention for Euler angles. + // Conversion to quaternion as listed in https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf (page A-6) + // a3 is the angle of the first rotation, following the notation in this reference. + + real_t cos_a1 = Mathf.Cos(half_a1); + real_t sin_a1 = Mathf.Sin(half_a1); + real_t cos_a2 = Mathf.Cos(half_a2); + real_t sin_a2 = Mathf.Sin(half_a2); + real_t cos_a3 = Mathf.Cos(half_a3); + real_t sin_a3 = Mathf.Sin(half_a3); + + x = sin_a1 * cos_a2 * sin_a3 + cos_a1 * sin_a2 * cos_a3; + y = sin_a1 * cos_a2 * cos_a3 - cos_a1 * sin_a2 * sin_a3; + z = -sin_a1 * sin_a2 * cos_a3 + cos_a1 * cos_a2 * sin_a3; + w = sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3; + } + public Quat(Vector3 axis, real_t angle) { real_t d = axis.Length(); diff --git a/modules/mono/glue/Managed/Files/RID.cs b/modules/mono/glue/Managed/Files/RID.cs new file mode 100644 index 0000000000..b862b8cac0 --- /dev/null +++ b/modules/mono/glue/Managed/Files/RID.cs @@ -0,0 +1,76 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Godot +{ + public partial class RID : IDisposable + { + private bool disposed = false; + + internal IntPtr ptr; + + internal static IntPtr GetPtr(RID instance) + { + return instance == null ? IntPtr.Zero : instance.ptr; + } + + ~RID() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposed) + return; + + if (ptr != IntPtr.Zero) + { + godot_icall_RID_Dtor(ptr); + ptr = IntPtr.Zero; + } + + disposed = true; + } + + internal RID(IntPtr ptr) + { + this.ptr = ptr; + } + + public IntPtr NativeInstance + { + get { return ptr; } + } + + internal RID() + { + this.ptr = IntPtr.Zero; + } + + public RID(Object from) + { + this.ptr = godot_icall_RID_Ctor(Object.GetPtr(from)); + } + + public int GetId() + { + return godot_icall_RID_get_id(RID.GetPtr(this)); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_RID_Ctor(IntPtr from); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_RID_Dtor(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static int godot_icall_RID_get_id(IntPtr ptr); + } +} diff --git a/modules/mono/glue/cs_files/Rect2.cs b/modules/mono/glue/Managed/Files/Rect2.cs index cb25c267bc..cb25c267bc 100644 --- a/modules/mono/glue/cs_files/Rect2.cs +++ b/modules/mono/glue/Managed/Files/Rect2.cs diff --git a/modules/mono/glue/cs_files/SignalAwaiter.cs b/modules/mono/glue/Managed/Files/SignalAwaiter.cs index c06f6b05c9..9483b6ffb4 100644 --- a/modules/mono/glue/cs_files/SignalAwaiter.cs +++ b/modules/mono/glue/Managed/Files/SignalAwaiter.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; namespace Godot { @@ -10,12 +11,12 @@ namespace Godot public SignalAwaiter(Object source, string signal, Object target) { - NativeCalls.godot_icall_Object_connect_signal_awaiter( - Object.GetPtr(source), - signal, Object.GetPtr(target), this - ); + godot_icall_SignalAwaiter_connect(Object.GetPtr(source), signal, Object.GetPtr(target), this); } + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static Error godot_icall_SignalAwaiter_connect(IntPtr source, string signal, IntPtr target, SignalAwaiter awaiter); + public bool IsCompleted { get diff --git a/modules/mono/glue/cs_files/StringExtensions.cs b/modules/mono/glue/Managed/Files/StringExtensions.cs index b58f8bc6a8..21c9be98c1 100644 --- a/modules/mono/glue/cs_files/StringExtensions.cs +++ b/modules/mono/glue/Managed/Files/StringExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.Runtime.CompilerServices; using System.Security; using System.Text; using System.Text.RegularExpressions; @@ -26,7 +27,7 @@ namespace Godot return slices; } - private static string GetSlicec(this string instance, char splitter, int slice) + private static string GetSliceCharacter(this string instance, char splitter, int slice) { if (!instance.Empty() && slice >= 0) { @@ -36,12 +37,18 @@ namespace Godot while (true) { - if (instance[i] == 0 || instance[i] == splitter) + bool end = instance.Length <= i; + + if (end || instance[i] == splitter) { if (slice == count) { return instance.Substring(prev, i - prev); } + else if (end) + { + return string.Empty; + } count++; prev = i + 1; @@ -57,7 +64,7 @@ namespace Godot // <summary> // If the string is a path to a file, return the path to the file without the extension. // </summary> - public static string Basename(this string instance) + public static string BaseName(this string instance) { int index = instance.LastIndexOf('.'); @@ -144,7 +151,7 @@ namespace Godot for (int i = 0; i < aux.GetSliceCount(" "); i++) { - string slice = aux.GetSlicec(' ', i); + string slice = aux.GetSliceCharacter(' ', i); if (slice.Length > 0) { slice = char.ToUpper(slice[0]) + slice.Substring(1); @@ -162,30 +169,59 @@ namespace Godot // </summary> public static int CasecmpTo(this string instance, string to) { + return instance.CompareTo(to, true); + } + + // <summary> + // Perform a comparison to another string, return -1 if less, 0 if equal and +1 if greater. + // </summary> + public static int CompareTo(this string instance, string to, bool caseSensitive = true) + { if (instance.Empty()) return to.Empty() ? 0 : -1; if (to.Empty()) return 1; - int instance_idx = 0; - int to_idx = 0; - - while (true) + int instanceIndex = 0; + int toIndex = 0; + + if (caseSensitive) // Outside while loop to avoid checking multiple times, despite some code duplication. { - if (to[to_idx] == 0 && instance[instance_idx] == 0) - return 0; // We're equal - if (instance[instance_idx] == 0) - return -1; // If this is empty, and the other one is not, then we're less... I think? - if (to[to_idx] == 0) - return 1; // Otherwise the other one is smaller... - if (instance[instance_idx] < to[to_idx]) // More than - return -1; - if (instance[instance_idx] > to[to_idx]) // Less than - return 1; - - instance_idx++; - to_idx++; + while (true) + { + if (to[toIndex] == 0 && instance[instanceIndex] == 0) + return 0; // We're equal + if (instance[instanceIndex] == 0) + return -1; // If this is empty, and the other one is not, then we're less... I think? + if (to[toIndex] == 0) + return 1; // Otherwise the other one is smaller... + if (instance[instanceIndex] < to[toIndex]) // More than + return -1; + if (instance[instanceIndex] > to[toIndex]) // Less than + return 1; + + instanceIndex++; + toIndex++; + } + } else + { + while (true) + { + if (to[toIndex] == 0 && instance[instanceIndex] == 0) + return 0; // We're equal + if (instance[instanceIndex] == 0) + return -1; // If this is empty, and the other one is not, then we're less... I think? + if (to[toIndex] == 0) + return 1; // Otherwise the other one is smaller.. + if (char.ToUpper(instance[instanceIndex]) < char.ToUpper(to[toIndex])) // More than + return -1; + if (char.ToUpper(instance[instanceIndex]) > char.ToUpper(to[toIndex])) // Less than + return 1; + + instanceIndex++; + toIndex++; + } } } @@ -361,7 +397,7 @@ namespace Godot // <summary> // Check whether this string is a subsequence of the given string. // </summary> - public static bool IsSubsequenceOf(this string instance, string text, bool case_insensitive) + public static bool IsSubsequenceOf(this string instance, string text, bool caseSensitive = true) { int len = instance.Length; @@ -371,50 +407,42 @@ namespace Godot if (len > text.Length) return false; - int src = 0; - int tgt = 0; + int source = 0; + int target = 0; - while (instance[src] != 0 && text[tgt] != 0) + while (instance[source] != 0 && text[target] != 0) { bool match; - if (case_insensitive) + if (!caseSensitive) { - char srcc = char.ToLower(instance[src]); - char tgtc = char.ToLower(text[tgt]); - match = srcc == tgtc; + char sourcec = char.ToLower(instance[source]); + char targetc = char.ToLower(text[target]); + match = sourcec == targetc; } else { - match = instance[src] == text[tgt]; + match = instance[source] == text[target]; } if (match) { - src++; - if (instance[src] == 0) + source++; + if (instance[source] == 0) return true; } - tgt++; + target++; } return false; } // <summary> - // Check whether this string is a subsequence of the given string, considering case. - // </summary> - public static bool IsSubsequenceOf(this string instance, string text) - { - return instance.IsSubsequenceOf(text, false); - } - - // <summary> - // Check whether this string is a subsequence of the given string, without considering case. + // Check whether this string is a subsequence of the given string, ignoring case differences. // </summary> public static bool IsSubsequenceOfI(this string instance, string text) { - return instance.IsSubsequenceOf(text, true); + return instance.IsSubsequenceOf(text, false); } // <summary> @@ -452,12 +480,12 @@ namespace Godot return false; // Don't start with number plz } - bool valid_char = instance[i] >= '0' && + bool validChar = instance[i] >= '0' && instance[i] <= '9' || instance[i] >= 'a' && instance[i] <= 'z' || instance[i] >= 'A' && instance[i] <= 'Z' || instance[i] == '_'; - if (!valid_char) + if (!validChar) return false; } @@ -476,8 +504,9 @@ namespace Godot // <summary> // Check whether the string contains a valid IP address. // </summary> - public static bool IsValidIpAddress(this string instance) + public static bool IsValidIPAddress(this string instance) { + // TODO: Support IPv6 addresses string[] ip = instance.Split("."); if (ip.Length != 4) @@ -500,7 +529,7 @@ namespace Godot // <summary> // Return a copy of the string with special characters escaped using the JSON standard. // </summary> - public static string JsonEscape(this string instance) + public static string JSONEscape(this string instance) { var sb = new StringBuilder(string.Copy(instance)); @@ -563,15 +592,15 @@ namespace Godot // <summary> // Do a simple case sensitive expression match, using ? and * wildcards (see [method expr_match]). // </summary> - public static bool Match(this string instance, string expr) + public static bool Match(this string instance, string expr, bool caseSensitive = true) { - return instance.ExprMatch(expr, true); + return instance.ExprMatch(expr, caseSensitive); } // <summary> // Do a simple case insensitive expression match, using ? and * wildcards (see [method expr_match]). // </summary> - public static bool Matchn(this string instance, string expr) + public static bool MatchN(this string instance, string expr) { return instance.ExprMatch(expr, false); } @@ -579,49 +608,31 @@ namespace Godot // <summary> // Return the MD5 hash of the string as an array of bytes. // </summary> - public static byte[] Md5Buffer(this string instance) + public static byte[] MD5Buffer(this string instance) { - return NativeCalls.godot_icall_String_md5_buffer(instance); + return godot_icall_String_md5_buffer(instance); } + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static byte[] godot_icall_String_md5_buffer(string str); + // <summary> // Return the MD5 hash of the string as a string. // </summary> - public static string Md5Text(this string instance) + public static string MD5Text(this string instance) { - return NativeCalls.godot_icall_String_md5_text(instance); + return godot_icall_String_md5_text(instance); } + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_String_md5_text(string str); + // <summary> // Perform a case-insensitive comparison to another string, return -1 if less, 0 if equal and +1 if greater. // </summary> public static int NocasecmpTo(this string instance, string to) { - if (instance.Empty()) - return to.Empty() ? 0 : -1; - - if (to.Empty()) - return 1; - - int instance_idx = 0; - int to_idx = 0; - - while (true) - { - if (to[to_idx] == 0 && instance[instance_idx] == 0) - return 0; // We're equal - if (instance[instance_idx] == 0) - return -1; // If this is empty, and the other one is not, then we're less... I think? - if (to[to_idx] == 0) - return 1; // Otherwise the other one is smaller.. - if (char.ToUpper(instance[instance_idx]) < char.ToUpper(to[to_idx])) // More than - return -1; - if (char.ToUpper(instance[instance_idx]) > char.ToUpper(to[to_idx])) // Less than - return 1; - - instance_idx++; - to_idx++; - } + return instance.CompareTo(to, false); } // <summary> @@ -738,7 +749,7 @@ namespace Godot // <summary> // Replace occurrences of a substring for different ones inside the string, but search case-insensitive. // </summary> - public static string Replacen(this string instance, string what, string forwhat) + public static string ReplaceN(this string instance, string what, string forwhat) { return Regex.Replace(instance, what, forwhat, RegexOptions.IgnoreCase); } @@ -746,19 +757,25 @@ namespace Godot // <summary> // Perform a search for a substring, but start from the end of the string instead of the beginning. // </summary> - public static int Rfind(this string instance, string what, int from = -1) + public static int RFind(this string instance, string what, int from = -1) { - return NativeCalls.godot_icall_String_rfind(instance, what, from); + return godot_icall_String_rfind(instance, what, from); } + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static int godot_icall_String_rfind(string str, string what, int from); + // <summary> // Perform a search for a substring, but start from the end of the string instead of the beginning. Also search case-insensitive. // </summary> - public static int Rfindn(this string instance, string what, int from = -1) + public static int RFindN(this string instance, string what, int from = -1) { - return NativeCalls.godot_icall_String_rfindn(instance, what, from); + return godot_icall_String_rfindn(instance, what, from); } + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static int godot_icall_String_rfindn(string str, string what, int from); + // <summary> // Return the right side of the string from a given position. // </summary> @@ -773,19 +790,25 @@ namespace Godot return instance.Substring(pos, instance.Length - pos); } - public static byte[] Sha256Buffer(this string instance) + public static byte[] SHA256Buffer(this string instance) { - return NativeCalls.godot_icall_String_sha256_buffer(instance); + return godot_icall_String_sha256_buffer(instance); } + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static byte[] godot_icall_String_sha256_buffer(string str); + // <summary> // Return the SHA-256 hash of the string as a string. // </summary> - public static string Sha256Text(this string instance) + public static string SHA256Text(this string instance) { - return NativeCalls.godot_icall_String_sha256_text(instance); + return godot_icall_String_sha256_text(instance); } + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_String_sha256_text(string str); + // <summary> // Return the similarity index of the text compared to this string. 1 means totally similar and 0 means totally dissimilar. // </summary> @@ -802,20 +825,20 @@ namespace Godot return 0.0f; } - string[] srcBigrams = instance.Bigrams(); - string[] tgtBigrams = text.Bigrams(); + string[] sourceBigrams = instance.Bigrams(); + string[] targetBigrams = text.Bigrams(); - int src_size = srcBigrams.Length; - int tgt_size = tgtBigrams.Length; + int sourceSize = sourceBigrams.Length; + int targetSize = targetBigrams.Length; - float sum = src_size + tgt_size; + float sum = sourceSize + targetSize; float inter = 0; - for (int i = 0; i < src_size; i++) + for (int i = 0; i < sourceSize; i++) { - for (int j = 0; j < tgt_size; j++) + for (int j = 0; j < targetSize; j++) { - if (srcBigrams[i] == tgtBigrams[j]) + if (sourceBigrams[i] == targetBigrams[j]) { inter++; break; @@ -829,7 +852,7 @@ namespace Godot // <summary> // Split the string by a divisor string, return an array of the substrings. Example "One,Two,Three" will return ["One","Two","Three"] if split by ",". // </summary> - public static string[] Split(this string instance, string divisor, bool allow_empty = true) + public static string[] Split(this string instance, string divisor, bool allowEmpty = true) { return instance.Split(new[] { divisor }, StringSplitOptions.RemoveEmptyEntries); } @@ -837,7 +860,7 @@ namespace Godot // <summary> // Split the string in floats by using a divisor string, return an array of the substrings. Example "1,2.5,3" will return [1,2.5,3] if split by ",". // </summary> - public static float[] SplitFloats(this string instance, string divisor, bool allow_empty = true) + public static float[] SplitFloats(this string instance, string divisor, bool allowEmpty = true) { var ret = new List<float>(); int from = 0; @@ -848,7 +871,7 @@ namespace Godot int end = instance.Find(divisor, from); if (end < 0) end = len; - if (allow_empty || end > from) + if (allowEmpty || end > from) ret.Add(float.Parse(instance.Substring(from))); if (end == len) break; @@ -859,7 +882,7 @@ namespace Godot return ret.ToArray(); } - private static readonly char[] non_printable = { + private static readonly char[] _nonPrintable = { (char)00, (char)01, (char)02, (char)03, (char)04, (char)05, (char)06, (char)07, (char)08, (char)09, (char)10, (char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, @@ -876,11 +899,11 @@ namespace Godot if (left) { if (right) - return instance.Trim(non_printable); - return instance.TrimStart(non_printable); + return instance.Trim(_nonPrintable); + return instance.TrimStart(_nonPrintable); } - return instance.TrimEnd(non_printable); + return instance.TrimEnd(_nonPrintable); } // <summary> @@ -934,7 +957,7 @@ namespace Godot // <summary> // Convert the String (which is an array of characters) to PoolByteArray (which is an array of bytes). The conversion is a bit slower than to_ascii(), but supports all UTF-8 characters. Therefore, you should prefer this function over to_ascii(). // </summary> - public static byte[] ToUtf8(this string instance) + public static byte[] ToUTF8(this string instance) { return Encoding.UTF8.GetBytes(instance); } @@ -942,7 +965,7 @@ namespace Godot // <summary> // Return a copy of the string with special characters escaped using the XML standard. // </summary> - public static string XmlEscape(this string instance) + public static string XMLEscape(this string instance) { return SecurityElement.Escape(instance); } @@ -950,7 +973,7 @@ namespace Godot // <summary> // Return a copy of the string with escaped characters replaced by their meanings according to the XML standard. // </summary> - public static string XmlUnescape(this string instance) + public static string XMLUnescape(this string instance) { return SecurityElement.FromString(instance).Text; } diff --git a/modules/mono/glue/cs_files/Transform.cs b/modules/mono/glue/Managed/Files/Transform.cs index e432d5b52c..068007d7f0 100644 --- a/modules/mono/glue/cs_files/Transform.cs +++ b/modules/mono/glue/Managed/Files/Transform.cs @@ -20,6 +20,25 @@ namespace Godot return new Transform(basisInv, basisInv.Xform(-origin)); } + public Transform InterpolateWith(Transform transform, real_t c) + { + /* not sure if very "efficient" but good enough? */ + + Vector3 sourceScale = basis.Scale; + Quat sourceRotation = basis.RotationQuat(); + Vector3 sourceLocation = origin; + + Vector3 destinationScale = transform.basis.Scale; + Quat destinationRotation = transform.basis.RotationQuat(); + Vector3 destinationLocation = transform.origin; + + var interpolated = new Transform(); + interpolated.basis.SetQuantScale(sourceRotation.Slerp(destinationRotation, c).Normalized(), sourceScale.LinearInterpolate(destinationScale, c)); + interpolated.origin = sourceLocation.LinearInterpolate(destinationLocation, c); + + return interpolated; + } + public Transform Inverse() { Basis basisTr = basis.Transposed(); diff --git a/modules/mono/glue/cs_files/Transform2D.cs b/modules/mono/glue/Managed/Files/Transform2D.cs index 8d30833066..8d30833066 100644 --- a/modules/mono/glue/cs_files/Transform2D.cs +++ b/modules/mono/glue/Managed/Files/Transform2D.cs diff --git a/modules/mono/glue/cs_files/Vector2.cs b/modules/mono/glue/Managed/Files/Vector2.cs index 080b8802ba..080b8802ba 100644 --- a/modules/mono/glue/cs_files/Vector2.cs +++ b/modules/mono/glue/Managed/Files/Vector2.cs diff --git a/modules/mono/glue/cs_files/Vector3.cs b/modules/mono/glue/Managed/Files/Vector3.cs index 6fffe5e4d6..6fffe5e4d6 100644 --- a/modules/mono/glue/cs_files/Vector3.cs +++ b/modules/mono/glue/Managed/Files/Vector3.cs diff --git a/modules/mono/glue/Managed/IgnoredFiles/Enums.cs b/modules/mono/glue/Managed/IgnoredFiles/Enums.cs new file mode 100644 index 0000000000..05f1abcf93 --- /dev/null +++ b/modules/mono/glue/Managed/IgnoredFiles/Enums.cs @@ -0,0 +1,21 @@ + +namespace Godot +{ + public enum Margin + { + Left = 0, + Top = 1, + Right = 2, + Bottom = 3 + } + + public enum Error + { + Ok = 0 + } + + public enum PropertyHint + { + None = 0 + } +} diff --git a/modules/mono/glue/Managed/IgnoredFiles/FuncRef.cs b/modules/mono/glue/Managed/IgnoredFiles/FuncRef.cs new file mode 100644 index 0000000000..83504fe49f --- /dev/null +++ b/modules/mono/glue/Managed/IgnoredFiles/FuncRef.cs @@ -0,0 +1,17 @@ +using System; + +namespace Godot +{ + public partial class FuncRef + { + public void SetInstance(Object instance) + { + throw new NotImplementedException(); + } + + public void SetFunction(string name) + { + throw new NotImplementedException(); + } + } +} diff --git a/modules/mono/glue/Managed/IgnoredFiles/Node.cs b/modules/mono/glue/Managed/IgnoredFiles/Node.cs new file mode 100644 index 0000000000..99ba0f827a --- /dev/null +++ b/modules/mono/glue/Managed/IgnoredFiles/Node.cs @@ -0,0 +1,28 @@ + +using System; + +namespace Godot +{ + public partial class Node + { + public Node GetChild(int idx) + { + throw new NotImplementedException(); + } + + public Node GetNode(NodePath path) + { + throw new NotImplementedException(); + } + + public Node GetOwner() + { + throw new NotImplementedException(); + } + + public Node GetParent() + { + throw new NotImplementedException(); + } + } +} diff --git a/modules/mono/glue/Managed/IgnoredFiles/Resource.cs b/modules/mono/glue/Managed/IgnoredFiles/Resource.cs new file mode 100644 index 0000000000..cc0a5555b1 --- /dev/null +++ b/modules/mono/glue/Managed/IgnoredFiles/Resource.cs @@ -0,0 +1,7 @@ +namespace Godot +{ + public partial class Resource + { + + } +} diff --git a/modules/mono/glue/Managed/IgnoredFiles/ResourceLoader.cs b/modules/mono/glue/Managed/IgnoredFiles/ResourceLoader.cs new file mode 100644 index 0000000000..6461d35146 --- /dev/null +++ b/modules/mono/glue/Managed/IgnoredFiles/ResourceLoader.cs @@ -0,0 +1,12 @@ +using System; + +namespace Godot +{ + public partial class ResourceLoader + { + public static Resource Load(string path, string typeHint = "", bool pNoCache = false) + { + throw new NotImplementedException(); + } + } +} diff --git a/modules/mono/glue/Managed/IgnoredFiles/WeakRef.cs b/modules/mono/glue/Managed/IgnoredFiles/WeakRef.cs new file mode 100644 index 0000000000..1498b7836b --- /dev/null +++ b/modules/mono/glue/Managed/IgnoredFiles/WeakRef.cs @@ -0,0 +1,7 @@ +namespace Godot +{ + public partial class WeakRef + { + + } +} diff --git a/modules/mono/glue/Managed/Managed.csproj b/modules/mono/glue/Managed/Managed.csproj new file mode 100644 index 0000000000..1f82dde5e7 --- /dev/null +++ b/modules/mono/glue/Managed/Managed.csproj @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">x86</Platform> + <ProjectGuid>{DAA3DEF8-5112-407C-A5E5-6C608CF5F955}</ProjectGuid> + <OutputType>Library</OutputType> + <RootNamespace>Managed</RootNamespace> + <AssemblyName>Managed</AssemblyName> + <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug</OutputPath> + <DefineConstants>DEBUG;</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <ExternalConsole>true</ExternalConsole> + <PlatformTarget>x86</PlatformTarget> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> + <Optimize>true</Optimize> + <OutputPath>bin\Release</OutputPath> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <ExternalConsole>true</ExternalConsole> + <PlatformTarget>x86</PlatformTarget> + </PropertyGroup> + <ItemGroup> + <Reference Include="System" /> + </ItemGroup> + <ItemGroup> + <Compile Include="Files\**\*.cs" /> + <Compile Include="IgnoredFiles\**\*.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + </ItemGroup> + <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> +</Project> diff --git a/modules/mono/glue/Managed/Managed.sln b/modules/mono/glue/Managed/Managed.sln new file mode 100644 index 0000000000..61ddde0fb7 --- /dev/null +++ b/modules/mono/glue/Managed/Managed.sln @@ -0,0 +1,17 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Managed", "Managed.csproj", "{DAA3DEF8-5112-407C-A5E5-6C608CF5F955}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DAA3DEF8-5112-407C-A5E5-6C608CF5F955}.Debug|x86.ActiveCfg = Debug|x86 + {DAA3DEF8-5112-407C-A5E5-6C608CF5F955}.Debug|x86.Build.0 = Debug|x86 + {DAA3DEF8-5112-407C-A5E5-6C608CF5F955}.Release|x86.ActiveCfg = Release|x86 + {DAA3DEF8-5112-407C-A5E5-6C608CF5F955}.Release|x86.Build.0 = Release|x86 + EndGlobalSection +EndGlobal diff --git a/modules/mono/glue/Managed/Properties/AssemblyInfo.cs b/modules/mono/glue/Managed/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..7ed68acad7 --- /dev/null +++ b/modules/mono/glue/Managed/Properties/AssemblyInfo.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle("Managed")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] diff --git a/modules/mono/glue/Managed/README.md b/modules/mono/glue/Managed/README.md new file mode 100644 index 0000000000..65e63cae37 --- /dev/null +++ b/modules/mono/glue/Managed/README.md @@ -0,0 +1,5 @@ +The directory `Files` contains C# files from the core assembly project that are not part of the generated API. Any file with the `.cs` extension in this directory will be added to the core assembly project. + +A dummy solution and project is provided to get tooling help while editing these files, like code completion and name refactoring. + +The directory `IgnoredFiles` contains C# files that are needed to build the dummy project but must not be added to the core assembly project. They contain placeholders for the declarations that are part of the generated API. diff --git a/modules/mono/glue/base_object_glue.cpp b/modules/mono/glue/base_object_glue.cpp new file mode 100644 index 0000000000..d718c3cc61 --- /dev/null +++ b/modules/mono/glue/base_object_glue.cpp @@ -0,0 +1,154 @@ +/*************************************************************************/ +/* base_object_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 "base_object_glue.h" + +#ifdef MONO_GLUE_ENABLED + +#include "core/reference.h" +#include "core/string_db.h" + +#include "../csharp_script.h" +#include "../mono_gd/gd_mono_internals.h" +#include "../mono_gd/gd_mono_utils.h" +#include "../signal_awaiter_utils.h" + +Object *godot_icall_Object_Ctor(MonoObject *p_obj) { + Object *instance = memnew(Object); + GDMonoInternals::tie_managed_to_unmanaged(p_obj, instance); + return instance; +} + +void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr) { +#ifdef DEBUG_ENABLED + CRASH_COND(p_ptr == NULL); +#endif + + if (p_ptr->get_script_instance()) { + CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(p_ptr->get_script_instance()); + if (cs_instance) { + cs_instance->mono_object_disposed(p_obj); + p_ptr->set_script_instance(NULL); + return; + } + } + + void *data = p_ptr->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()); + + if (data) { + Ref<MonoGCHandle> &gchandle = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get().gchandle; + if (gchandle.is_valid()) { + CSharpLanguage::release_script_gchandle(p_obj, gchandle); + } + } +} + +void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, bool p_is_finalizer) { +#ifdef DEBUG_ENABLED + CRASH_COND(p_ptr == NULL); + // This is only called with Reference derived classes + CRASH_COND(!Object::cast_to<Reference>(p_ptr)); +#endif + + Reference *ref = static_cast<Reference *>(p_ptr); + + if (ref->get_script_instance()) { + CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(ref->get_script_instance()); + if (cs_instance) { + bool r_owner_deleted; + cs_instance->mono_object_disposed_baseref(p_obj, p_is_finalizer, r_owner_deleted); + if (!r_owner_deleted && !p_is_finalizer) { + // If the native instance is still alive and Dispose() was called + // (instead of the finalizer), then we remove the script instance. + ref->set_script_instance(NULL); + } + return; + } + } + + // Unsafe refcount decrement. The managed instance also counts as a reference. + // See: CSharpLanguage::alloc_instance_binding_data(Object *p_object) + if (ref->unreference()) { + memdelete(ref); + } else { + void *data = ref->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()); + + if (data) { + Ref<MonoGCHandle> &gchandle = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get().gchandle; + if (gchandle.is_valid()) { + CSharpLanguage::release_script_gchandle(p_obj, gchandle); + } + } + } +} + +MethodBind *godot_icall_Object_ClassDB_get_method(MonoString *p_type, MonoString *p_method) { + StringName type(GDMonoMarshal::mono_string_to_godot(p_type)); + StringName method(GDMonoMarshal::mono_string_to_godot(p_method)); + return ClassDB::get_method(type, method); +} + +MonoObject *godot_icall_Object_weakref(Object *p_obj) { + if (!p_obj) + return NULL; + + Ref<WeakRef> wref; + Reference *ref = Object::cast_to<Reference>(p_obj); + + if (ref) { + REF r = ref; + if (!r.is_valid()) + return NULL; + + wref.instance(); + wref->set_ref(r); + } else { + wref.instance(); + wref->set_obj(p_obj); + } + + return GDMonoUtils::create_managed_for_godot_object(CACHED_CLASS(WeakRef), Reference::get_class_static(), Object::cast_to<Object>(wref.ptr())); +} + +Error godot_icall_SignalAwaiter_connect(Object *p_source, MonoString *p_signal, Object *p_target, MonoObject *p_awaiter) { + String signal = GDMonoMarshal::mono_string_to_godot(p_signal); + return SignalAwaiterUtils::connect_signal_awaiter(p_source, signal, p_target, p_awaiter); +} + +void godot_register_object_icalls() { + mono_add_internal_call("Godot.Object::godot_icall_Object_Ctor", (void *)godot_icall_Object_Ctor); + mono_add_internal_call("Godot.Object::godot_icall_Object_Disposed", (void *)godot_icall_Object_Disposed); + mono_add_internal_call("Godot.Object::godot_icall_Reference_Disposed", (void *)godot_icall_Reference_Disposed); + mono_add_internal_call("Godot.Object::godot_icall_Object_ClassDB_get_method", (void *)godot_icall_Object_ClassDB_get_method); + mono_add_internal_call("Godot.Object::godot_icall_Object_weakref", (void *)godot_icall_Object_weakref); + mono_add_internal_call("Godot.SignalAwaiter::godot_icall_SignalAwaiter_connect", (void *)godot_icall_SignalAwaiter_connect); +} + +#endif // MONO_GLUE_ENABLED diff --git a/modules/mono/glue/base_object_glue.h b/modules/mono/glue/base_object_glue.h new file mode 100644 index 0000000000..2d4d66ebb8 --- /dev/null +++ b/modules/mono/glue/base_object_glue.h @@ -0,0 +1,59 @@ +/*************************************************************************/ +/* base_object_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 BASE_OBJECT_GLUE_H +#define BASE_OBJECT_GLUE_H + +#ifdef MONO_GLUE_ENABLED + +#include "core/class_db.h" +#include "core/object.h" + +#include "../mono_gd/gd_mono_marshal.h" + +Object *godot_icall_Object_Ctor(MonoObject *p_obj); + +void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr); + +void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, bool p_is_finalizer); + +MethodBind *godot_icall_Object_ClassDB_get_method(MonoString *p_type, MonoString *p_method); + +MonoObject *godot_icall_Object_weakref(Object *p_obj); + +Error godot_icall_SignalAwaiter_connect(Object *p_source, MonoString *p_signal, Object *p_target, MonoObject *p_awaiter); + +// Register internal calls + +void godot_register_object_icalls(); + +#endif // MONO_GLUE_ENABLED + +#endif // BASE_OBJECT_GLUE_H diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp index 148bb32398..059e2ff6de 100644 --- a/modules/mono/glue/collections_glue.cpp +++ b/modules/mono/glue/collections_glue.cpp @@ -30,9 +30,12 @@ #include "collections_glue.h" +#ifdef MONO_GLUE_ENABLED + #include <mono/metadata/exception.h> #include "../mono_gd/gd_mono_class.h" +#include "../mono_gd/gd_mono_utils.h" Array *godot_icall_Array_Ctor() { return memnew(Array); @@ -50,6 +53,14 @@ MonoObject *godot_icall_Array_At(Array *ptr, int index) { return GDMonoMarshal::variant_to_mono_object(ptr->operator[](index)); } +MonoObject *godot_icall_Array_At_Generic(Array *ptr, int index, uint32_t type_encoding, GDMonoClass *type_class) { + 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), ManagedType(type_encoding, type_class)); +} + 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()); @@ -119,6 +130,14 @@ void godot_icall_Array_RemoveAt(Array *ptr, int index) { ptr->remove(index); } +void godot_icall_Array_Generic_GetElementTypeInfo(MonoReflectionType *refltype, uint32_t *type_encoding, GDMonoClass **type_class) { + MonoType *elem_type = mono_reflection_type_get_type(refltype); + + *type_encoding = mono_type_get_type(elem_type); + MonoClass *type_class_raw = mono_class_from_mono_type(elem_type); + *type_class = GDMono::get_singleton()->get_class(type_class_raw); +} + Dictionary *godot_icall_Dictionary_Ctor() { return memnew(Dictionary); } @@ -141,6 +160,20 @@ MonoObject *godot_icall_Dictionary_GetValue(Dictionary *ptr, MonoObject *key) { return GDMonoMarshal::variant_to_mono_object(ret); } +MonoObject *godot_icall_Dictionary_GetValue_Generic(Dictionary *ptr, MonoObject *key, uint32_t type_encoding, GDMonoClass *type_class) { + 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, ManagedType(type_encoding, type_class)); +} + 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); } @@ -208,33 +241,58 @@ bool godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, MonoOb return true; } +bool godot_icall_Dictionary_TryGetValue_Generic(Dictionary *ptr, MonoObject *key, MonoObject **value, uint32_t type_encoding, GDMonoClass *type_class) { + Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key)); + if (ret == NULL) { + *value = NULL; + return false; + } + *value = GDMonoMarshal::variant_to_mono_object(ret, ManagedType(type_encoding, type_class)); + return true; +} + +void godot_icall_Dictionary_Generic_GetValueTypeInfo(MonoReflectionType *refltype, uint32_t *type_encoding, GDMonoClass **type_class) { + MonoType *value_type = mono_reflection_type_get_type(refltype); + + *type_encoding = mono_type_get_type(value_type); + MonoClass *type_class_raw = mono_class_from_mono_type(value_type); + *type_class = GDMono::get_singleton()->get_class(type_class_raw); +} + 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); + mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor", (void *)godot_icall_Array_Ctor); + mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Dtor", (void *)godot_icall_Array_Dtor); + mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_At", (void *)godot_icall_Array_At); + mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_At_Generic", (void *)godot_icall_Array_At_Generic); + mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_SetAt", (void *)godot_icall_Array_SetAt); + mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Count", (void *)godot_icall_Array_Count); + mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Add", (void *)godot_icall_Array_Add); + mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Clear", (void *)godot_icall_Array_Clear); + mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Contains", (void *)godot_icall_Array_Contains); + mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_CopyTo", (void *)godot_icall_Array_CopyTo); + mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_IndexOf", (void *)godot_icall_Array_IndexOf); + mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Insert", (void *)godot_icall_Array_Insert); + mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Remove", (void *)godot_icall_Array_Remove); + mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_RemoveAt", (void *)godot_icall_Array_RemoveAt); + mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Generic_GetElementTypeInfo", (void *)godot_icall_Array_Generic_GetElementTypeInfo); + + mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Ctor", (void *)godot_icall_Dictionary_Ctor); + mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Dtor", (void *)godot_icall_Dictionary_Dtor); + mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_GetValue", (void *)godot_icall_Dictionary_GetValue); + mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_GetValue_Generic", (void *)godot_icall_Dictionary_GetValue_Generic); + mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_SetValue", (void *)godot_icall_Dictionary_SetValue); + mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Keys", (void *)godot_icall_Dictionary_Keys); + mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Values", (void *)godot_icall_Dictionary_Values); + mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Count", (void *)godot_icall_Dictionary_Count); + mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Add", (void *)godot_icall_Dictionary_Add); + mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Clear", (void *)godot_icall_Dictionary_Clear); + mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Contains", (void *)godot_icall_Dictionary_Contains); + mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_ContainsKey", (void *)godot_icall_Dictionary_ContainsKey); + mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_RemoveKey", (void *)godot_icall_Dictionary_RemoveKey); + mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Remove", (void *)godot_icall_Dictionary_Remove); + mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue", (void *)godot_icall_Dictionary_TryGetValue); + mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue_Generic", (void *)godot_icall_Dictionary_TryGetValue_Generic); + mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Generic_GetValueTypeInfo", (void *)godot_icall_Dictionary_Generic_GetValueTypeInfo); } + +#endif // MONO_GLUE_ENABLED diff --git a/modules/mono/glue/collections_glue.h b/modules/mono/glue/collections_glue.h index eb5ecfb725..b9b1338510 100644 --- a/modules/mono/glue/collections_glue.h +++ b/modules/mono/glue/collections_glue.h @@ -31,6 +31,8 @@ #ifndef COLLECTIONS_GLUE_H #define COLLECTIONS_GLUE_H +#ifdef MONO_GLUE_ENABLED + #include "core/array.h" #include "../mono_gd/gd_mono_marshal.h" @@ -43,6 +45,8 @@ void godot_icall_Array_Dtor(Array *ptr); MonoObject *godot_icall_Array_At(Array *ptr, int index); +MonoObject *godot_icall_Array_At_Generic(Array *ptr, int index, uint32_t type_encoding, GDMonoClass *type_class); + void godot_icall_Array_SetAt(Array *ptr, int index, MonoObject *value); int godot_icall_Array_Count(Array *ptr); @@ -63,6 +67,8 @@ bool godot_icall_Array_Remove(Array *ptr, MonoObject *item); void godot_icall_Array_RemoveAt(Array *ptr, int index); +void godot_icall_Array_Generic_GetElementTypeInfo(MonoReflectionType *refltype, uint32_t *type_encoding, GDMonoClass **type_class); + // Dictionary Dictionary *godot_icall_Dictionary_Ctor(); @@ -71,6 +77,8 @@ void godot_icall_Dictionary_Dtor(Dictionary *ptr); MonoObject *godot_icall_Dictionary_GetValue(Dictionary *ptr, MonoObject *key); +MonoObject *godot_icall_Dictionary_GetValue_Generic(Dictionary *ptr, MonoObject *key, uint32_t type_encoding, GDMonoClass *type_class); + void godot_icall_Dictionary_SetValue(Dictionary *ptr, MonoObject *key, MonoObject *value); Array *godot_icall_Dictionary_Keys(Dictionary *ptr); @@ -93,8 +101,14 @@ bool godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, MonoObject bool godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, MonoObject **value); +bool godot_icall_Dictionary_TryGetValue_Generic(Dictionary *ptr, MonoObject *key, MonoObject **value, uint32_t type_encoding, GDMonoClass *type_class); + +void godot_icall_Dictionary_Generic_GetValueTypeInfo(MonoReflectionType *refltype, uint32_t *type_encoding, GDMonoClass **type_class); + // Register internal calls void godot_register_collections_icalls(); +#endif // MONO_GLUE_ENABLED + #endif // COLLECTIONS_GLUE_H diff --git a/modules/mono/glue/cs_files/Extensions/NodeExtensions.cs b/modules/mono/glue/cs_files/Extensions/NodeExtensions.cs deleted file mode 100644 index 71534d7782..0000000000 --- a/modules/mono/glue/cs_files/Extensions/NodeExtensions.cs +++ /dev/null @@ -1,45 +0,0 @@ -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/Extensions/ResourceLoaderExtensions.cs b/modules/mono/glue/cs_files/Extensions/ResourceLoaderExtensions.cs deleted file mode 100644 index ceecc589e6..0000000000 --- a/modules/mono/glue/cs_files/Extensions/ResourceLoaderExtensions.cs +++ /dev/null @@ -1,10 +0,0 @@ -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/GodotSynchronizationContext.cs b/modules/mono/glue/cs_files/GodotSynchronizationContext.cs deleted file mode 100644 index da3c7bac83..0000000000 --- a/modules/mono/glue/cs_files/GodotSynchronizationContext.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Threading; - -namespace Godot -{ - public class GodotSynchronizationContext : SynchronizationContext - { - private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> queue = new BlockingCollection<KeyValuePair<SendOrPostCallback, object>>(); - - public override void Post(SendOrPostCallback d, object state) - { - queue.Add(new KeyValuePair<SendOrPostCallback, object>(d, state)); - } - - public void ExecutePendingContinuations() - { - KeyValuePair<SendOrPostCallback, object> workItem; - while (queue.TryTake(out workItem)) - { - workItem.Key(workItem.Value); - } - } - } -} diff --git a/modules/mono/glue/cs_files/GodotTaskScheduler.cs b/modules/mono/glue/cs_files/GodotTaskScheduler.cs deleted file mode 100644 index 3d23ec10f1..0000000000 --- a/modules/mono/glue/cs_files/GodotTaskScheduler.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace Godot -{ - public class GodotTaskScheduler : TaskScheduler - { - private GodotSynchronizationContext Context { get; set; } - private readonly LinkedList<Task> _tasks = new LinkedList<Task>(); - - public GodotTaskScheduler() - { - Context = new GodotSynchronizationContext(); - SynchronizationContext.SetSynchronizationContext(Context); - } - - protected sealed override void QueueTask(Task task) - { - lock (_tasks) - { - _tasks.AddLast(task); - } - } - - protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) - { - if (SynchronizationContext.Current != Context) - { - return false; - } - - if (taskWasPreviouslyQueued) - { - TryDequeue(task); - } - - return TryExecuteTask(task); - } - - protected sealed override bool TryDequeue(Task task) - { - lock (_tasks) - { - return _tasks.Remove(task); - } - } - - protected sealed override IEnumerable<Task> GetScheduledTasks() - { - lock (_tasks) - { - return _tasks.ToArray(); - } - } - - public void Activate() - { - ExecuteQueuedTasks(); - Context.ExecutePendingContinuations(); - } - - private void ExecuteQueuedTasks() - { - while (true) - { - Task task; - - lock (_tasks) - { - if (_tasks.Any()) - { - task = _tasks.First.Value; - _tasks.RemoveFirst(); - } - else - { - break; - } - } - - if (task != null) - { - if (!TryExecuteTask(task)) - { - throw new InvalidOperationException(); - } - } - } - } - } -} diff --git a/modules/mono/glue/gd_glue.cpp b/modules/mono/glue/gd_glue.cpp new file mode 100644 index 0000000000..9f5bcecdd9 --- /dev/null +++ b/modules/mono/glue/gd_glue.cpp @@ -0,0 +1,212 @@ +/*************************************************************************/ +/* gd_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 "gd_glue.h" + +#ifdef MONO_GLUE_ENABLED + +#include "core/array.h" +#include "core/io/marshalls.h" +#include "core/os/os.h" +#include "core/ustring.h" +#include "core/variant.h" +#include "core/variant_parser.h" + +#include "../mono_gd/gd_mono_utils.h" + +MonoObject *godot_icall_GD_bytes2var(MonoArray *p_bytes) { + Variant ret; + PoolByteArray varr = GDMonoMarshal::mono_array_to_PoolByteArray(p_bytes); + PoolByteArray::Read r = varr.read(); + Error err = decode_variant(ret, r.ptr(), varr.size(), NULL); + if (err != OK) { + ret = RTR("Not enough bytes for decoding bytes, or invalid format."); + } + return GDMonoMarshal::variant_to_mono_object(ret); +} + +MonoObject *godot_icall_GD_convert(MonoObject *p_what, int p_type) { + Variant what = GDMonoMarshal::mono_object_to_variant(p_what); + const Variant *args[1] = { &what }; + Variant::CallError ce; + Variant ret = Variant::construct(Variant::Type(p_type), args, 1, ce); + ERR_FAIL_COND_V(ce.error != Variant::CallError::CALL_OK, NULL); + return GDMonoMarshal::variant_to_mono_object(ret); +} + +int godot_icall_GD_hash(MonoObject *p_var) { + return GDMonoMarshal::mono_object_to_variant(p_var).hash(); +} + +MonoObject *godot_icall_GD_instance_from_id(int p_instance_id) { + return GDMonoUtils::unmanaged_get_managed(ObjectDB::get_instance(p_instance_id)); +} + +void godot_icall_GD_print(MonoArray *p_what) { + Array what = GDMonoMarshal::mono_array_to_Array(p_what); + String str; + for (int i = 0; i < what.size(); i++) + str += what[i].operator String(); + print_line(str); +} + +void godot_icall_GD_printerr(MonoArray *p_what) { + Array what = GDMonoMarshal::mono_array_to_Array(p_what); + String str; + for (int i = 0; i < what.size(); i++) + str += what[i].operator String(); + OS::get_singleton()->printerr("%s\n", str.utf8().get_data()); +} + +void godot_icall_GD_printraw(MonoArray *p_what) { + Array what = GDMonoMarshal::mono_array_to_Array(p_what); + String str; + for (int i = 0; i < what.size(); i++) + str += what[i].operator String(); + OS::get_singleton()->print("%s", str.utf8().get_data()); +} + +void godot_icall_GD_prints(MonoArray *p_what) { + Array what = GDMonoMarshal::mono_array_to_Array(p_what); + String str; + for (int i = 0; i < what.size(); i++) { + if (i) + str += " "; + str += what[i].operator String(); + } + print_line(str); +} + +void godot_icall_GD_printt(MonoArray *p_what) { + Array what = GDMonoMarshal::mono_array_to_Array(p_what); + String str; + for (int i = 0; i < what.size(); i++) { + if (i) + str += "\t"; + str += what[i].operator String(); + } + print_line(str); +} + +void godot_icall_GD_seed(int p_seed) { + Math::seed(p_seed); +} + +MonoString *godot_icall_GD_str(MonoArray *p_what) { + String str; + Array what = GDMonoMarshal::mono_array_to_Array(p_what); + + for (int i = 0; i < what.size(); i++) { + String os = what[i].operator String(); + + if (i == 0) + str = os; + else + str += os; + } + + return GDMonoMarshal::mono_string_from_godot(str); +} + +MonoObject *godot_icall_GD_str2var(MonoString *p_str) { + Variant ret; + + VariantParser::StreamString ss; + ss.s = GDMonoMarshal::mono_string_to_godot(p_str); + + String errs; + int line; + Error err = VariantParser::parse(&ss, ret, errs, line); + if (err != OK) { + String err_str = "Parse error at line " + itos(line) + ": " + errs; + ERR_PRINTS(err_str); + ret = err_str; + } + + return GDMonoMarshal::variant_to_mono_object(ret); +} + +bool godot_icall_GD_type_exists(MonoString *p_type) { + return ClassDB::class_exists(GDMonoMarshal::mono_string_to_godot(p_type)); +} + +void godot_icall_GD_pusherror(MonoString *p_str) { + ERR_PRINTS(GDMonoMarshal::mono_string_to_godot(p_str)); +} + +void godot_icall_GD_pushwarning(MonoString *p_str) { + WARN_PRINTS(GDMonoMarshal::mono_string_to_godot(p_str)); +} + +MonoArray *godot_icall_GD_var2bytes(MonoObject *p_var) { + Variant var = GDMonoMarshal::mono_object_to_variant(p_var); + + PoolByteArray barr; + int len; + Error err = encode_variant(var, NULL, len); + ERR_EXPLAIN("Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID)."); + ERR_FAIL_COND_V(err != OK, NULL); + + barr.resize(len); + { + PoolByteArray::Write w = barr.write(); + encode_variant(var, w.ptr(), len); + } + + return GDMonoMarshal::PoolByteArray_to_mono_array(barr); +} + +MonoString *godot_icall_GD_var2str(MonoObject *p_var) { + String vars; + VariantWriter::write_to_string(GDMonoMarshal::mono_object_to_variant(p_var), vars); + return GDMonoMarshal::mono_string_from_godot(vars); +} + +void godot_register_gd_icalls() { + mono_add_internal_call("Godot.GD::godot_icall_GD_bytes2var", (void *)godot_icall_GD_bytes2var); + mono_add_internal_call("Godot.GD::godot_icall_GD_convert", (void *)godot_icall_GD_convert); + mono_add_internal_call("Godot.GD::godot_icall_GD_hash", (void *)godot_icall_GD_hash); + mono_add_internal_call("Godot.GD::godot_icall_GD_instance_from_id", (void *)godot_icall_GD_instance_from_id); + mono_add_internal_call("Godot.GD::godot_icall_GD_pusherror", (void *)godot_icall_GD_pusherror); + mono_add_internal_call("Godot.GD::godot_icall_GD_pushwarning", (void *)godot_icall_GD_pushwarning); + mono_add_internal_call("Godot.GD::godot_icall_GD_print", (void *)godot_icall_GD_print); + mono_add_internal_call("Godot.GD::godot_icall_GD_printerr", (void *)godot_icall_GD_printerr); + mono_add_internal_call("Godot.GD::godot_icall_GD_printraw", (void *)godot_icall_GD_printraw); + mono_add_internal_call("Godot.GD::godot_icall_GD_prints", (void *)godot_icall_GD_prints); + mono_add_internal_call("Godot.GD::godot_icall_GD_printt", (void *)godot_icall_GD_printt); + mono_add_internal_call("Godot.GD::godot_icall_GD_seed", (void *)godot_icall_GD_seed); + mono_add_internal_call("Godot.GD::godot_icall_GD_str", (void *)godot_icall_GD_str); + mono_add_internal_call("Godot.GD::godot_icall_GD_str2var", (void *)godot_icall_GD_str2var); + mono_add_internal_call("Godot.GD::godot_icall_GD_type_exists", (void *)godot_icall_GD_type_exists); + mono_add_internal_call("Godot.GD::godot_icall_GD_var2bytes", (void *)godot_icall_GD_var2bytes); + mono_add_internal_call("Godot.GD::godot_icall_GD_var2str", (void *)godot_icall_GD_var2str); +} + +#endif // MONO_GLUE_ENABLED diff --git a/modules/mono/glue/gd_glue.h b/modules/mono/glue/gd_glue.h new file mode 100644 index 0000000000..6f846f221d --- /dev/null +++ b/modules/mono/glue/gd_glue.h @@ -0,0 +1,74 @@ +/*************************************************************************/ +/* gd_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 GD_GLUE_H +#define GD_GLUE_H + +#ifdef MONO_GLUE_ENABLED + +#include "../mono_gd/gd_mono_marshal.h" + +MonoObject *godot_icall_GD_bytes2var(MonoArray *p_bytes); + +MonoObject *godot_icall_GD_convert(MonoObject *p_what, int p_type); + +int godot_icall_GD_hash(MonoObject *p_var); + +MonoObject *godot_icall_GD_instance_from_id(int p_instance_id); + +void godot_icall_GD_print(MonoArray *p_what); + +void godot_icall_GD_printerr(MonoArray *p_what); + +void godot_icall_GD_printraw(MonoArray *p_what); + +void godot_icall_GD_prints(MonoArray *p_what); + +void godot_icall_GD_printt(MonoArray *p_what); + +void godot_icall_GD_seed(int p_seed); + +MonoString *godot_icall_GD_str(MonoArray *p_what); + +MonoObject *godot_icall_GD_str2var(MonoString *p_str); + +bool godot_icall_GD_type_exists(MonoString *p_type); + +MonoArray *godot_icall_GD_var2bytes(MonoObject *p_var); + +MonoString *godot_icall_GD_var2str(MonoObject *p_var); + +// Register internal calls + +void godot_register_gd_icalls(); + +#endif // MONO_GLUE_ENABLED + +#endif // GD_GLUE_H diff --git a/modules/mono/glue/glue_header.h b/modules/mono/glue/glue_header.h index 6a6f3062b4..69c5c6dcdb 100644 --- a/modules/mono/glue/glue_header.h +++ b/modules/mono/glue/glue_header.h @@ -28,27 +28,44 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "builtin_types_glue.h" +#ifdef MONO_GLUE_ENABLED + +#include "base_object_glue.h" #include "collections_glue.h" +#include "gd_glue.h" +#include "nodepath_glue.h" +#include "rid_glue.h" +#include "string_glue.h" + +/** + * Registers internal calls that were not generated. This function is called + * from the generated GodotSharpBindings::register_generated_icalls() function. + */ +void godot_register_glue_header_icalls() { + godot_register_collections_icalls(); + godot_register_gd_icalls(); + godot_register_nodepath_icalls(); + godot_register_object_icalls(); + godot_register_rid_icalls(); + godot_register_string_icalls(); +} + +// Used by the generated glue + +#include "core/array.h" +#include "core/class_db.h" +#include "core/dictionary.h" +#include "core/engine.h" +#include "core/method_bind.h" +#include "core/node_path.h" +#include "core/object.h" +#include "core/reference.h" +#include "core/typedefs.h" +#include "core/ustring.h" -#include "../csharp_script.h" #include "../mono_gd/gd_mono_class.h" #include "../mono_gd/gd_mono_internals.h" -#include "../mono_gd/gd_mono_marshal.h" -#include "../signal_awaiter_utils.h" - -#include "bind/core_bind.h" -#include "class_db.h" -#include "engine.h" -#include "io/marshalls.h" -#include "object.h" -#include "os/os.h" -#include "reference.h" -#include "variant_parser.h" - -#ifdef TOOLS_ENABLED -#include "editor/editor_node.h" -#endif +#include "../mono_gd/gd_mono_utils.h" #define GODOTSHARP_INSTANCE_OBJECT(m_instance, m_type) \ static ClassDB::ClassInfo *ci = NULL; \ @@ -57,257 +74,4 @@ } \ Object *m_instance = ci->creation_func(); -void godot_icall_Object_Dtor(MonoObject *obj, Object *ptr) { -#ifdef DEBUG_ENABLED - CRASH_COND(ptr == NULL); -#endif - _GodotSharp::get_singleton()->queue_dispose(obj, ptr); -} - -// -- ClassDB -- - -MethodBind *godot_icall_ClassDB_get_method(MonoString *p_type, MonoString *p_method) { - StringName type(GDMonoMarshal::mono_string_to_godot(p_type)); - StringName method(GDMonoMarshal::mono_string_to_godot(p_method)); - return ClassDB::get_method(type, method); -} - -// -- SignalAwaiter -- - -Error godot_icall_Object_connect_signal_awaiter(Object *p_source, MonoString *p_signal, Object *p_target, MonoObject *p_awaiter) { - String signal = GDMonoMarshal::mono_string_to_godot(p_signal); - return SignalAwaiterUtils::connect_signal_awaiter(p_source, signal, p_target, p_awaiter); -} - -// -- NodePath -- - -NodePath *godot_icall_NodePath_Ctor(MonoString *p_path) { - return memnew(NodePath(GDMonoMarshal::mono_string_to_godot(p_path))); -} - -void godot_icall_NodePath_Dtor(NodePath *p_ptr) { - ERR_FAIL_NULL(p_ptr); - _GodotSharp::get_singleton()->queue_dispose(p_ptr); -} - -MonoString *godot_icall_NodePath_operator_String(NodePath *p_np) { - return GDMonoMarshal::mono_string_from_godot(p_np->operator String()); -} - -// -- RID -- - -RID *godot_icall_RID_Ctor(Object *p_from) { - Resource *res_from = Object::cast_to<Resource>(p_from); - - if (res_from) - return memnew(RID(res_from->get_rid())); - - return memnew(RID); -} - -void godot_icall_RID_Dtor(RID *p_ptr) { - ERR_FAIL_NULL(p_ptr); - _GodotSharp::get_singleton()->queue_dispose(p_ptr); -} - -// -- String -- - -MonoArray *godot_icall_String_md5_buffer(MonoString *p_str) { - Vector<uint8_t> ret = GDMonoMarshal::mono_string_to_godot(p_str).md5_buffer(); - // TODO Check possible Array/Vector<uint8_t> problem? - return GDMonoMarshal::Array_to_mono_array(Variant(ret)); -} - -MonoString *godot_icall_String_md5_text(MonoString *p_str) { - String ret = GDMonoMarshal::mono_string_to_godot(p_str).md5_text(); - return GDMonoMarshal::mono_string_from_godot(ret); -} - -int godot_icall_String_rfind(MonoString *p_str, MonoString *p_what, int p_from) { - String what = GDMonoMarshal::mono_string_to_godot(p_what); - return GDMonoMarshal::mono_string_to_godot(p_str).rfind(what, p_from); -} - -int godot_icall_String_rfindn(MonoString *p_str, MonoString *p_what, int p_from) { - String what = GDMonoMarshal::mono_string_to_godot(p_what); - return GDMonoMarshal::mono_string_to_godot(p_str).rfindn(what, p_from); -} - -MonoArray *godot_icall_String_sha256_buffer(MonoString *p_str) { - Vector<uint8_t> ret = GDMonoMarshal::mono_string_to_godot(p_str).sha256_buffer(); - return GDMonoMarshal::Array_to_mono_array(Variant(ret)); -} - -MonoString *godot_icall_String_sha256_text(MonoString *p_str) { - String ret = GDMonoMarshal::mono_string_to_godot(p_str).sha256_text(); - return GDMonoMarshal::mono_string_from_godot(ret); -} - -// -- Global Scope -- - -MonoObject *godot_icall_Godot_bytes2var(MonoArray *p_bytes) { - Variant ret; - PoolByteArray varr = GDMonoMarshal::mono_array_to_PoolByteArray(p_bytes); - PoolByteArray::Read r = varr.read(); - Error err = decode_variant(ret, r.ptr(), varr.size(), NULL); - if (err != OK) { - ret = RTR("Not enough bytes for decoding bytes, or invalid format."); - } - return GDMonoMarshal::variant_to_mono_object(ret); -} - -MonoObject *godot_icall_Godot_convert(MonoObject *p_what, int p_type) { - Variant what = GDMonoMarshal::mono_object_to_variant(p_what); - const Variant *args[1] = { &what }; - Variant::CallError ce; - Variant ret = Variant::construct(Variant::Type(p_type), args, 1, ce); - ERR_FAIL_COND_V(ce.error != Variant::CallError::CALL_OK, NULL); - return GDMonoMarshal::variant_to_mono_object(ret); -} - -int godot_icall_Godot_hash(MonoObject *p_var) { - return GDMonoMarshal::mono_object_to_variant(p_var).hash(); -} - -MonoObject *godot_icall_Godot_instance_from_id(int p_instance_id) { - return GDMonoUtils::unmanaged_get_managed(ObjectDB::get_instance(p_instance_id)); -} - -void godot_icall_Godot_print(MonoArray *p_what) { - Array what = GDMonoMarshal::mono_array_to_Array(p_what); - String str; - for (int i = 0; i < what.size(); i++) - str += what[i].operator String(); - print_line(str); -} - -void godot_icall_Godot_printerr(MonoArray *p_what) { - Array what = GDMonoMarshal::mono_array_to_Array(p_what); - String str; - for (int i = 0; i < what.size(); i++) - str += what[i].operator String(); - OS::get_singleton()->printerr("%s\n", str.utf8().get_data()); -} - -void godot_icall_Godot_printraw(MonoArray *p_what) { - Array what = GDMonoMarshal::mono_array_to_Array(p_what); - String str; - for (int i = 0; i < what.size(); i++) - str += what[i].operator String(); - OS::get_singleton()->print("%s", str.utf8().get_data()); -} - -void godot_icall_Godot_prints(MonoArray *p_what) { - Array what = GDMonoMarshal::mono_array_to_Array(p_what); - String str; - for (int i = 0; i < what.size(); i++) { - if (i) - str += " "; - str += what[i].operator String(); - } - print_line(str); -} - -void godot_icall_Godot_printt(MonoArray *p_what) { - Array what = GDMonoMarshal::mono_array_to_Array(p_what); - String str; - for (int i = 0; i < what.size(); i++) { - if (i) - str += "\t"; - str += what[i].operator String(); - } - print_line(str); -} - -void godot_icall_Godot_seed(int p_seed) { - Math::seed(p_seed); -} - -MonoString *godot_icall_Godot_str(MonoArray *p_what) { - String str; - Array what = GDMonoMarshal::mono_array_to_Array(p_what); - - for (int i = 0; i < what.size(); i++) { - String os = what[i].operator String(); - - if (i == 0) - str = os; - else - str += os; - } - - return GDMonoMarshal::mono_string_from_godot(str); -} - -MonoObject *godot_icall_Godot_str2var(MonoString *p_str) { - Variant ret; - - VariantParser::StreamString ss; - ss.s = GDMonoMarshal::mono_string_to_godot(p_str); - - String errs; - int line; - Error err = VariantParser::parse(&ss, ret, errs, line); - if (err != OK) { - String err_str = "Parse error at line " + itos(line) + ": " + errs; - ERR_PRINTS(err_str); - ret = err_str; - } - - return GDMonoMarshal::variant_to_mono_object(ret); -} - -bool godot_icall_Godot_type_exists(MonoString *p_type) { - return ClassDB::class_exists(GDMonoMarshal::mono_string_to_godot(p_type)); -} - -MonoArray *godot_icall_Godot_var2bytes(MonoObject *p_var) { - Variant var = GDMonoMarshal::mono_object_to_variant(p_var); - - PoolByteArray barr; - int len; - Error err = encode_variant(var, NULL, len); - ERR_EXPLAIN("Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID)."); - ERR_FAIL_COND_V(err != OK, NULL); - - barr.resize(len); - { - PoolByteArray::Write w = barr.write(); - encode_variant(var, w.ptr(), len); - } - - return GDMonoMarshal::PoolByteArray_to_mono_array(barr); -} - -MonoString *godot_icall_Godot_var2str(MonoObject *p_var) { - String vars; - VariantWriter::write_to_string(GDMonoMarshal::mono_object_to_variant(p_var), vars); - return GDMonoMarshal::mono_string_from_godot(vars); -} - -MonoObject *godot_icall_Godot_weakref(Object *p_obj) { - if (!p_obj) - return NULL; - - Ref<WeakRef> wref; - Reference *ref = Object::cast_to<Reference>(p_obj); - - if (ref) { - REF r = ref; - if (!r.is_valid()) - return NULL; - - wref.instance(); - wref->set_ref(r); - } else { - wref.instance(); - wref->set_obj(p_obj); - } - - return GDMonoUtils::create_managed_for_godot_object(CACHED_CLASS(WeakRef), Reference::get_class_static(), Object::cast_to<Object>(wref.ptr())); -} - -void godot_register_header_icalls() { - godot_register_builtin_type_icalls(); - godot_register_collections_icalls(); -} +#endif // MONO_GLUE_ENABLED diff --git a/modules/mono/glue/builtin_types_glue.h b/modules/mono/glue/nodepath_glue.cpp index ef9f152682..422d73104d 100644 --- a/modules/mono/glue/builtin_types_glue.h +++ b/modules/mono/glue/nodepath_glue.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* builtin_types_glue.h */ +/* nodepath_glue.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,15 +28,24 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef BUILTIN_TYPES_GLUE_H -#define BUILTIN_TYPES_GLUE_H +#include "nodepath_glue.h" -#include "core/node_path.h" -#include "core/rid.h" +#ifdef MONO_GLUE_ENABLED -#include <mono/metadata/object.h> +#include "core/ustring.h" -#include "../mono_gd/gd_mono_marshal.h" +NodePath *godot_icall_NodePath_Ctor(MonoString *p_path) { + return memnew(NodePath(GDMonoMarshal::mono_string_to_godot(p_path))); +} + +void godot_icall_NodePath_Dtor(NodePath *p_ptr) { + ERR_FAIL_NULL(p_ptr); + memdelete(p_ptr); +} + +MonoString *godot_icall_NodePath_operator_String(NodePath *p_np) { + return GDMonoMarshal::mono_string_from_godot(p_np->operator String()); +} MonoBoolean godot_icall_NodePath_is_absolute(NodePath *p_ptr) { return (MonoBoolean)p_ptr->is_absolute(); @@ -70,20 +79,18 @@ MonoBoolean godot_icall_NodePath_is_empty(NodePath *p_ptr) { return (MonoBoolean)p_ptr->is_empty(); } -uint32_t godot_icall_RID_get_id(RID *p_ptr) { - return p_ptr->get_id(); -} - -void godot_register_builtin_type_icalls() { - mono_add_internal_call("Godot.NativeCalls::godot_icall_NodePath_get_as_property_path", (void *)godot_icall_NodePath_get_as_property_path); - mono_add_internal_call("Godot.NativeCalls::godot_icall_NodePath_get_concatenated_subnames", (void *)godot_icall_NodePath_get_concatenated_subnames); - mono_add_internal_call("Godot.NativeCalls::godot_icall_NodePath_get_name", (void *)godot_icall_NodePath_get_name); - mono_add_internal_call("Godot.NativeCalls::godot_icall_NodePath_get_name_count", (void *)godot_icall_NodePath_get_name_count); - mono_add_internal_call("Godot.NativeCalls::godot_icall_NodePath_get_subname", (void *)godot_icall_NodePath_get_subname); - mono_add_internal_call("Godot.NativeCalls::godot_icall_NodePath_get_subname_count", (void *)godot_icall_NodePath_get_subname_count); - mono_add_internal_call("Godot.NativeCalls::godot_icall_NodePath_is_absolute", (void *)godot_icall_NodePath_is_absolute); - mono_add_internal_call("Godot.NativeCalls::godot_icall_NodePath_is_empty", (void *)godot_icall_NodePath_is_empty); - mono_add_internal_call("Godot.NativeCalls::godot_icall_RID_get_id", (void *)godot_icall_RID_get_id); +void godot_register_nodepath_icalls() { + mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_Ctor", (void *)godot_icall_NodePath_Ctor); + mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_Dtor", (void *)godot_icall_NodePath_Dtor); + mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_operator_String", (void *)godot_icall_NodePath_operator_String); + mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_get_as_property_path", (void *)godot_icall_NodePath_get_as_property_path); + mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_get_concatenated_subnames", (void *)godot_icall_NodePath_get_concatenated_subnames); + mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_get_name", (void *)godot_icall_NodePath_get_name); + mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_get_name_count", (void *)godot_icall_NodePath_get_name_count); + mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_get_subname", (void *)godot_icall_NodePath_get_subname); + mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_get_subname_count", (void *)godot_icall_NodePath_get_subname_count); + mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_is_absolute", (void *)godot_icall_NodePath_is_absolute); + mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_is_empty", (void *)godot_icall_NodePath_is_empty); } -#endif // BUILTIN_TYPES_GLUE_H +#endif // MONO_GLUE_ENABLED diff --git a/modules/mono/glue/nodepath_glue.h b/modules/mono/glue/nodepath_glue.h new file mode 100644 index 0000000000..92579399a6 --- /dev/null +++ b/modules/mono/glue/nodepath_glue.h @@ -0,0 +1,68 @@ +/*************************************************************************/ +/* nodepath_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 NODEPATH_GLUE_H +#define NODEPATH_GLUE_H + +#ifdef MONO_GLUE_ENABLED + +#include "core/node_path.h" + +#include "../mono_gd/gd_mono_marshal.h" + +NodePath *godot_icall_NodePath_Ctor(MonoString *p_path); + +void godot_icall_NodePath_Dtor(NodePath *p_ptr); + +MonoString *godot_icall_NodePath_operator_String(NodePath *p_np); + +MonoBoolean godot_icall_NodePath_is_absolute(NodePath *p_ptr); + +uint32_t godot_icall_NodePath_get_name_count(NodePath *p_ptr); + +MonoString *godot_icall_NodePath_get_name(NodePath *p_ptr, uint32_t p_idx); + +uint32_t godot_icall_NodePath_get_subname_count(NodePath *p_ptr); + +MonoString *godot_icall_NodePath_get_subname(NodePath *p_ptr, uint32_t p_idx); + +MonoString *godot_icall_NodePath_get_concatenated_subnames(NodePath *p_ptr); + +NodePath *godot_icall_NodePath_get_as_property_path(NodePath *p_ptr); + +MonoBoolean godot_icall_NodePath_is_empty(NodePath *p_ptr); + +// Register internal calls + +void godot_register_nodepath_icalls(); + +#endif // MONO_GLUE_ENABLED + +#endif // NODEPATH_GLUE_H diff --git a/modules/mono/glue/rid_glue.cpp b/modules/mono/glue/rid_glue.cpp new file mode 100644 index 0000000000..6c002b5b9d --- /dev/null +++ b/modules/mono/glue/rid_glue.cpp @@ -0,0 +1,61 @@ +/*************************************************************************/ +/* rid_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 "rid_glue.h" + +#ifdef MONO_GLUE_ENABLED + +#include "core/resource.h" + +RID *godot_icall_RID_Ctor(Object *p_from) { + Resource *res_from = Object::cast_to<Resource>(p_from); + + if (res_from) + return memnew(RID(res_from->get_rid())); + + return memnew(RID); +} + +void godot_icall_RID_Dtor(RID *p_ptr) { + ERR_FAIL_NULL(p_ptr); + memdelete(p_ptr); +} + +uint32_t godot_icall_RID_get_id(RID *p_ptr) { + return p_ptr->get_id(); +} + +void godot_register_rid_icalls() { + mono_add_internal_call("Godot.RID::godot_icall_RID_Ctor", (void *)godot_icall_RID_Ctor); + mono_add_internal_call("Godot.RID::godot_icall_RID_Dtor", (void *)godot_icall_RID_Dtor); + mono_add_internal_call("Godot.RID::godot_icall_RID_get_id", (void *)godot_icall_RID_get_id); +} + +#endif // MONO_GLUE_ENABLED diff --git a/modules/mono/glue/rid_glue.h b/modules/mono/glue/rid_glue.h new file mode 100644 index 0000000000..c725a9b5de --- /dev/null +++ b/modules/mono/glue/rid_glue.h @@ -0,0 +1,53 @@ +/*************************************************************************/ +/* rid_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 RID_GLUE_H +#define RID_GLUE_H + +#ifdef MONO_GLUE_ENABLED + +#include "core/object.h" +#include "core/rid.h" + +#include "../mono_gd/gd_mono_marshal.h" + +RID *godot_icall_RID_Ctor(Object *p_from); + +void godot_icall_RID_Dtor(RID *p_ptr); + +uint32_t godot_icall_RID_get_id(RID *p_ptr); + +// Register internal calls + +void godot_register_rid_icalls(); + +#endif // MONO_GLUE_ENABLED + +#endif // RID_GLUE_H diff --git a/modules/mono/glue/string_glue.cpp b/modules/mono/glue/string_glue.cpp new file mode 100644 index 0000000000..e1a2f1affd --- /dev/null +++ b/modules/mono/glue/string_glue.cpp @@ -0,0 +1,79 @@ +/*************************************************************************/ +/* string_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 "string_glue.h" + +#ifdef MONO_GLUE_ENABLED + +#include "core/ustring.h" +#include "core/variant.h" +#include "core/vector.h" + +MonoArray *godot_icall_String_md5_buffer(MonoString *p_str) { + Vector<uint8_t> ret = GDMonoMarshal::mono_string_to_godot(p_str).md5_buffer(); + // TODO Check possible Array/Vector<uint8_t> problem? + return GDMonoMarshal::Array_to_mono_array(Variant(ret)); +} + +MonoString *godot_icall_String_md5_text(MonoString *p_str) { + String ret = GDMonoMarshal::mono_string_to_godot(p_str).md5_text(); + return GDMonoMarshal::mono_string_from_godot(ret); +} + +int godot_icall_String_rfind(MonoString *p_str, MonoString *p_what, int p_from) { + String what = GDMonoMarshal::mono_string_to_godot(p_what); + return GDMonoMarshal::mono_string_to_godot(p_str).rfind(what, p_from); +} + +int godot_icall_String_rfindn(MonoString *p_str, MonoString *p_what, int p_from) { + String what = GDMonoMarshal::mono_string_to_godot(p_what); + return GDMonoMarshal::mono_string_to_godot(p_str).rfindn(what, p_from); +} + +MonoArray *godot_icall_String_sha256_buffer(MonoString *p_str) { + Vector<uint8_t> ret = GDMonoMarshal::mono_string_to_godot(p_str).sha256_buffer(); + return GDMonoMarshal::Array_to_mono_array(Variant(ret)); +} + +MonoString *godot_icall_String_sha256_text(MonoString *p_str) { + String ret = GDMonoMarshal::mono_string_to_godot(p_str).sha256_text(); + return GDMonoMarshal::mono_string_from_godot(ret); +} + +void godot_register_string_icalls() { + mono_add_internal_call("Godot.String::godot_icall_String_md5_buffer", (void *)godot_icall_String_md5_buffer); + mono_add_internal_call("Godot.String::godot_icall_String_md5_text", (void *)godot_icall_String_md5_text); + mono_add_internal_call("Godot.String::godot_icall_String_rfind", (void *)godot_icall_String_rfind); + mono_add_internal_call("Godot.String::godot_icall_String_rfindn", (void *)godot_icall_String_rfindn); + mono_add_internal_call("Godot.String::godot_icall_String_sha256_buffer", (void *)godot_icall_String_sha256_buffer); + mono_add_internal_call("Godot.String::godot_icall_String_sha256_text", (void *)godot_icall_String_sha256_text); +} + +#endif // MONO_GLUE_ENABLED diff --git a/drivers/unix/tcp_server_posix.h b/modules/mono/glue/string_glue.h index c749314fb3..8b1553e28b 100644 --- a/drivers/unix/tcp_server_posix.h +++ b/modules/mono/glue/string_glue.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* tcp_server_posix.h */ +/* string_glue.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,31 +28,29 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef TCP_SERVER_POSIX_H -#define TCP_SERVER_POSIX_H +#ifndef STRING_GLUE_H +#define STRING_GLUE_H -#ifdef UNIX_ENABLED -#include "core/io/tcp_server.h" +#ifdef MONO_GLUE_ENABLED -class TCPServerPosix : public TCP_Server { +#include "../mono_gd/gd_mono_marshal.h" - int listen_sockfd; - IP::Type sock_type; +MonoArray *godot_icall_String_md5_buffer(MonoString *p_str); - static TCP_Server *_create(); +MonoString *godot_icall_String_md5_text(MonoString *p_str); -public: - virtual Error listen(uint16_t p_port, const IP_Address &p_bind_address = IP_Address("*")); - virtual bool is_connection_available() const; - virtual Ref<StreamPeerTCP> take_connection(); +int godot_icall_String_rfind(MonoString *p_str, MonoString *p_what, int p_from); - virtual void stop(); +int godot_icall_String_rfindn(MonoString *p_str, MonoString *p_what, int p_from); - static void make_default(); +MonoArray *godot_icall_String_sha256_buffer(MonoString *p_str); - TCPServerPosix(); - ~TCPServerPosix(); -}; +MonoString *godot_icall_String_sha256_text(MonoString *p_str); -#endif // TCP_SERVER_POSIX_H -#endif +// Register internal calls + +void godot_register_string_icalls(); + +#endif // MONO_GLUE_ENABLED + +#endif // STRING_GLUE_H diff --git a/modules/mono/godotsharp_defs.h b/modules/mono/godotsharp_defs.h index f604464e8f..39d608de9f 100644 --- a/modules/mono/godotsharp_defs.h +++ b/modules/mono/godotsharp_defs.h @@ -32,6 +32,7 @@ #define GODOTSHARP_DEFS_H #define BINDINGS_NAMESPACE "Godot" +#define BINDINGS_NAMESPACE_COLLECTIONS BINDINGS_NAMESPACE ".Collections" #define BINDINGS_GLOBAL_SCOPE_CLASS "GD" #define BINDINGS_PTR_FIELD "ptr" #define BINDINGS_NATIVE_NAME_FIELD "nativeName" diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp index 92c5cdc5c1..d3fb2cb640 100644 --- a/modules/mono/godotsharp_dirs.cpp +++ b/modules/mono/godotsharp_dirs.cpp @@ -30,13 +30,13 @@ #include "godotsharp_dirs.h" -#include "os/os.h" +#include "core/os/dir_access.h" +#include "core/os/os.h" +#include "core/project_settings.h" #ifdef TOOLS_ENABLED +#include "core/version.h" #include "editor/editor_settings.h" -#include "os/dir_access.h" -#include "project_settings.h" -#include "version.h" #endif namespace GodotSharpDirs { @@ -95,10 +95,18 @@ public: #ifdef TOOLS_ENABLED String mono_solutions_dir; String build_logs_dir; + String sln_filepath; String csproj_filepath; + + String data_mono_bin_dir; + String data_editor_tools_dir; + String data_editor_prebuilt_api_dir; #endif + String data_mono_etc_dir; + String data_mono_lib_dir; + private: _GodotSharpDirs() { res_data_dir = "res://.mono"; @@ -123,10 +131,60 @@ private: name = "UnnamedProject"; } - String base_path = String("res://") + name; + String base_path = ProjectSettings::get_singleton()->globalize_path("res://"); + + sln_filepath = base_path.plus_file(name + ".sln"); + csproj_filepath = base_path.plus_file(name + ".csproj"); +#endif + + String exe_dir = OS::get_singleton()->get_executable_path().get_base_dir(); + +#ifdef TOOLS_ENABLED + + String data_dir_root = exe_dir.plus_file("GodotSharp"); + data_editor_tools_dir = data_dir_root.plus_file("Tools"); + data_editor_prebuilt_api_dir = data_dir_root.plus_file("Api"); + + String data_mono_root_dir = data_dir_root.plus_file("Mono"); + data_mono_bin_dir = data_mono_root_dir.plus_file("bin"); + data_mono_etc_dir = data_mono_root_dir.plus_file("etc"); + data_mono_lib_dir = data_mono_root_dir.plus_file("lib"); + +#ifdef OSX_ENABLED + if (!DirAccess::exists(data_editor_tools_dir)) { + data_editor_tools_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Tools"); + } + + if (!DirAccess::exists(data_editor_prebuilt_api_dir)) { + data_editor_prebuilt_api_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Api"); + } + + if (!DirAccess::exists(data_mono_root_dir)) { + data_mono_bin_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Mono/bin"); + data_mono_etc_dir = exe_dir.plus_file("../Resources/GodotSharp/Mono/etc"); + data_mono_lib_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Mono/lib"); + } +#endif + +#else + + String appname = OS::get_singleton()->get_safe_dir_name(ProjectSettings::get_singleton()->get("application/config/name")); + String data_dir_root = exe_dir.plus_file("data_" + appname); + if (!DirAccess::exists(data_dir_root)) { + data_dir_root = exe_dir.plus_file("data_Godot"); + } + + String data_mono_root_dir = data_dir_root.plus_file("Mono"); + data_mono_etc_dir = data_mono_root_dir.plus_file("etc"); + data_mono_lib_dir = data_mono_root_dir.plus_file("lib"); + +#ifdef OSX_ENABLED + if (!DirAccess::exists(data_mono_root_dir)) { + data_mono_etc_dir = exe_dir.plus_file("../Resources/GodotSharp/Mono/etc"); + data_mono_lib_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Mono/lib"); + } +#endif - sln_filepath = ProjectSettings::get_singleton()->globalize_path(base_path + ".sln"); - csproj_filepath = ProjectSettings::get_singleton()->globalize_path(base_path + ".csproj"); #endif } @@ -192,5 +250,26 @@ String get_project_sln_path() { String get_project_csproj_path() { return _GodotSharpDirs::get_singleton().csproj_filepath; } + +String get_data_mono_bin_dir() { + return _GodotSharpDirs::get_singleton().data_mono_bin_dir; +} + +String get_data_editor_tools_dir() { + return _GodotSharpDirs::get_singleton().data_editor_tools_dir; +} + +String get_data_editor_prebuilt_api_dir() { + return _GodotSharpDirs::get_singleton().data_editor_prebuilt_api_dir; +} #endif + +String get_data_mono_etc_dir() { + return _GodotSharpDirs::get_singleton().data_mono_etc_dir; +} + +String get_data_mono_lib_dir() { + return _GodotSharpDirs::get_singleton().data_mono_lib_dir; +} + } // namespace GodotSharpDirs diff --git a/modules/mono/godotsharp_dirs.h b/modules/mono/godotsharp_dirs.h index e87b5a4150..35b564be30 100644 --- a/modules/mono/godotsharp_dirs.h +++ b/modules/mono/godotsharp_dirs.h @@ -31,7 +31,7 @@ #ifndef GODOTSHARP_DIRS_H #define GODOTSHARP_DIRS_H -#include "ustring.h" +#include "core/ustring.h" namespace GodotSharpDirs { @@ -49,11 +49,18 @@ String get_mono_logs_dir(); #ifdef TOOLS_ENABLED String get_mono_solutions_dir(); String get_build_logs_dir(); -String get_custom_project_settings_dir(); -#endif String get_project_sln_path(); String get_project_csproj_path(); + +String get_data_mono_bin_dir(); +String get_data_editor_tools_dir(); +String get_data_editor_prebuilt_api_dir(); +#endif + +String get_data_mono_etc_dir(); +String get_data_mono_lib_dir(); + } // namespace GodotSharpDirs #endif // GODOTSHARP_DIRS_H diff --git a/modules/mono/mono_gc_handle.cpp b/modules/mono/mono_gc_handle.cpp index 12109045e0..f695bddfeb 100644 --- a/modules/mono/mono_gc_handle.cpp +++ b/modules/mono/mono_gc_handle.cpp @@ -32,24 +32,34 @@ #include "mono_gd/gd_mono.h" -uint32_t MonoGCHandle::make_strong_handle(MonoObject *p_object) { +uint32_t MonoGCHandle::new_strong_handle(MonoObject *p_object) { return mono_gchandle_new(p_object, /* pinned: */ false); } -uint32_t MonoGCHandle::make_weak_handle(MonoObject *p_object) { +uint32_t MonoGCHandle::new_strong_handle_pinned(MonoObject *p_object) { + + return mono_gchandle_new(p_object, /* pinned: */ true); +} + +uint32_t MonoGCHandle::new_weak_handle(MonoObject *p_object) { return mono_gchandle_new_weakref(p_object, /* track_resurrection: */ false); } +void MonoGCHandle::free_handle(uint32_t p_gchandle) { + + mono_gchandle_free(p_gchandle); +} + Ref<MonoGCHandle> MonoGCHandle::create_strong(MonoObject *p_object) { - return memnew(MonoGCHandle(make_strong_handle(p_object))); + return memnew(MonoGCHandle(new_strong_handle(p_object), STRONG_HANDLE)); } Ref<MonoGCHandle> MonoGCHandle::create_weak(MonoObject *p_object) { - return memnew(MonoGCHandle(make_weak_handle(p_object))); + return memnew(MonoGCHandle(new_weak_handle(p_object), WEAK_HANDLE)); } void MonoGCHandle::release() { @@ -59,14 +69,15 @@ void MonoGCHandle::release() { #endif if (!released && GDMono::get_singleton()->is_runtime_initialized()) { - mono_gchandle_free(handle); + free_handle(handle); released = true; } } -MonoGCHandle::MonoGCHandle(uint32_t p_handle) { +MonoGCHandle::MonoGCHandle(uint32_t p_handle, HandleType p_handle_type) { released = false; + weak = p_handle_type == WEAK_HANDLE; handle = p_handle; } diff --git a/modules/mono/mono_gc_handle.h b/modules/mono/mono_gc_handle.h index 9cb3ef0fbb..e5aa04e2b8 100644 --- a/modules/mono/mono_gc_handle.h +++ b/modules/mono/mono_gc_handle.h @@ -33,31 +33,43 @@ #include <mono/jit/jit.h> -#include "reference.h" +#include "core/reference.h" class MonoGCHandle : public Reference { GDCLASS(MonoGCHandle, Reference) bool released; + bool weak; uint32_t handle; public: - static uint32_t make_strong_handle(MonoObject *p_object); - static uint32_t make_weak_handle(MonoObject *p_object); + enum HandleType { + STRONG_HANDLE, + WEAK_HANDLE + }; + + static uint32_t new_strong_handle(MonoObject *p_object); + static uint32_t new_strong_handle_pinned(MonoObject *p_object); + static uint32_t new_weak_handle(MonoObject *p_object); + static void free_handle(uint32_t p_gchandle); static Ref<MonoGCHandle> create_strong(MonoObject *p_object); static Ref<MonoGCHandle> create_weak(MonoObject *p_object); + _FORCE_INLINE_ bool is_released() { return released; } + _FORCE_INLINE_ bool is_weak() { return weak; } + _FORCE_INLINE_ MonoObject *get_target() const { return released ? NULL : mono_gchandle_get_target(handle); } - _FORCE_INLINE_ void set_handle(uint32_t p_handle) { - handle = p_handle; + _FORCE_INLINE_ void set_handle(uint32_t p_handle, HandleType p_handle_type) { released = false; + weak = p_handle_type == WEAK_HANDLE; + handle = p_handle; } void release(); - MonoGCHandle(uint32_t p_handle); + MonoGCHandle(uint32_t p_handle, HandleType p_handle_type); ~MonoGCHandle(); }; diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index fadac941e9..c3feafee28 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -35,13 +35,14 @@ #include <mono/metadata/mono-debug.h> #include <mono/metadata/mono-gc.h> -#include "os/dir_access.h" -#include "os/file_access.h" -#include "os/os.h" -#include "os/thread.h" -#include "project_settings.h" +#include "core/os/dir_access.h" +#include "core/os/file_access.h" +#include "core/os/os.h" +#include "core/os/thread.h" +#include "core/project_settings.h" #include "../csharp_script.h" +#include "../glue/cs_glue_version.gen.h" #include "../godotsharp_dirs.h" #include "../utils/path_utils.h" #include "gd_mono_class.h" @@ -161,26 +162,62 @@ void GDMono::initialize() { mono_trace_set_printerr_handler(gdmono_MonoPrintCallback); #endif + String assembly_rootdir; + String config_dir; + +#ifdef TOOLS_ENABLED #ifdef WINDOWS_ENABLED mono_reg_info = MonoRegUtils::find_mono(); - CharString assembly_dir; - CharString config_dir; - if (mono_reg_info.assembly_dir.length() && DirAccess::exists(mono_reg_info.assembly_dir)) { - assembly_dir = mono_reg_info.assembly_dir.utf8(); + assembly_rootdir = mono_reg_info.assembly_dir; } if (mono_reg_info.config_dir.length() && DirAccess::exists(mono_reg_info.config_dir)) { - config_dir = mono_reg_info.config_dir.utf8(); + config_dir = mono_reg_info.config_dir; + } +#elif OSX_ENABLED + const char *c_assembly_rootdir = mono_assembly_getrootdir(); + const char *c_config_dir = mono_get_config_dir(); + + if (!c_assembly_rootdir || !c_config_dir || !DirAccess::exists(c_assembly_rootdir) || !DirAccess::exists(c_config_dir)) { + Vector<const char *> locations; + locations.push_back("/Library/Frameworks/Mono.framework/Versions/Current/"); + locations.push_back("/usr/local/var/homebrew/linked/mono/"); + + for (int i = 0; i < locations.size(); i++) { + String hint_assembly_rootdir = path_join(locations[i], "lib"); + String hint_mscorlib_path = path_join(hint_assembly_rootdir, "mono", "4.5", "mscorlib.dll"); + String hint_config_dir = path_join(locations[i], "etc"); + + if (FileAccess::exists(hint_mscorlib_path) && DirAccess::exists(hint_config_dir)) { + assembly_rootdir = hint_assembly_rootdir; + config_dir = hint_config_dir; + break; + } + } } +#endif +#endif // TOOLS_ENABLED + + String bundled_assembly_rootdir = GodotSharpDirs::get_data_mono_lib_dir(); + String bundled_config_dir = GodotSharpDirs::get_data_mono_etc_dir(); - mono_set_dirs(assembly_dir.length() ? assembly_dir.get_data() : NULL, - config_dir.length() ? config_dir.get_data() : NULL); +#ifdef TOOLS_ENABLED + if (DirAccess::exists(bundled_assembly_rootdir) && DirAccess::exists(bundled_config_dir)) { + assembly_rootdir = bundled_assembly_rootdir; + config_dir = bundled_config_dir; + } #else - mono_set_dirs(NULL, NULL); + // These are always the directories in export templates + assembly_rootdir = bundled_assembly_rootdir; + config_dir = bundled_config_dir; #endif + // Leak if we call mono_set_dirs more than once + mono_set_dirs(assembly_rootdir.length() ? assembly_rootdir.utf8().get_data() : NULL, + config_dir.length() ? config_dir.utf8().get_data() : NULL); + GDMonoAssembly::initialize(); #ifdef DEBUG_ENABLED @@ -232,16 +269,20 @@ void GDMono::initialize() { _register_internal_calls(); // The following assemblies are not required at initialization -#ifndef MONO_GLUE_DISABLED +#ifdef MONO_GLUE_ENABLED if (_load_api_assemblies()) { - if (!core_api_assembly_out_of_sync && !editor_api_assembly_out_of_sync && GDMonoUtils::mono_cache.godot_api_cache_updated) { - // Everything is fine with the api assemblies, load the project assembly - _load_project_assembly(); - } else { + // Everything is fine with the api assemblies, load the project assembly + _load_project_assembly(); + } else { + if ((core_api_assembly && (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated)) #ifdef TOOLS_ENABLED - // The assembly was successfuly loaded, but the full api could not be cached. - // This is most likely an outdated assembly loaded because of an invalid version in the metadata, - // so we invalidate the version in the metadata and unload the script domain. + || (editor_api_assembly && editor_api_assembly_out_of_sync) +#endif + ) { +#ifdef TOOLS_ENABLED + // The assembly was successfully loaded, but the full api could not be cached. + // This is most likely an outdated assembly loaded because of an invalid version in the + // metadata, so we invalidate the version in the metadata and unload the script domain. if (core_api_assembly_out_of_sync) { ERR_PRINT("The loaded Core API assembly is out of sync"); @@ -265,17 +306,17 @@ void GDMono::initialize() { #else ERR_PRINT("The loaded API assembly is invalid"); CRASH_NOW(); -#endif +#endif // TOOLS_ENABLED } } #else print_verbose("Mono: Glue disabled, ignoring script assemblies."); -#endif +#endif // MONO_GLUE_ENABLED print_verbose("Mono: INITIALIZED"); } -#ifndef MONO_GLUE_DISABLED +#ifdef MONO_GLUE_ENABLED namespace GodotSharpBindings { uint64_t get_core_api_hash(); @@ -283,19 +324,18 @@ uint64_t get_core_api_hash(); uint64_t get_editor_api_hash(); #endif // TOOLS_ENABLED uint32_t get_bindings_version(); -uint32_t get_cs_glue_version(); void register_generated_icalls(); } // namespace GodotSharpBindings #endif void GDMono::_register_internal_calls() { -#ifndef MONO_GLUE_DISABLED +#ifdef MONO_GLUE_ENABLED GodotSharpBindings::register_generated_icalls(); #endif #ifdef TOOLS_ENABLED - GodotSharpBuilds::_register_internal_calls(); + GodotSharpEditor::register_internal_calls(); #endif } @@ -304,7 +344,7 @@ void GDMono::_initialize_and_check_api_hashes() { api_core_hash = ClassDB::get_api_hash(ClassDB::API_CORE); -#ifndef MONO_GLUE_DISABLED +#ifdef MONO_GLUE_ENABLED if (api_core_hash != GodotSharpBindings::get_core_api_hash()) { ERR_PRINT("Mono: Core API hash mismatch!"); } @@ -313,7 +353,7 @@ void GDMono::_initialize_and_check_api_hashes() { #ifdef TOOLS_ENABLED api_editor_hash = ClassDB::get_api_hash(ClassDB::API_EDITOR); -#ifndef MONO_GLUE_DISABLED +#ifdef MONO_GLUE_ENABLED if (api_editor_hash != GodotSharpBindings::get_editor_api_hash()) { ERR_PRINT("Mono: Editor API hash mismatch!"); } @@ -375,6 +415,32 @@ bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMo return true; } +bool GDMono::load_assembly_from(const String &p_name, const String &p_path, GDMonoAssembly **r_assembly, bool p_refonly) { + + CRASH_COND(!r_assembly); + + print_verbose("Mono: Loading assembly " + p_name + (p_refonly ? " (refonly)" : "") + "..."); + + GDMonoAssembly *assembly = GDMonoAssembly::load_from(p_name, p_path, p_refonly); + + if (!assembly) + return false; + +#ifdef DEBUG_ENABLED + uint32_t domain_id = mono_domain_get_id(mono_domain_get()); + GDMonoAssembly **stored_assembly = assemblies[domain_id].getptr(p_name); + + ERR_FAIL_COND_V(stored_assembly == NULL, false); + ERR_FAIL_COND_V(*stored_assembly != assembly, false); +#endif + + *r_assembly = assembly; + + print_verbose("Mono: Assembly " + p_name + (p_refonly ? " (refonly)" : "") + " loaded from path: " + (*r_assembly)->get_path()); + + return true; +} + APIAssembly::Version APIAssembly::Version::get_from_loaded_assembly(GDMonoAssembly *p_api_assembly, APIAssembly::Type p_api_type) { APIAssembly::Version api_assembly_version; @@ -424,20 +490,33 @@ bool GDMono::_load_core_api_assembly() { return true; #ifdef TOOLS_ENABLED - if (metadata_is_api_assembly_invalidated(APIAssembly::API_CORE)) + if (metadata_is_api_assembly_invalidated(APIAssembly::API_CORE)) { + print_verbose("Mono: Skipping loading of Core API assembly because it was invalidated"); return false; + } #endif - bool success = load_assembly(API_ASSEMBLY_NAME, &core_api_assembly); + String assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(API_ASSEMBLY_NAME ".dll"); + + if (!FileAccess::exists(assembly_path)) + return false; + + bool success = load_assembly_from(API_ASSEMBLY_NAME, + assembly_path, + &core_api_assembly); if (success) { -#ifndef MONO_GLUE_DISABLED +#ifdef MONO_GLUE_ENABLED APIAssembly::Version api_assembly_ver = APIAssembly::Version::get_from_loaded_assembly(core_api_assembly, APIAssembly::API_CORE); core_api_assembly_out_of_sync = GodotSharpBindings::get_core_api_hash() != api_assembly_ver.godot_api_hash || GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version || - GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version; -#endif + CS_GLUE_VERSION != api_assembly_ver.cs_glue_version; + if (!core_api_assembly_out_of_sync) { + GDMonoUtils::update_godot_api_cache(); + } +#else GDMonoUtils::update_godot_api_cache(); +#endif } return success; @@ -449,19 +528,26 @@ bool GDMono::_load_editor_api_assembly() { if (editor_api_assembly) return true; -#ifdef TOOLS_ENABLED - if (metadata_is_api_assembly_invalidated(APIAssembly::API_EDITOR)) + if (metadata_is_api_assembly_invalidated(APIAssembly::API_EDITOR)) { + print_verbose("Mono: Skipping loading of Editor API assembly because it was invalidated"); return false; -#endif + } + + String assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"); - bool success = load_assembly(EDITOR_API_ASSEMBLY_NAME, &editor_api_assembly); + if (!FileAccess::exists(assembly_path)) + return false; + + bool success = load_assembly_from(EDITOR_API_ASSEMBLY_NAME, + assembly_path, + &editor_api_assembly); if (success) { -#ifndef MONO_GLUE_DISABLED +#ifdef MONO_GLUE_ENABLED APIAssembly::Version api_assembly_ver = APIAssembly::Version::get_from_loaded_assembly(editor_api_assembly, APIAssembly::API_EDITOR); editor_api_assembly_out_of_sync = GodotSharpBindings::get_editor_api_hash() != api_assembly_ver.godot_api_hash || GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version || - GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version; + CS_GLUE_VERSION != api_assembly_ver.cs_glue_version; #endif } @@ -495,6 +581,8 @@ bool GDMono::_load_project_assembly() { if (success) { mono_assembly_set_main(project_assembly->get_assembly()); + + CSharpLanguage::get_singleton()->project_assembly_loaded(); } else { if (OS::get_singleton()->is_stdout_verbose()) print_error("Mono: Failed to load project assembly"); @@ -509,16 +597,22 @@ bool GDMono::_load_api_assemblies() { if (OS::get_singleton()->is_stdout_verbose()) print_error("Mono: Failed to load Core API assembly"); return false; - } else { + } + + if (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated) + return false; + #ifdef TOOLS_ENABLED - if (!_load_editor_api_assembly()) { - if (OS::get_singleton()->is_stdout_verbose()) - print_error("Mono: Failed to load Editor API assembly"); - return false; - } -#endif + if (!_load_editor_api_assembly()) { + if (OS::get_singleton()->is_stdout_verbose()) + print_error("Mono: Failed to load Editor API assembly"); + return false; } + if (editor_api_assembly_out_of_sync) + return false; +#endif + return true; } @@ -608,15 +702,17 @@ Error GDMono::_unload_scripts_domain() { print_verbose("Mono: Unloading scripts domain..."); - _GodotSharp::get_singleton()->_dispose_callback(); - if (mono_domain_get() != root_domain) mono_domain_set(root_domain, true); mono_gc_collect(mono_gc_max_generation()); finalizing_scripts_domain = true; - mono_domain_finalize(scripts_domain, 2000); + + if (!mono_domain_finalize(scripts_domain, 2000)) { + ERR_PRINT("Mono: Domain finalization timeout"); + } + finalizing_scripts_domain = false; mono_gc_collect(mono_gc_max_generation()); @@ -635,8 +731,6 @@ Error GDMono::_unload_scripts_domain() { MonoDomain *domain = scripts_domain; scripts_domain = NULL; - _GodotSharp::get_singleton()->_dispose_callback(); - MonoException *exc = NULL; mono_domain_try_unload(domain, (MonoObject **)&exc); @@ -684,43 +778,44 @@ Error GDMono::reload_scripts_domain() { return err; } -#ifndef MONO_GLUE_DISABLED +#ifdef MONO_GLUE_ENABLED if (!_load_api_assemblies()) { - return ERR_CANT_OPEN; - } + if ((core_api_assembly && (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated)) || + (editor_api_assembly && editor_api_assembly_out_of_sync)) { + // The assembly was successfully loaded, but the full api could not be cached. + // This is most likely an outdated assembly loaded because of an invalid version in the + // metadata, so we invalidate the version in the metadata and unload the script domain. - if (!core_api_assembly_out_of_sync && !editor_api_assembly_out_of_sync && GDMonoUtils::mono_cache.godot_api_cache_updated) { - // Everything is fine with the api assemblies, load the project assembly - _load_project_assembly(); - } else { - // The assembly was successfuly loaded, but the full api could not be cached. - // This is most likely an outdated assembly loaded because of an invalid version in the metadata, - // so we invalidate the version in the metadata and unload the script domain. - - if (core_api_assembly_out_of_sync) { - metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true); - } else if (!GDMonoUtils::mono_cache.godot_api_cache_updated) { - ERR_PRINT("Core API assembly is in sync, but the cache update failed"); - metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true); - } + if (core_api_assembly_out_of_sync) { + ERR_PRINT("The loaded Core API assembly is out of sync"); + metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true); + } else if (!GDMonoUtils::mono_cache.godot_api_cache_updated) { + ERR_PRINT("The loaded Core API assembly is in sync, but the cache update failed"); + metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true); + } - if (editor_api_assembly_out_of_sync) { - metadata_set_api_assembly_invalidated(APIAssembly::API_EDITOR, true); - } + if (editor_api_assembly_out_of_sync) { + ERR_PRINT("The loaded Editor API assembly is out of sync"); + metadata_set_api_assembly_invalidated(APIAssembly::API_EDITOR, true); + } - Error err = _unload_scripts_domain(); - if (err != OK) { - WARN_PRINT("Mono: Failed to unload scripts domain"); - } + Error err = _unload_scripts_domain(); + if (err != OK) { + WARN_PRINT("Mono: Failed to unload scripts domain"); + } - return ERR_CANT_RESOLVE; + return ERR_CANT_RESOLVE; + } else { + return ERR_CANT_OPEN; + } } - if (!_load_project_assembly()) + if (!_load_project_assembly()) { return ERR_CANT_OPEN; + } #else print_verbose("Mono: Glue disabled, ignoring script assemblies."); -#endif +#endif // MONO_GLUE_ENABLED return OK; } @@ -734,11 +829,13 @@ Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) { print_verbose("Mono: Unloading domain `" + domain_name + "`..."); - if (mono_domain_get() != root_domain) + if (mono_domain_get() == p_domain) mono_domain_set(root_domain, true); mono_gc_collect(mono_gc_max_generation()); - mono_domain_finalize(p_domain, 2000); + if (!mono_domain_finalize(p_domain, 2000)) { + ERR_PRINT("Mono: Domain finalization timeout"); + } mono_gc_collect(mono_gc_max_generation()); _domain_assemblies_cleanup(mono_domain_get_id(p_domain)); @@ -842,7 +939,7 @@ GDMono::GDMono() { GDMono::~GDMono() { - if (runtime_initialized) { + if (is_runtime_initialized()) { if (scripts_domain) { @@ -867,8 +964,9 @@ GDMono::~GDMono() { print_verbose("Mono: Runtime cleanup..."); - runtime_initialized = false; mono_jit_cleanup(root_domain); + + runtime_initialized = false; } if (gdmono_log) @@ -879,51 +977,6 @@ GDMono::~GDMono() { _GodotSharp *_GodotSharp::singleton = NULL; -void _GodotSharp::_dispose_object(Object *p_object) { - - if (p_object->get_script_instance()) { - CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(p_object->get_script_instance()); - if (cs_instance) { - cs_instance->mono_object_disposed(); - return; - } - } - - // Unsafe refcount decrement. The managed instance also counts as a reference. - // See: CSharpLanguage::alloc_instance_binding_data(Object *p_object) - if (Object::cast_to<Reference>(p_object)->unreference()) { - memdelete(p_object); - } -} - -void _GodotSharp::_dispose_callback() { - -#ifndef NO_THREADS - queue_mutex->lock(); -#endif - - for (List<Object *>::Element *E = obj_delete_queue.front(); E; E = E->next()) { - _dispose_object(E->get()); - } - - for (List<NodePath *>::Element *E = np_delete_queue.front(); E; E = E->next()) { - memdelete(E->get()); - } - - for (List<RID *>::Element *E = rid_delete_queue.front(); E; E = E->next()) { - memdelete(E->get()); - } - - obj_delete_queue.clear(); - np_delete_queue.clear(); - rid_delete_queue.clear(); - queue_empty = true; - -#ifndef NO_THREADS - queue_mutex->unlock(); -#endif -} - void _GodotSharp::attach_thread() { GDMonoUtils::attach_current_thread(); @@ -934,81 +987,57 @@ void _GodotSharp::detach_thread() { GDMonoUtils::detach_current_thread(); } -bool _GodotSharp::is_finalizing_domain() { +int32_t _GodotSharp::get_domain_id() { - return GDMono::get_singleton()->is_finalizing_scripts_domain(); + MonoDomain *domain = mono_domain_get(); + CRASH_COND(!domain); // User must check if runtime is initialized before calling this method + return mono_domain_get_id(domain); } -bool _GodotSharp::is_domain_loaded() { +int32_t _GodotSharp::get_scripts_domain_id() { - return GDMono::get_singleton()->get_scripts_domain() != NULL; + MonoDomain *domain = SCRIPTS_DOMAIN; + CRASH_COND(!domain); // User must check if scripts domain is loaded before calling this method + return mono_domain_get_id(domain); } -#define ENQUEUE_FOR_DISPOSAL(m_queue, m_inst) \ - m_queue.push_back(m_inst); \ - if (queue_empty) { \ - queue_empty = false; \ - if (!is_finalizing_domain()) { /* call_deferred may not be safe here */ \ - call_deferred("_dispose_callback"); \ - } \ - } +bool _GodotSharp::is_scripts_domain_loaded() { -void _GodotSharp::queue_dispose(MonoObject *p_mono_object, Object *p_object) { + return GDMono::get_singleton()->is_runtime_initialized() && SCRIPTS_DOMAIN != NULL; +} - if (GDMonoUtils::is_main_thread() && !GDMono::get_singleton()->is_finalizing_scripts_domain()) { - _dispose_object(p_object); - } else { -#ifndef NO_THREADS - queue_mutex->lock(); -#endif +bool _GodotSharp::_is_domain_finalizing_for_unload(int32_t p_domain_id) { - // This is our last chance to invoke notification predelete (this is being called from the finalizer) - // We must use the MonoObject* passed by the finalizer, because the weak GC handle target returns NULL at this point - CSharpInstance *si = CAST_CSHARP_INSTANCE(p_object->get_script_instance()); - if (si) { - si->call_notification_no_check(p_mono_object, Object::NOTIFICATION_PREDELETE); - } + return is_domain_finalizing_for_unload(p_domain_id); +} - ENQUEUE_FOR_DISPOSAL(obj_delete_queue, p_object); +bool _GodotSharp::is_domain_finalizing_for_unload() { -#ifndef NO_THREADS - queue_mutex->unlock(); -#endif - } + return is_domain_finalizing_for_unload(mono_domain_get()); } -void _GodotSharp::queue_dispose(NodePath *p_node_path) { +bool _GodotSharp::is_domain_finalizing_for_unload(int32_t p_domain_id) { - if (GDMonoUtils::is_main_thread() && !GDMono::get_singleton()->is_finalizing_scripts_domain()) { - memdelete(p_node_path); - } else { -#ifndef NO_THREADS - queue_mutex->lock(); -#endif + return is_domain_finalizing_for_unload(mono_domain_get_by_id(p_domain_id)); +} - ENQUEUE_FOR_DISPOSAL(np_delete_queue, p_node_path); +bool _GodotSharp::is_domain_finalizing_for_unload(MonoDomain *p_domain) { -#ifndef NO_THREADS - queue_mutex->unlock(); -#endif - } + if (!p_domain) + return true; + if (p_domain == SCRIPTS_DOMAIN && GDMono::get_singleton()->is_finalizing_scripts_domain()) + return true; + return mono_domain_is_unloading(p_domain); } -void _GodotSharp::queue_dispose(RID *p_rid) { +bool _GodotSharp::is_runtime_shutting_down() { - if (GDMonoUtils::is_main_thread() && !GDMono::get_singleton()->is_finalizing_scripts_domain()) { - memdelete(p_rid); - } else { -#ifndef NO_THREADS - queue_mutex->lock(); -#endif + return mono_runtime_is_shutting_down(); +} - ENQUEUE_FOR_DISPOSAL(rid_delete_queue, p_rid); +bool _GodotSharp::is_runtime_initialized() { -#ifndef NO_THREADS - queue_mutex->unlock(); -#endif - } + return GDMono::get_singleton()->is_runtime_initialized(); } void _GodotSharp::_bind_methods() { @@ -1016,10 +1045,13 @@ void _GodotSharp::_bind_methods() { ClassDB::bind_method(D_METHOD("attach_thread"), &_GodotSharp::attach_thread); ClassDB::bind_method(D_METHOD("detach_thread"), &_GodotSharp::detach_thread); - ClassDB::bind_method(D_METHOD("is_finalizing_domain"), &_GodotSharp::is_finalizing_domain); - ClassDB::bind_method(D_METHOD("is_domain_loaded"), &_GodotSharp::is_domain_loaded); + ClassDB::bind_method(D_METHOD("get_domain_id"), &_GodotSharp::get_domain_id); + ClassDB::bind_method(D_METHOD("get_scripts_domain_id"), &_GodotSharp::get_scripts_domain_id); + ClassDB::bind_method(D_METHOD("is_scripts_domain_loaded"), &_GodotSharp::is_scripts_domain_loaded); + ClassDB::bind_method(D_METHOD("is_domain_finalizing_for_unload", "domain_id"), &_GodotSharp::_is_domain_finalizing_for_unload); - ClassDB::bind_method(D_METHOD("_dispose_callback"), &_GodotSharp::_dispose_callback); + ClassDB::bind_method(D_METHOD("is_runtime_shutting_down"), &_GodotSharp::is_runtime_shutting_down); + ClassDB::bind_method(D_METHOD("is_runtime_initialized"), &_GodotSharp::is_runtime_initialized); } _GodotSharp::_GodotSharp() { diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h index e0ec6ced5e..fd9551b6de 100644 --- a/modules/mono/mono_gd/gd_mono.h +++ b/modules/mono/mono_gd/gd_mono.h @@ -142,7 +142,7 @@ class GDMono { GDMonoLog *gdmono_log; -#ifdef WINDOWS_ENABLED +#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED) MonoRegInfo mono_reg_info; #endif @@ -170,8 +170,9 @@ public: void add_assembly(uint32_t p_domain_id, GDMonoAssembly *p_assembly); GDMonoAssembly **get_loaded_assembly(const String &p_name); - _FORCE_INLINE_ bool is_runtime_initialized() const { return runtime_initialized; } - _FORCE_INLINE_ bool is_finalizing_scripts_domain() const { return finalizing_scripts_domain; } + _FORCE_INLINE_ bool is_runtime_initialized() const { return runtime_initialized && !mono_runtime_is_shutting_down() /* stays true after shutdown finished */; } + + _FORCE_INLINE_ bool is_finalizing_scripts_domain() { return finalizing_scripts_domain; } _FORCE_INLINE_ MonoDomain *get_scripts_domain() { return scripts_domain; } #ifdef TOOLS_ENABLED @@ -186,7 +187,7 @@ public: _FORCE_INLINE_ GDMonoAssembly *get_editor_tools_assembly() const { return editor_tools_assembly; } #endif -#ifdef WINDOWS_ENABLED +#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED) const MonoRegInfo &get_mono_reg_info() { return mono_reg_info; } #endif @@ -198,6 +199,8 @@ public: bool load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly = false); bool load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly = false); + bool load_assembly_from(const String &p_name, const String &p_path, GDMonoAssembly **r_assembly, bool p_refonly = false); + Error finalize_and_unload_domain(MonoDomain *p_domain); void initialize(); @@ -206,12 +209,14 @@ public: ~GDMono(); }; -class GDMonoScopeDomain { +namespace gdmono { + +class ScopeDomain { MonoDomain *prev_domain; public: - GDMonoScopeDomain(MonoDomain *p_domain) { + ScopeDomain(MonoDomain *p_domain) { MonoDomain *prev_domain = mono_domain_get(); if (prev_domain != p_domain) { this->prev_domain = prev_domain; @@ -221,26 +226,43 @@ public: } } - ~GDMonoScopeDomain() { + ~ScopeDomain() { if (prev_domain) mono_domain_set(prev_domain, false); } }; -#define _GDMONO_SCOPE_DOMAIN_(m_mono_domain) \ - GDMonoScopeDomain __gdmono__scope__domain__(m_mono_domain); \ +class ScopeExitDomainUnload { + MonoDomain *domain; + +public: + ScopeExitDomainUnload(MonoDomain *p_domain) : + domain(p_domain) { + } + + ~ScopeExitDomainUnload() { + if (domain) + GDMono::get_singleton()->finalize_and_unload_domain(domain); + } +}; + +} // namespace gdmono + +#define _GDMONO_SCOPE_DOMAIN_(m_mono_domain) \ + gdmono::ScopeDomain __gdmono__scope__domain__(m_mono_domain); \ (void)__gdmono__scope__domain__; +#define _GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(m_mono_domain) \ + gdmono::ScopeExitDomainUnload __gdmono__scope__exit__domain__unload__(m_mono_domain); \ + (void)__gdmono__scope__exit__domain__unload__; + class _GodotSharp : public Object { GDCLASS(_GodotSharp, Object) friend class GDMono; - void _dispose_object(Object *p_object); - - void _dispose_callback(); + bool _is_domain_finalizing_for_unload(int32_t p_domain_id); - List<Object *> obj_delete_queue; List<NodePath *> np_delete_queue; List<RID *> rid_delete_queue; @@ -260,12 +282,17 @@ public: void attach_thread(); void detach_thread(); - bool is_finalizing_domain(); - bool is_domain_loaded(); + int32_t get_domain_id(); + int32_t get_scripts_domain_id(); + + bool is_scripts_domain_loaded(); + + bool is_domain_finalizing_for_unload(); + bool is_domain_finalizing_for_unload(int32_t p_domain_id); + bool is_domain_finalizing_for_unload(MonoDomain *p_domain); - void queue_dispose(MonoObject *p_mono_object, Object *p_object); - void queue_dispose(NodePath *p_node_path); - void queue_dispose(RID *p_rid); + bool is_runtime_shutting_down(); + bool is_runtime_initialized(); _GodotSharp(); ~_GodotSharp(); diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp index 27ce39b6d7..72b0204672 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.cpp +++ b/modules/mono/mono_gd/gd_mono_assembly.cpp @@ -46,6 +46,29 @@ bool GDMonoAssembly::in_preload = false; Vector<String> GDMonoAssembly::search_dirs; +void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config) { + + const char *rootdir = mono_assembly_getrootdir(); + if (rootdir) { + String framework_dir = String(rootdir).plus_file("mono").plus_file("4.5"); + r_search_dirs.push_back(framework_dir); + r_search_dirs.push_back(framework_dir.plus_file("Facades")); + } + + if (p_custom_config.length()) { + r_search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_base_dir().plus_file(p_custom_config)); + } else { + r_search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_dir()); + } + + r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_dir()); + r_search_dirs.push_back(OS::get_singleton()->get_resource_dir()); + r_search_dirs.push_back(OS::get_singleton()->get_executable_path().get_base_dir()); +#ifdef TOOLS_ENABLED + r_search_dirs.push_back(GodotSharpDirs::get_data_editor_tools_dir()); +#endif +} + void GDMonoAssembly::assembly_load_hook(MonoAssembly *assembly, void *user_data) { if (no_search) @@ -93,35 +116,7 @@ MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_d no_search = true; // Avoid the recursion madness - String path; - GDMonoAssembly *res = NULL; - - for (int i = 0; i < search_dirs.size(); i++) { - const String &search_dir = search_dirs[i]; - - if (has_extension) { - path = search_dir.plus_file(name); - if (FileAccess::exists(path)) { - res = _load_assembly_from(name.get_basename(), path, refonly); - if (res != NULL) - break; - } - } else { - path = search_dir.plus_file(name + ".dll"); - if (FileAccess::exists(path)) { - res = _load_assembly_from(name, path, refonly); - if (res != NULL) - break; - } - - path = search_dir.plus_file(name + ".exe"); - if (FileAccess::exists(path)) { - res = _load_assembly_from(name, path, refonly); - if (res != NULL) - break; - } - } - } + GDMonoAssembly *res = _load_assembly_search(name, search_dirs, refonly); no_search = false; @@ -130,31 +125,12 @@ MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_d static _THREAD_LOCAL_(MonoImage *) image_corlib_loading = NULL; -MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data, bool refonly) { +MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **, void *user_data, bool refonly) { (void)user_data; // UNUSED if (search_dirs.empty()) { - search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_dir()); - search_dirs.push_back(GodotSharpDirs::get_res_assemblies_dir()); - search_dirs.push_back(OS::get_singleton()->get_resource_dir()); - search_dirs.push_back(OS::get_singleton()->get_executable_path().get_base_dir()); -#ifdef GD_MONO_EDITOR_ASSEMBLIES_DIR - search_dirs.push_back(OS::get_singleton()->get_executable_path().get_base_dir().plus_file(_MKSTR(GD_MONO_EDITOR_ASSEMBLIES_DIR)).simplify_path()); -#endif - - const char *rootdir = mono_assembly_getrootdir(); - if (rootdir) { - search_dirs.push_back(String(rootdir).plus_file("mono").plus_file("4.5")); - search_dirs.push_back(String(rootdir).plus_file("mono").plus_file("4.5").plus_file("Facades")); - } - - if (assemblies_path) { - while (*assemblies_path) { - search_dirs.push_back(*assemblies_path); - ++assemblies_path; - } - } + fill_search_dirs(search_dirs); } { @@ -188,27 +164,7 @@ MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **asse if (stored_assembly) return (*stored_assembly)->get_assembly(); - String path; - - for (int i = 0; i < search_dirs.size(); i++) { - const String &search_dir = search_dirs[i]; - - if (has_extension) { - path = search_dir.plus_file(name); - if (FileAccess::exists(path)) { - res = _load_assembly_from(name.get_basename(), path, refonly); - if (res != NULL) - break; - } - } else { - path = search_dir.plus_file(name + ".dll"); - if (FileAccess::exists(path)) { - res = _load_assembly_from(name, path, refonly); - if (res != NULL) - break; - } - } - } + res = _load_assembly_search("mscorlib.dll", search_dirs, refonly); } no_search = false; @@ -217,6 +173,43 @@ MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **asse return res ? res->get_assembly() : NULL; } +GDMonoAssembly *GDMonoAssembly::_load_assembly_search(const String &p_name, const Vector<String> &p_search_dirs, bool p_refonly) { + + GDMonoAssembly *res = NULL; + String path; + + bool has_extension = p_name.ends_with(".dll") || p_name.ends_with(".exe"); + + for (int i = 0; i < p_search_dirs.size(); i++) { + const String &search_dir = p_search_dirs[i]; + + if (has_extension) { + path = search_dir.plus_file(p_name); + if (FileAccess::exists(path)) { + res = _load_assembly_from(p_name.get_basename(), path, p_refonly); + if (res != NULL) + return res; + } + } else { + path = search_dir.plus_file(p_name + ".dll"); + if (FileAccess::exists(path)) { + res = _load_assembly_from(p_name, path, p_refonly); + if (res != NULL) + return res; + } + + path = search_dir.plus_file(p_name + ".exe"); + if (FileAccess::exists(path)) { + res = _load_assembly_from(p_name, path, p_refonly); + if (res != NULL) + return res; + } + } + } + + return NULL; +} + GDMonoAssembly *GDMonoAssembly::_load_assembly_from(const String &p_name, const String &p_path, bool p_refonly) { GDMonoAssembly *assembly = memnew(GDMonoAssembly(p_name, p_path)); @@ -469,11 +462,12 @@ GDMonoAssembly *GDMonoAssembly::load_from(const String &p_name, const String &p_ GDMonoAssembly **loaded_asm = GDMono::get_singleton()->get_loaded_assembly(p_name); if (loaded_asm) return *loaded_asm; - +#ifdef DEBUG_ENABLED + CRASH_COND(!FileAccess::exists(p_path)); +#endif no_search = true; GDMonoAssembly *res = _load_assembly_from(p_name, p_path, p_refonly); no_search = false; - return res; } diff --git a/modules/mono/mono_gd/gd_mono_assembly.h b/modules/mono/mono_gd/gd_mono_assembly.h index 2c6d367fc6..2af40ec901 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.h +++ b/modules/mono/mono_gd/gd_mono_assembly.h @@ -34,10 +34,10 @@ #include <mono/jit/jit.h> #include <mono/metadata/assembly.h> +#include "core/hash_map.h" +#include "core/map.h" +#include "core/ustring.h" #include "gd_mono_utils.h" -#include "hash_map.h" -#include "map.h" -#include "ustring.h" class GDMonoAssembly { @@ -102,6 +102,7 @@ class GDMonoAssembly { static MonoAssembly *_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data, bool refonly); static GDMonoAssembly *_load_assembly_from(const String &p_name, const String &p_path, bool p_refonly); + static GDMonoAssembly *_load_assembly_search(const String &p_name, const Vector<String> &p_search_dirs, bool p_refonly); static void _wrap_mono_assembly(MonoAssembly *assembly); friend class GDMono; @@ -125,6 +126,8 @@ public: GDMonoClass *get_object_derived_class(const StringName &p_class); + static void fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config = String()); + static GDMonoAssembly *load_from(const String &p_name, const String &p_path, bool p_refonly); GDMonoAssembly(const String &p_name, const String &p_path = String()); diff --git a/modules/mono/mono_gd/gd_mono_class.h b/modules/mono/mono_gd/gd_mono_class.h index f4e386549a..477305d503 100644 --- a/modules/mono/mono_gd/gd_mono_class.h +++ b/modules/mono/mono_gd/gd_mono_class.h @@ -33,8 +33,8 @@ #include <mono/metadata/debug-helpers.h> -#include "map.h" -#include "ustring.h" +#include "core/map.h" +#include "core/ustring.h" #include "gd_mono_field.h" #include "gd_mono_header.h" diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp index d3a673dc1b..5d9b9213e7 100644 --- a/modules/mono/mono_gd/gd_mono_field.cpp +++ b/modules/mono/mono_gd/gd_mono_field.cpp @@ -40,65 +40,71 @@ void GDMonoField::set_value_raw(MonoObject *p_object, void *p_ptr) { } void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_value) { -#define SET_FROM_STRUCT_AND_BREAK(m_type) \ - { \ - const m_type &val = p_value.operator ::m_type(); \ - MARSHALLED_OUT(m_type, val, raw); \ - mono_field_set_value(p_object, mono_field, raw); \ - break; \ +#define SET_FROM_STRUCT(m_type) \ + { \ + GDMonoMarshal::M_##m_type from = MARSHALLED_OUT(m_type, p_value.operator ::m_type()); \ + mono_field_set_value(p_object, mono_field, &from); \ } -#define SET_FROM_PRIMITIVE(m_type) \ - { \ - m_type val = p_value.operator m_type(); \ - mono_field_set_value(p_object, mono_field, &val); \ - break; \ - } - -#define SET_FROM_ARRAY_AND_BREAK(m_type) \ - { \ - MonoArray *managed = GDMonoMarshal::m_type##_to_mono_array(p_value.operator m_type()); \ - mono_field_set_value(p_object, mono_field, &managed); \ - break; \ +#define SET_FROM_ARRAY(m_type) \ + { \ + MonoArray *managed = GDMonoMarshal::m_type##_to_mono_array(p_value.operator ::m_type()); \ + mono_field_set_value(p_object, mono_field, &managed); \ } switch (type.type_encoding) { case MONO_TYPE_BOOLEAN: { - SET_FROM_PRIMITIVE(bool); + MonoBoolean val = p_value.operator bool(); + mono_field_set_value(p_object, mono_field, &val); + } break; + + case MONO_TYPE_CHAR: { + int16_t val = p_value.operator unsigned short(); + mono_field_set_value(p_object, mono_field, &val); } break; case MONO_TYPE_I1: { - SET_FROM_PRIMITIVE(signed char); + int8_t val = p_value.operator signed char(); + mono_field_set_value(p_object, mono_field, &val); } break; case MONO_TYPE_I2: { - SET_FROM_PRIMITIVE(signed short); + int16_t val = p_value.operator signed short(); + mono_field_set_value(p_object, mono_field, &val); } break; case MONO_TYPE_I4: { - SET_FROM_PRIMITIVE(signed int); + int32_t val = p_value.operator signed int(); + mono_field_set_value(p_object, mono_field, &val); } break; case MONO_TYPE_I8: { - SET_FROM_PRIMITIVE(int64_t); + int64_t val = p_value.operator int64_t(); + mono_field_set_value(p_object, mono_field, &val); } break; case MONO_TYPE_U1: { - SET_FROM_PRIMITIVE(unsigned char); + uint8_t val = p_value.operator unsigned char(); + mono_field_set_value(p_object, mono_field, &val); } break; case MONO_TYPE_U2: { - SET_FROM_PRIMITIVE(unsigned short); + uint16_t val = p_value.operator unsigned short(); + mono_field_set_value(p_object, mono_field, &val); } break; case MONO_TYPE_U4: { - SET_FROM_PRIMITIVE(unsigned int); + uint32_t val = p_value.operator unsigned int(); + mono_field_set_value(p_object, mono_field, &val); } break; case MONO_TYPE_U8: { - SET_FROM_PRIMITIVE(uint64_t); + uint64_t val = p_value.operator uint64_t(); + mono_field_set_value(p_object, mono_field, &val); } break; case MONO_TYPE_R4: { - SET_FROM_PRIMITIVE(float); + float val = p_value.operator float(); + mono_field_set_value(p_object, mono_field, &val); } break; case MONO_TYPE_R8: { - SET_FROM_PRIMITIVE(double); + double val = p_value.operator double(); + mono_field_set_value(p_object, mono_field, &val); } break; case MONO_TYPE_STRING: { @@ -109,38 +115,117 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ case MONO_TYPE_VALUETYPE: { GDMonoClass *tclass = type.type_class; - if (tclass == CACHED_CLASS(Vector2)) - SET_FROM_STRUCT_AND_BREAK(Vector2); + if (tclass == CACHED_CLASS(Vector2)) { + SET_FROM_STRUCT(Vector2); + break; + } + + if (tclass == CACHED_CLASS(Rect2)) { + SET_FROM_STRUCT(Rect2); + break; + } - if (tclass == CACHED_CLASS(Rect2)) - SET_FROM_STRUCT_AND_BREAK(Rect2); + if (tclass == CACHED_CLASS(Transform2D)) { + SET_FROM_STRUCT(Transform2D); + break; + } - if (tclass == CACHED_CLASS(Transform2D)) - SET_FROM_STRUCT_AND_BREAK(Transform2D); + if (tclass == CACHED_CLASS(Vector3)) { + SET_FROM_STRUCT(Vector3); + break; + } - if (tclass == CACHED_CLASS(Vector3)) - SET_FROM_STRUCT_AND_BREAK(Vector3); + if (tclass == CACHED_CLASS(Basis)) { + SET_FROM_STRUCT(Basis); + break; + } - if (tclass == CACHED_CLASS(Basis)) - SET_FROM_STRUCT_AND_BREAK(Basis); + if (tclass == CACHED_CLASS(Quat)) { + SET_FROM_STRUCT(Quat); + break; + } - if (tclass == CACHED_CLASS(Quat)) - SET_FROM_STRUCT_AND_BREAK(Quat); + if (tclass == CACHED_CLASS(Transform)) { + SET_FROM_STRUCT(Transform); + break; + } - if (tclass == CACHED_CLASS(Transform)) - SET_FROM_STRUCT_AND_BREAK(Transform); + if (tclass == CACHED_CLASS(AABB)) { + SET_FROM_STRUCT(AABB); + break; + } - if (tclass == CACHED_CLASS(AABB)) - SET_FROM_STRUCT_AND_BREAK(AABB); + if (tclass == CACHED_CLASS(Color)) { + SET_FROM_STRUCT(Color); + break; + } - if (tclass == CACHED_CLASS(Color)) - SET_FROM_STRUCT_AND_BREAK(Color); + if (tclass == CACHED_CLASS(Plane)) { + SET_FROM_STRUCT(Plane); + break; + } - if (tclass == CACHED_CLASS(Plane)) - SET_FROM_STRUCT_AND_BREAK(Plane); + if (mono_class_is_enum(tclass->get_mono_ptr())) { + MonoType *enum_basetype = mono_class_enum_basetype(tclass->get_mono_ptr()); + switch (mono_type_get_type(enum_basetype)) { + case MONO_TYPE_BOOLEAN: { + MonoBoolean val = p_value.operator bool(); + mono_field_set_value(p_object, mono_field, &val); + break; + } + case MONO_TYPE_CHAR: { + uint16_t val = p_value.operator unsigned short(); + mono_field_set_value(p_object, mono_field, &val); + break; + } + case MONO_TYPE_I1: { + int8_t val = p_value.operator signed char(); + mono_field_set_value(p_object, mono_field, &val); + break; + } + case MONO_TYPE_I2: { + int16_t val = p_value.operator signed short(); + mono_field_set_value(p_object, mono_field, &val); + break; + } + case MONO_TYPE_I4: { + int32_t val = p_value.operator signed int(); + mono_field_set_value(p_object, mono_field, &val); + break; + } + case MONO_TYPE_I8: { + int64_t val = p_value.operator int64_t(); + mono_field_set_value(p_object, mono_field, &val); + break; + } + case MONO_TYPE_U1: { + uint8_t val = p_value.operator unsigned char(); + mono_field_set_value(p_object, mono_field, &val); + break; + } + case MONO_TYPE_U2: { + uint16_t val = p_value.operator unsigned short(); + mono_field_set_value(p_object, mono_field, &val); + break; + } + case MONO_TYPE_U4: { + uint32_t val = p_value.operator unsigned int(); + mono_field_set_value(p_object, mono_field, &val); + break; + } + case MONO_TYPE_U8: { + uint64_t val = p_value.operator uint64_t(); + mono_field_set_value(p_object, mono_field, &val); + break; + } + default: { + ERR_EXPLAIN(String() + "Attempted to convert Variant to a managed enum value of unmarshallable base type."); + ERR_FAIL(); + } + } - if (mono_class_is_enum(tclass->get_mono_ptr())) - SET_FROM_PRIMITIVE(signed int); + break; + } ERR_EXPLAIN(String() + "Attempted to set the value of a field of unmarshallable type: " + tclass->get_name()); ERR_FAIL(); @@ -150,29 +235,45 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ case MONO_TYPE_SZARRAY: { 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); + if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) { + SET_FROM_ARRAY(Array); + break; + } - if (array_type->eklass == CACHED_CLASS_RAW(uint8_t)) - SET_FROM_ARRAY_AND_BREAK(PoolByteArray); + if (array_type->eklass == CACHED_CLASS_RAW(uint8_t)) { + SET_FROM_ARRAY(PoolByteArray); + break; + } - if (array_type->eklass == CACHED_CLASS_RAW(int32_t)) - SET_FROM_ARRAY_AND_BREAK(PoolIntArray); + if (array_type->eklass == CACHED_CLASS_RAW(int32_t)) { + SET_FROM_ARRAY(PoolIntArray); + break; + } - if (array_type->eklass == REAL_T_MONOCLASS) - SET_FROM_ARRAY_AND_BREAK(PoolRealArray); + if (array_type->eklass == REAL_T_MONOCLASS) { + SET_FROM_ARRAY(PoolRealArray); + break; + } - if (array_type->eklass == CACHED_CLASS_RAW(String)) - SET_FROM_ARRAY_AND_BREAK(PoolStringArray); + if (array_type->eklass == CACHED_CLASS_RAW(String)) { + SET_FROM_ARRAY(PoolStringArray); + break; + } - if (array_type->eklass == CACHED_CLASS_RAW(Vector2)) - SET_FROM_ARRAY_AND_BREAK(PoolVector2Array); + if (array_type->eklass == CACHED_CLASS_RAW(Vector2)) { + SET_FROM_ARRAY(PoolVector2Array); + break; + } - if (array_type->eklass == CACHED_CLASS_RAW(Vector3)) - SET_FROM_ARRAY_AND_BREAK(PoolVector3Array); + if (array_type->eklass == CACHED_CLASS_RAW(Vector3)) { + SET_FROM_ARRAY(PoolVector3Array); + break; + } - if (array_type->eklass == CACHED_CLASS_RAW(Color)) - SET_FROM_ARRAY_AND_BREAK(PoolColorArray); + if (array_type->eklass == CACHED_CLASS_RAW(Color)) { + SET_FROM_ARRAY(PoolColorArray); + break; + } ERR_EXPLAIN(String() + "Attempted to convert Variant to a managed array of unmarshallable element type."); ERR_FAIL(); @@ -220,32 +321,56 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ // Variant switch (p_value.get_type()) { case Variant::BOOL: { - SET_FROM_PRIMITIVE(bool); + MonoBoolean val = p_value.operator bool(); + mono_field_set_value(p_object, mono_field, &val); } break; case Variant::INT: { - SET_FROM_PRIMITIVE(int); + int32_t val = p_value.operator signed int(); + mono_field_set_value(p_object, mono_field, &val); } break; case Variant::REAL: { #ifdef REAL_T_IS_DOUBLE - SET_FROM_PRIMITIVE(double); + double val = p_value.operator double(); + mono_field_set_value(p_object, mono_field, &val); #else - SET_FROM_PRIMITIVE(float); + float val = p_value.operator float(); + mono_field_set_value(p_object, mono_field, &val); #endif } break; case Variant::STRING: { MonoString *mono_string = GDMonoMarshal::mono_string_from_godot(p_value); mono_field_set_value(p_object, mono_field, mono_string); } break; - case Variant::VECTOR2: SET_FROM_STRUCT_AND_BREAK(Vector2); - case Variant::RECT2: SET_FROM_STRUCT_AND_BREAK(Rect2); - case Variant::VECTOR3: SET_FROM_STRUCT_AND_BREAK(Vector3); - case Variant::TRANSFORM2D: SET_FROM_STRUCT_AND_BREAK(Transform2D); - case Variant::PLANE: SET_FROM_STRUCT_AND_BREAK(Plane); - case Variant::QUAT: SET_FROM_STRUCT_AND_BREAK(Quat); - case Variant::AABB: SET_FROM_STRUCT_AND_BREAK(AABB); - case Variant::BASIS: SET_FROM_STRUCT_AND_BREAK(Basis); - case Variant::TRANSFORM: SET_FROM_STRUCT_AND_BREAK(Transform); - case Variant::COLOR: SET_FROM_STRUCT_AND_BREAK(Color); + case Variant::VECTOR2: { + SET_FROM_STRUCT(Vector2); + } break; + case Variant::RECT2: { + SET_FROM_STRUCT(Rect2); + } break; + case Variant::VECTOR3: { + SET_FROM_STRUCT(Vector3); + } break; + case Variant::TRANSFORM2D: { + SET_FROM_STRUCT(Transform2D); + } break; + case Variant::PLANE: { + SET_FROM_STRUCT(Plane); + } break; + case Variant::QUAT: { + SET_FROM_STRUCT(Quat); + } break; + case Variant::AABB: { + SET_FROM_STRUCT(AABB); + } break; + case Variant::BASIS: { + SET_FROM_STRUCT(Basis); + } break; + case Variant::TRANSFORM: { + SET_FROM_STRUCT(Transform); + } break; + case Variant::COLOR: { + SET_FROM_STRUCT(Color); + } break; case Variant::NODE_PATH: { MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator NodePath()); mono_field_set_value(p_object, mono_field, managed); @@ -267,14 +392,27 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ 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::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); - case Variant::POOL_STRING_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolStringArray); - case Variant::POOL_VECTOR2_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolVector2Array); - case Variant::POOL_VECTOR3_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolVector3Array); - case Variant::POOL_COLOR_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolColorArray); -#undef SET_FROM_ARRAY_AND_BREAK + case Variant::POOL_BYTE_ARRAY: { + SET_FROM_ARRAY(PoolByteArray); + } break; + case Variant::POOL_INT_ARRAY: { + SET_FROM_ARRAY(PoolIntArray); + } break; + case Variant::POOL_REAL_ARRAY: { + SET_FROM_ARRAY(PoolRealArray); + } break; + case Variant::POOL_STRING_ARRAY: { + SET_FROM_ARRAY(PoolStringArray); + } break; + case Variant::POOL_VECTOR2_ARRAY: { + SET_FROM_ARRAY(PoolVector2Array); + } break; + case Variant::POOL_VECTOR3_ARRAY: { + SET_FROM_ARRAY(PoolVector3Array); + } break; + case Variant::POOL_COLOR_ARRAY: { + SET_FROM_ARRAY(PoolColorArray); + } break; default: break; } } break; @@ -312,8 +450,8 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ } break; } +#undef SET_FROM_ARRAY_AND_BREAK #undef SET_FROM_STRUCT_AND_BREAK -#undef SET_FROM_PRIMITIVE } MonoObject *GDMonoField::get_value(MonoObject *p_object) { diff --git a/modules/mono/mono_gd/gd_mono_header.h b/modules/mono/mono_gd/gd_mono_header.h index 72a5439044..51d0c9b356 100644 --- a/modules/mono/mono_gd/gd_mono_header.h +++ b/modules/mono/mono_gd/gd_mono_header.h @@ -31,7 +31,7 @@ #ifndef GD_MONO_HEADER_H #define GD_MONO_HEADER_H -#include "int_types.h" +#include "core/int_types.h" class GDMonoAssembly; class GDMonoClass; @@ -44,20 +44,15 @@ struct ManagedType { int type_encoding; GDMonoClass *type_class; - ManagedType() { - type_encoding = 0; - type_class = NULL; + ManagedType() : + type_encoding(0), + type_class(NULL) { } -}; - -typedef union { - uint32_t _uint32; - float _float; -} mono_float; -typedef union { - uint64_t _uint64; - float _double; -} mono_double; + ManagedType(int p_type_encoding, GDMonoClass *p_type_class) : + type_encoding(p_type_encoding), + type_class(p_type_class) { + } +}; #endif // GD_MONO_HEADER_H diff --git a/modules/mono/mono_gd/gd_mono_log.cpp b/modules/mono/mono_gd/gd_mono_log.cpp index 5224d309de..b7bb2cb2d6 100644 --- a/modules/mono/mono_gd/gd_mono_log.cpp +++ b/modules/mono/mono_gd/gd_mono_log.cpp @@ -33,8 +33,8 @@ #include <mono/utils/mono-logger.h> #include <stdlib.h> // abort -#include "os/dir_access.h" -#include "os/os.h" +#include "core/os/dir_access.h" +#include "core/os/os.h" #include "../godotsharp_dirs.h" diff --git a/modules/mono/mono_gd/gd_mono_log.h b/modules/mono/mono_gd/gd_mono_log.h index a7e374858c..3b4ff07b7b 100644 --- a/modules/mono/mono_gd/gd_mono_log.h +++ b/modules/mono/mono_gd/gd_mono_log.h @@ -31,7 +31,7 @@ #ifndef GD_MONO_LOG_H #define GD_MONO_LOG_H -#include "os/file_access.h" +#include "core/os/file_access.h" class GDMonoLog { diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp index de91e71bab..2543f5dc47 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.cpp +++ b/modules/mono/mono_gd/gd_mono_marshal.cpp @@ -35,20 +35,6 @@ namespace GDMonoMarshal { -#define RETURN_BOXED_STRUCT(m_t, m_var_in) \ - { \ - const m_t &m_in = m_var_in->operator ::m_t(); \ - MARSHALLED_OUT(m_t, m_in, raw); \ - return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(m_t), raw); \ - } - -#define RETURN_UNBOXED_STRUCT(m_t, m_var_in) \ - { \ - float *raw = (float *)mono_object_unbox(m_var_in); \ - MARSHALLED_IN(m_t, raw, ret); \ - return ret; \ - } - Variant::Type managed_to_variant_type(const ManagedType &p_type) { switch (p_type.type_encoding) { case MONO_TYPE_BOOLEAN: @@ -252,16 +238,21 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty return BOX_BOOLEAN(val); } + case MONO_TYPE_CHAR: { + uint16_t val = p_var->operator unsigned short(); + return BOX_UINT16(val); + } + case MONO_TYPE_I1: { - char val = p_var->operator signed char(); + int8_t val = p_var->operator signed char(); return BOX_INT8(val); } case MONO_TYPE_I2: { - short val = p_var->operator signed short(); + int16_t val = p_var->operator signed short(); return BOX_INT16(val); } case MONO_TYPE_I4: { - int val = p_var->operator signed int(); + int32_t val = p_var->operator signed int(); return BOX_INT32(val); } case MONO_TYPE_I8: { @@ -270,15 +261,15 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty } case MONO_TYPE_U1: { - char val = p_var->operator unsigned char(); + uint8_t val = p_var->operator unsigned char(); return BOX_UINT8(val); } case MONO_TYPE_U2: { - short val = p_var->operator unsigned short(); + uint16_t val = p_var->operator unsigned short(); return BOX_UINT16(val); } case MONO_TYPE_U4: { - int val = p_var->operator unsigned int(); + uint32_t val = p_var->operator unsigned int(); return BOX_UINT32(val); } case MONO_TYPE_U8: { @@ -302,39 +293,105 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty case MONO_TYPE_VALUETYPE: { GDMonoClass *tclass = p_type.type_class; - if (tclass == CACHED_CLASS(Vector2)) - RETURN_BOXED_STRUCT(Vector2, p_var); + if (tclass == CACHED_CLASS(Vector2)) { + GDMonoMarshal::M_Vector2 from = MARSHALLED_OUT(Vector2, p_var->operator ::Vector2()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2), &from); + } - if (tclass == CACHED_CLASS(Rect2)) - RETURN_BOXED_STRUCT(Rect2, p_var); + if (tclass == CACHED_CLASS(Rect2)) { + GDMonoMarshal::M_Rect2 from = MARSHALLED_OUT(Rect2, p_var->operator ::Rect2()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2), &from); + } - if (tclass == CACHED_CLASS(Transform2D)) - RETURN_BOXED_STRUCT(Transform2D, p_var); + if (tclass == CACHED_CLASS(Transform2D)) { + GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_var->operator ::Transform2D()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform2D), &from); + } - if (tclass == CACHED_CLASS(Vector3)) - RETURN_BOXED_STRUCT(Vector3, p_var); + if (tclass == CACHED_CLASS(Vector3)) { + GDMonoMarshal::M_Vector3 from = MARSHALLED_OUT(Vector3, p_var->operator ::Vector3()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3), &from); + } - if (tclass == CACHED_CLASS(Basis)) - RETURN_BOXED_STRUCT(Basis, p_var); + if (tclass == CACHED_CLASS(Basis)) { + GDMonoMarshal::M_Basis from = MARSHALLED_OUT(Basis, p_var->operator ::Basis()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Basis), &from); + } - if (tclass == CACHED_CLASS(Quat)) - RETURN_BOXED_STRUCT(Quat, p_var); + if (tclass == CACHED_CLASS(Quat)) { + GDMonoMarshal::M_Quat from = MARSHALLED_OUT(Quat, p_var->operator ::Quat()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Quat), &from); + } - if (tclass == CACHED_CLASS(Transform)) - RETURN_BOXED_STRUCT(Transform, p_var); + if (tclass == CACHED_CLASS(Transform)) { + GDMonoMarshal::M_Transform from = MARSHALLED_OUT(Transform, p_var->operator ::Transform()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform), &from); + } - if (tclass == CACHED_CLASS(AABB)) - RETURN_BOXED_STRUCT(AABB, p_var); + if (tclass == CACHED_CLASS(AABB)) { + GDMonoMarshal::M_AABB from = MARSHALLED_OUT(AABB, p_var->operator ::AABB()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(AABB), &from); + } - if (tclass == CACHED_CLASS(Color)) - RETURN_BOXED_STRUCT(Color, p_var); + if (tclass == CACHED_CLASS(Color)) { + GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_var->operator ::Color()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Color), &from); + } - if (tclass == CACHED_CLASS(Plane)) - RETURN_BOXED_STRUCT(Plane, p_var); + if (tclass == CACHED_CLASS(Plane)) { + GDMonoMarshal::M_Plane from = MARSHALLED_OUT(Plane, p_var->operator ::Plane()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Plane), &from); + } if (mono_class_is_enum(tclass->get_mono_ptr())) { - int val = p_var->operator signed int(); - return BOX_ENUM(tclass->get_mono_ptr(), val); + MonoType *enum_basetype = mono_class_enum_basetype(tclass->get_mono_ptr()); + MonoClass *enum_baseclass = mono_class_from_mono_type(enum_basetype); + switch (mono_type_get_type(enum_basetype)) { + case MONO_TYPE_BOOLEAN: { + MonoBoolean val = p_var->operator bool(); + return BOX_ENUM(enum_baseclass, val); + } + case MONO_TYPE_CHAR: { + uint16_t val = p_var->operator unsigned short(); + return BOX_ENUM(enum_baseclass, val); + } + case MONO_TYPE_I1: { + int8_t val = p_var->operator signed char(); + return BOX_ENUM(enum_baseclass, val); + } + case MONO_TYPE_I2: { + int16_t val = p_var->operator signed short(); + return BOX_ENUM(enum_baseclass, val); + } + case MONO_TYPE_I4: { + int32_t val = p_var->operator signed int(); + return BOX_ENUM(enum_baseclass, val); + } + case MONO_TYPE_I8: { + int64_t val = p_var->operator int64_t(); + return BOX_ENUM(enum_baseclass, val); + } + case MONO_TYPE_U1: { + uint8_t val = p_var->operator unsigned char(); + return BOX_ENUM(enum_baseclass, val); + } + case MONO_TYPE_U2: { + uint16_t val = p_var->operator unsigned short(); + return BOX_ENUM(enum_baseclass, val); + } + case MONO_TYPE_U4: { + uint32_t val = p_var->operator unsigned int(); + return BOX_ENUM(enum_baseclass, val); + } + case MONO_TYPE_U8: { + uint64_t val = p_var->operator uint64_t(); + return BOX_ENUM(enum_baseclass, val); + } + default: { + ERR_EXPLAIN(String() + "Attempted to convert Variant to a managed enum value of unmarshallable base type."); + ERR_FAIL_V(NULL); + } + } } } break; @@ -402,7 +459,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty return BOX_BOOLEAN(val); } case Variant::INT: { - int val = p_var->operator signed int(); + int32_t val = p_var->operator signed int(); return BOX_INT32(val); } case Variant::REAL: { @@ -416,33 +473,52 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty } case Variant::STRING: return (MonoObject *)mono_string_from_godot(p_var->operator String()); - case Variant::VECTOR2: - RETURN_BOXED_STRUCT(Vector2, p_var); - case Variant::RECT2: - RETURN_BOXED_STRUCT(Rect2, p_var); - case Variant::VECTOR3: - RETURN_BOXED_STRUCT(Vector3, p_var); - case Variant::TRANSFORM2D: - RETURN_BOXED_STRUCT(Transform2D, p_var); - case Variant::PLANE: - RETURN_BOXED_STRUCT(Plane, p_var); - case Variant::QUAT: - RETURN_BOXED_STRUCT(Quat, p_var); - case Variant::AABB: - RETURN_BOXED_STRUCT(AABB, p_var); - case Variant::BASIS: - RETURN_BOXED_STRUCT(Basis, p_var); - case Variant::TRANSFORM: - RETURN_BOXED_STRUCT(Transform, p_var); - case Variant::COLOR: - RETURN_BOXED_STRUCT(Color, p_var); + case Variant::VECTOR2: { + GDMonoMarshal::M_Vector2 from = MARSHALLED_OUT(Vector2, p_var->operator ::Vector2()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2), &from); + } + case Variant::RECT2: { + GDMonoMarshal::M_Rect2 from = MARSHALLED_OUT(Rect2, p_var->operator ::Rect2()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2), &from); + } + case Variant::VECTOR3: { + GDMonoMarshal::M_Vector3 from = MARSHALLED_OUT(Vector3, p_var->operator ::Vector3()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3), &from); + } + case Variant::TRANSFORM2D: { + GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_var->operator ::Transform2D()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform2D), &from); + } + case Variant::PLANE: { + GDMonoMarshal::M_Plane from = MARSHALLED_OUT(Plane, p_var->operator ::Plane()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Plane), &from); + } + case Variant::QUAT: { + GDMonoMarshal::M_Quat from = MARSHALLED_OUT(Quat, p_var->operator ::Quat()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Quat), &from); + } + case Variant::AABB: { + GDMonoMarshal::M_AABB from = MARSHALLED_OUT(AABB, p_var->operator ::AABB()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(AABB), &from); + } + case Variant::BASIS: { + GDMonoMarshal::M_Basis from = MARSHALLED_OUT(Basis, p_var->operator ::Basis()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Basis), &from); + } + case Variant::TRANSFORM: { + GDMonoMarshal::M_Transform from = MARSHALLED_OUT(Transform, p_var->operator ::Transform()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform), &from); + } + case Variant::COLOR: { + GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_var->operator ::Color()); + return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Color), &from); + } case Variant::NODE_PATH: return GDMonoUtils::create_managed_from(p_var->operator NodePath()); case Variant::_RID: return GDMonoUtils::create_managed_from(p_var->operator RID()); - case Variant::OBJECT: { + case Variant::OBJECT: return GDMonoUtils::unmanaged_get_managed(p_var->operator Object *()); - } case Variant::DICTIONARY: return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary)); case Variant::ARRAY: @@ -512,6 +588,9 @@ Variant mono_object_to_variant(MonoObject *p_obj) { case MONO_TYPE_BOOLEAN: return (bool)unbox<MonoBoolean>(p_obj); + case MONO_TYPE_CHAR: + return unbox<uint16_t>(p_obj); + case MONO_TYPE_I1: return unbox<int8_t>(p_obj); case MONO_TYPE_I2: @@ -545,34 +624,34 @@ Variant mono_object_to_variant(MonoObject *p_obj) { GDMonoClass *tclass = type.type_class; if (tclass == CACHED_CLASS(Vector2)) - RETURN_UNBOXED_STRUCT(Vector2, p_obj); + return MARSHALLED_IN(Vector2, (GDMonoMarshal::M_Vector2 *)mono_object_unbox(p_obj)); if (tclass == CACHED_CLASS(Rect2)) - RETURN_UNBOXED_STRUCT(Rect2, p_obj); + return MARSHALLED_IN(Rect2, (GDMonoMarshal::M_Rect2 *)mono_object_unbox(p_obj)); if (tclass == CACHED_CLASS(Transform2D)) - RETURN_UNBOXED_STRUCT(Transform2D, p_obj); + return MARSHALLED_IN(Transform2D, (GDMonoMarshal::M_Transform2D *)mono_object_unbox(p_obj)); if (tclass == CACHED_CLASS(Vector3)) - RETURN_UNBOXED_STRUCT(Vector3, p_obj); + return MARSHALLED_IN(Vector3, (GDMonoMarshal::M_Vector3 *)mono_object_unbox(p_obj)); if (tclass == CACHED_CLASS(Basis)) - RETURN_UNBOXED_STRUCT(Basis, p_obj); + return MARSHALLED_IN(Basis, (GDMonoMarshal::M_Basis *)mono_object_unbox(p_obj)); if (tclass == CACHED_CLASS(Quat)) - RETURN_UNBOXED_STRUCT(Quat, p_obj); + return MARSHALLED_IN(Quat, (GDMonoMarshal::M_Quat *)mono_object_unbox(p_obj)); if (tclass == CACHED_CLASS(Transform)) - RETURN_UNBOXED_STRUCT(Transform, p_obj); + return MARSHALLED_IN(Transform, (GDMonoMarshal::M_Transform *)mono_object_unbox(p_obj)); if (tclass == CACHED_CLASS(AABB)) - RETURN_UNBOXED_STRUCT(AABB, p_obj); + return MARSHALLED_IN(AABB, (GDMonoMarshal::M_AABB *)mono_object_unbox(p_obj)); if (tclass == CACHED_CLASS(Color)) - RETURN_UNBOXED_STRUCT(Color, p_obj); + return MARSHALLED_IN(Color, (GDMonoMarshal::M_Color *)mono_object_unbox(p_obj)); if (tclass == CACHED_CLASS(Plane)) - RETURN_UNBOXED_STRUCT(Plane, p_obj); + return MARSHALLED_IN(Plane, (GDMonoMarshal::M_Plane *)mono_object_unbox(p_obj)); if (mono_class_is_enum(tclass->get_mono_ptr())) return unbox<int32_t>(p_obj); @@ -708,13 +787,13 @@ Array mono_array_to_Array(MonoArray *p_array) { return ret; } -// TODO Optimize reading/writing from/to PoolArrays - MonoArray *PoolIntArray_to_mono_array(const PoolIntArray &p_array) { + PoolIntArray::Read r = p_array.read(); + MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(int32_t), p_array.size()); for (int i = 0; i < p_array.size(); i++) { - mono_array_set(ret, int32_t, i, p_array[i]); + mono_array_set(ret, int32_t, i, r[i]); } return ret; @@ -726,19 +805,22 @@ PoolIntArray mono_array_to_PoolIntArray(MonoArray *p_array) { return ret; int length = mono_array_length(p_array); ret.resize(length); + PoolIntArray::Write w = ret.write(); + for (int i = 0; i < length; i++) { - int32_t elem = mono_array_get(p_array, int32_t, i); - ret.set(i, elem); + w[i] = mono_array_get(p_array, int32_t, i); } return ret; } MonoArray *PoolByteArray_to_mono_array(const PoolByteArray &p_array) { + PoolByteArray::Read r = p_array.read(); + MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(uint8_t), p_array.size()); for (int i = 0; i < p_array.size(); i++) { - mono_array_set(ret, uint8_t, i, p_array[i]); + mono_array_set(ret, uint8_t, i, r[i]); } return ret; @@ -750,20 +832,22 @@ PoolByteArray mono_array_to_PoolByteArray(MonoArray *p_array) { return ret; int length = mono_array_length(p_array); ret.resize(length); + PoolByteArray::Write w = ret.write(); for (int i = 0; i < length; i++) { - uint8_t elem = mono_array_get(p_array, uint8_t, i); - ret.set(i, elem); + w[i] = mono_array_get(p_array, uint8_t, i); } return ret; } MonoArray *PoolRealArray_to_mono_array(const PoolRealArray &p_array) { + PoolRealArray::Read r = p_array.read(); + MonoArray *ret = mono_array_new(mono_domain_get(), REAL_T_MONOCLASS, p_array.size()); for (int i = 0; i < p_array.size(); i++) { - mono_array_set(ret, real_t, i, p_array[i]); + mono_array_set(ret, real_t, i, r[i]); } return ret; @@ -775,20 +859,22 @@ PoolRealArray mono_array_to_PoolRealArray(MonoArray *p_array) { return ret; int length = mono_array_length(p_array); ret.resize(length); + PoolRealArray::Write w = ret.write(); for (int i = 0; i < length; i++) { - real_t elem = mono_array_get(p_array, real_t, i); - ret.set(i, elem); + w[i] = mono_array_get(p_array, real_t, i); } return ret; } MonoArray *PoolStringArray_to_mono_array(const PoolStringArray &p_array) { + PoolStringArray::Read r = p_array.read(); + MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(String), p_array.size()); for (int i = 0; i < p_array.size(); i++) { - MonoString *boxed = mono_string_from_godot(p_array[i]); + MonoString *boxed = mono_string_from_godot(r[i]); mono_array_set(ret, MonoString *, i, boxed); } @@ -801,29 +887,24 @@ PoolStringArray mono_array_to_PoolStringArray(MonoArray *p_array) { return ret; int length = mono_array_length(p_array); ret.resize(length); + PoolStringArray::Write w = ret.write(); for (int i = 0; i < length; i++) { MonoString *elem = mono_array_get(p_array, MonoString *, i); - ret.set(i, mono_string_to_godot(elem)); + w[i] = mono_string_to_godot(elem); } return ret; } MonoArray *PoolColorArray_to_mono_array(const PoolColorArray &p_array) { + PoolColorArray::Read r = p_array.read(); + MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Color), p_array.size()); for (int i = 0; i < p_array.size(); i++) { -#ifdef YOLOCOPY - mono_array_set(ret, Color, i, p_array[i]); -#else - real_t *raw = (real_t *)mono_array_addr_with_size(ret, sizeof(real_t) * 4, i); - const Color &elem = p_array[i]; - raw[0] = elem.r; - raw[1] = elem.g; - raw[2] = elem.b; - raw[3] = elem.a; -#endif + M_Color *raw = (M_Color *)mono_array_addr_with_size(ret, sizeof(M_Color), i); + *raw = MARSHALLED_OUT(Color, r[i]); } return ret; @@ -835,28 +916,23 @@ PoolColorArray mono_array_to_PoolColorArray(MonoArray *p_array) { return ret; int length = mono_array_length(p_array); ret.resize(length); + PoolColorArray::Write w = ret.write(); for (int i = 0; i < length; i++) { - real_t *raw_elem = (real_t *)mono_array_addr_with_size(p_array, sizeof(real_t) * 4, i); - MARSHALLED_IN(Color, raw_elem, elem); - ret.set(i, elem); + w[i] = MARSHALLED_IN(Color, (M_Color *)mono_array_addr_with_size(p_array, sizeof(M_Color), i)); } return ret; } MonoArray *PoolVector2Array_to_mono_array(const PoolVector2Array &p_array) { + PoolVector2Array::Read r = p_array.read(); + MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector2), p_array.size()); for (int i = 0; i < p_array.size(); i++) { -#ifdef YOLOCOPY - mono_array_set(ret, Vector2, i, p_array[i]); -#else - real_t *raw = (real_t *)mono_array_addr_with_size(ret, sizeof(real_t) * 2, i); - const Vector2 &elem = p_array[i]; - raw[0] = elem.x; - raw[1] = elem.y; -#endif + M_Vector2 *raw = (M_Vector2 *)mono_array_addr_with_size(ret, sizeof(M_Vector2), i); + *raw = MARSHALLED_OUT(Vector2, r[i]); } return ret; @@ -868,29 +944,23 @@ PoolVector2Array mono_array_to_PoolVector2Array(MonoArray *p_array) { return ret; int length = mono_array_length(p_array); ret.resize(length); + PoolVector2Array::Write w = ret.write(); for (int i = 0; i < length; i++) { - real_t *raw_elem = (real_t *)mono_array_addr_with_size(p_array, sizeof(real_t) * 2, i); - MARSHALLED_IN(Vector2, raw_elem, elem); - ret.set(i, elem); + w[i] = MARSHALLED_IN(Vector2, (M_Vector2 *)mono_array_addr_with_size(p_array, sizeof(M_Vector2), i)); } return ret; } MonoArray *PoolVector3Array_to_mono_array(const PoolVector3Array &p_array) { + PoolVector3Array::Read r = p_array.read(); + MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector3), p_array.size()); for (int i = 0; i < p_array.size(); i++) { -#ifdef YOLOCOPY - mono_array_set(ret, Vector3, i, p_array[i]); -#else - real_t *raw = (real_t *)mono_array_addr_with_size(ret, sizeof(real_t) * 3, i); - const Vector3 &elem = p_array[i]; - raw[0] = elem.x; - raw[1] = elem.y; - raw[2] = elem.z; -#endif + M_Vector3 *raw = (M_Vector3 *)mono_array_addr_with_size(ret, sizeof(M_Vector3), i); + *raw = MARSHALLED_OUT(Vector3, r[i]); } return ret; @@ -902,11 +972,10 @@ PoolVector3Array mono_array_to_PoolVector3Array(MonoArray *p_array) { return ret; int length = mono_array_length(p_array); ret.resize(length); + PoolVector3Array::Write w = ret.write(); for (int i = 0; i < length; i++) { - real_t *raw_elem = (real_t *)mono_array_addr_with_size(p_array, sizeof(real_t) * 3, i); - MARSHALLED_IN(Vector3, raw_elem, elem); - ret.set(i, elem); + w[i] = MARSHALLED_IN(Vector3, (M_Vector3 *)mono_array_addr_with_size(p_array, sizeof(M_Vector3), i)); } return ret; diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h index 464f584a0a..4002f2a225 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.h +++ b/modules/mono/mono_gd/gd_mono_marshal.h @@ -31,9 +31,9 @@ #ifndef GDMONOMARSHAL_H #define GDMONOMARSHAL_H +#include "core/variant.h" #include "gd_mono.h" #include "gd_mono_utils.h" -#include "variant.h" namespace GDMonoMarshal { @@ -97,10 +97,14 @@ _FORCE_INLINE_ MonoString *mono_string_from_godot(const String &p_string) { MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_type); MonoObject *variant_to_mono_object(const Variant *p_var); -_FORCE_INLINE_ MonoObject *variant_to_mono_object(Variant p_var) { +_FORCE_INLINE_ MonoObject *variant_to_mono_object(const Variant &p_var) { return variant_to_mono_object(&p_var); } +_FORCE_INLINE_ MonoObject *variant_to_mono_object(const Variant &p_var, const ManagedType &p_type) { + return variant_to_mono_object(&p_var, p_type); +} + Variant mono_object_to_variant(MonoObject *p_obj); // Array @@ -143,78 +147,271 @@ 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); -#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); +// Structures + +namespace InteropLayout { + +enum { + MATCHES_float = (sizeof(float) == sizeof(uint32_t)), + + MATCHES_double = (sizeof(double) == sizeof(uint64_t)), + +#ifdef REAL_T_IS_DOUBLE + MATCHES_real_t = (sizeof(real_t) == sizeof(uint64_t)), #else + MATCHES_real_t = (sizeof(real_t) == sizeof(uint32_t)), +#endif + + MATCHES_Vector2 = (MATCHES_real_t && (sizeof(Vector2) == (sizeof(real_t) * 2)) && + offsetof(Vector2, x) == (sizeof(real_t) * 0) && + offsetof(Vector2, y) == (sizeof(real_t) * 1)), + + MATCHES_Rect2 = (MATCHES_Vector2 && (sizeof(Rect2) == (sizeof(Vector2) * 2)) && + offsetof(Rect2, position) == (sizeof(Vector2) * 0) && + offsetof(Rect2, size) == (sizeof(Vector2) * 1)), -// Expects m_in to be of type float* + MATCHES_Transform2D = (MATCHES_Vector2 && (sizeof(Transform2D) == (sizeof(Vector2) * 3))), // No field offset required, it stores an array -#define MARSHALLED_OUT(m_t, m_in, m_out) MARSHALLED_OUT_##m_t(m_in, m_out) -#define MARSHALLED_IN(m_t, m_in, m_out) MARSHALLED_IN_##m_t(m_in, m_out) + MATCHES_Vector3 = (MATCHES_real_t && (sizeof(Vector3) == (sizeof(real_t) * 3)) && + offsetof(Vector3, x) == (sizeof(real_t) * 0) && + offsetof(Vector3, y) == (sizeof(real_t) * 1) && + offsetof(Vector3, z) == (sizeof(real_t) * 2)), -// Vector2 + MATCHES_Basis = (MATCHES_Vector3 && (sizeof(Basis) == (sizeof(Vector3) * 3))), // No field offset required, it stores an array -#define MARSHALLED_OUT_Vector2(m_in, m_out) real_t m_out[2] = { m_in.x, m_in.y }; -#define MARSHALLED_IN_Vector2(m_in, m_out) Vector2 m_out(m_in[0], m_in[1]); + MATCHES_Quat = (MATCHES_real_t && (sizeof(Quat) == (sizeof(real_t) * 4)) && + offsetof(Quat, x) == (sizeof(real_t) * 0) && + offsetof(Quat, y) == (sizeof(real_t) * 1) && + offsetof(Quat, z) == (sizeof(real_t) * 2) && + offsetof(Quat, w) == (sizeof(real_t) * 3)), -// Rect2 + MATCHES_Transform = (MATCHES_Basis && MATCHES_Vector3 && (sizeof(Transform) == (sizeof(Basis) + sizeof(Vector3))) && + offsetof(Transform, basis) == 0 && + offsetof(Transform, origin) == sizeof(Basis)), -#define MARSHALLED_OUT_Rect2(m_in, m_out) real_t m_out[4] = { m_in.position.x, m_in.position.y, m_in.size.width, m_in.size.height }; -#define MARSHALLED_IN_Rect2(m_in, m_out) Rect2 m_out(m_in[0], m_in[1], m_in[2], m_in[3]); + MATCHES_AABB = (MATCHES_Vector3 && (sizeof(AABB) == (sizeof(Vector3) * 2)) && + offsetof(AABB, position) == (sizeof(Vector3) * 0) && + offsetof(AABB, size) == (sizeof(Vector3) * 1)), -// Transform2D + MATCHES_Color = (MATCHES_float && (sizeof(Color) == (sizeof(float) * 4)) && + offsetof(Color, r) == (sizeof(float) * 0) && + offsetof(Color, g) == (sizeof(float) * 1) && + offsetof(Color, b) == (sizeof(float) * 2) && + offsetof(Color, a) == (sizeof(float) * 3)), -#define MARSHALLED_OUT_Transform2D(m_in, m_out) real_t m_out[6] = { m_in[0].x, m_in[0].y, m_in[1].x, m_in[1].y, m_in[2].x, m_in[2].y }; -#define MARSHALLED_IN_Transform2D(m_in, m_out) Transform2D m_out(m_in[0], m_in[1], m_in[2], m_in[3], m_in[4], m_in[5]); + MATCHES_Plane = (MATCHES_Vector3 && MATCHES_real_t && (sizeof(Plane) == (sizeof(Vector3) + sizeof(real_t))) && + offsetof(Plane, normal) == 0 && + offsetof(Plane, d) == sizeof(Vector3)) +}; + +// In the future we may force this if we want to ref return these structs +#ifdef GD_MONO_FORCE_INTEROP_STRUCT_COPY +// Sometimes clang-format can be an ass +GD_STATIC_ASSERT(MATCHES_Vector2 &&MATCHES_Rect2 &&MATCHES_Transform2D &&MATCHES_Vector3 && + MATCHES_Basis &&MATCHES_Quat &&MATCHES_Transform &&MATCHES_AABB &&MATCHES_Color &&MATCHES_Plane); +#endif -// Vector3 +} // namespace InteropLayout -#define MARSHALLED_OUT_Vector3(m_in, m_out) real_t m_out[3] = { m_in.x, m_in.y, m_in.z }; -#define MARSHALLED_IN_Vector3(m_in, m_out) Vector3 m_out(m_in[0], m_in[1], m_in[2]); +#pragma pack(push, 1) -// Basis +struct M_Vector2 { + real_t x, y; -#define MARSHALLED_OUT_Basis(m_in, m_out) real_t m_out[9] = { \ - m_in[0].x, m_in[0].y, m_in[0].z, \ - m_in[1].x, m_in[1].y, m_in[1].z, \ - m_in[2].x, m_in[2].y, m_in[2].z \ + static _FORCE_INLINE_ Vector2 convert_to(const M_Vector2 &p_from) { + return Vector2(p_from.x, p_from.y); + } + + static _FORCE_INLINE_ M_Vector2 convert_from(const Vector2 &p_from) { + M_Vector2 ret = { p_from.x, p_from.y }; + return ret; + } }; -#define MARSHALLED_IN_Basis(m_in, m_out) Basis m_out(m_in[0], m_in[1], m_in[2], m_in[3], m_in[4], m_in[5], m_in[6], m_in[7], m_in[8]); -// Quat +struct M_Rect2 { + M_Vector2 position; + M_Vector2 size; -#define MARSHALLED_OUT_Quat(m_in, m_out) real_t m_out[4] = { m_in.x, m_in.y, m_in.z, m_in.w }; -#define MARSHALLED_IN_Quat(m_in, m_out) Quat m_out(m_in[0], m_in[1], m_in[2], m_in[3]); + static _FORCE_INLINE_ Rect2 convert_to(const M_Rect2 &p_from) { + return Rect2(M_Vector2::convert_to(p_from.position), + M_Vector2::convert_to(p_from.size)); + } -// Transform + static _FORCE_INLINE_ M_Rect2 convert_from(const Rect2 &p_from) { + M_Rect2 ret = { M_Vector2::convert_from(p_from.position), M_Vector2::convert_from(p_from.size) }; + return ret; + } +}; -#define MARSHALLED_OUT_Transform(m_in, m_out) real_t m_out[12] = { \ - m_in.basis[0].x, m_in.basis[0].y, m_in.basis[0].z, \ - m_in.basis[1].x, m_in.basis[1].y, m_in.basis[1].z, \ - m_in.basis[2].x, m_in.basis[2].y, m_in.basis[2].z, \ - m_in.origin.x, m_in.origin.y, m_in.origin.z \ +struct M_Transform2D { + M_Vector2 elements[3]; + + static _FORCE_INLINE_ Transform2D convert_to(const M_Transform2D &p_from) { + return Transform2D(p_from.elements[0].x, p_from.elements[0].y, + p_from.elements[1].x, p_from.elements[1].y, + p_from.elements[2].x, p_from.elements[2].y); + } + + static _FORCE_INLINE_ M_Transform2D convert_from(const Transform2D &p_from) { + M_Transform2D ret = { + M_Vector2::convert_from(p_from.elements[0]), + M_Vector2::convert_from(p_from.elements[1]), + M_Vector2::convert_from(p_from.elements[2]) + }; + return ret; + } }; -#define MARSHALLED_IN_Transform(m_in, m_out) Transform m_out( \ - Basis(m_in[0], m_in[1], m_in[2], m_in[3], m_in[4], m_in[5], m_in[6], m_in[7], m_in[8]), \ - Vector3(m_in[9], m_in[10], m_in[11])); -// AABB +struct M_Vector3 { + real_t x, y, z; -#define MARSHALLED_OUT_AABB(m_in, m_out) real_t m_out[6] = { m_in.position.x, m_in.position.y, m_in.position.z, m_in.size.x, m_in.size.y, m_in.size.z }; -#define MARSHALLED_IN_AABB(m_in, m_out) AABB m_out(Vector3(m_in[0], m_in[1], m_in[2]), Vector3(m_in[3], m_in[4], m_in[5])); + static _FORCE_INLINE_ Vector3 convert_to(const M_Vector3 &p_from) { + return Vector3(p_from.x, p_from.y, p_from.z); + } -// Color + static _FORCE_INLINE_ M_Vector3 convert_from(const Vector3 &p_from) { + M_Vector3 ret = { p_from.x, p_from.y, p_from.z }; + return ret; + } +}; -#define MARSHALLED_OUT_Color(m_in, m_out) real_t m_out[4] = { m_in.r, m_in.g, m_in.b, m_in.a }; -#define MARSHALLED_IN_Color(m_in, m_out) Color m_out(m_in[0], m_in[1], m_in[2], m_in[3]); +struct M_Basis { + M_Vector3 elements[3]; + + static _FORCE_INLINE_ Basis convert_to(const M_Basis &p_from) { + return Basis(M_Vector3::convert_to(p_from.elements[0]), + M_Vector3::convert_to(p_from.elements[1]), + M_Vector3::convert_to(p_from.elements[2])); + } + + static _FORCE_INLINE_ M_Basis convert_from(const Basis &p_from) { + M_Basis ret = { + M_Vector3::convert_from(p_from.elements[0]), + M_Vector3::convert_from(p_from.elements[1]), + M_Vector3::convert_from(p_from.elements[2]) + }; + return ret; + } +}; -// Plane +struct M_Quat { + real_t x, y, z, w; -#define MARSHALLED_OUT_Plane(m_in, m_out) real_t m_out[4] = { m_in.normal.x, m_in.normal.y, m_in.normal.z, m_in.d }; -#define MARSHALLED_IN_Plane(m_in, m_out) Plane m_out(m_in[0], m_in[1], m_in[2], m_in[3]); + static _FORCE_INLINE_ Quat convert_to(const M_Quat &p_from) { + return Quat(p_from.x, p_from.y, p_from.z, p_from.w); + } -#endif + static _FORCE_INLINE_ M_Quat convert_from(const Quat &p_from) { + M_Quat ret = { p_from.x, p_from.y, p_from.z, p_from.w }; + return ret; + } +}; + +struct M_Transform { + M_Basis basis; + M_Vector3 origin; + + static _FORCE_INLINE_ Transform convert_to(const M_Transform &p_from) { + return Transform(M_Basis::convert_to(p_from.basis), M_Vector3::convert_to(p_from.origin)); + } + + static _FORCE_INLINE_ M_Transform convert_from(const Transform &p_from) { + M_Transform ret = { M_Basis::convert_from(p_from.basis), M_Vector3::convert_from(p_from.origin) }; + return ret; + } +}; + +struct M_AABB { + M_Vector3 position; + M_Vector3 size; + + static _FORCE_INLINE_ AABB convert_to(const M_AABB &p_from) { + return AABB(M_Vector3::convert_to(p_from.position), M_Vector3::convert_to(p_from.size)); + } + + static _FORCE_INLINE_ M_AABB convert_from(const AABB &p_from) { + M_AABB ret = { M_Vector3::convert_from(p_from.position), M_Vector3::convert_from(p_from.size) }; + return ret; + } +}; + +struct M_Color { + float r, g, b, a; + + static _FORCE_INLINE_ Color convert_to(const M_Color &p_from) { + return Color(p_from.r, p_from.g, p_from.b, p_from.a); + } + + static _FORCE_INLINE_ M_Color convert_from(const Color &p_from) { + M_Color ret = { p_from.r, p_from.g, p_from.b, p_from.a }; + return ret; + } +}; + +struct M_Plane { + M_Vector3 normal; + real_t d; + + static _FORCE_INLINE_ Plane convert_to(const M_Plane &p_from) { + return Plane(M_Vector3::convert_to(p_from.normal), p_from.d); + } + + static _FORCE_INLINE_ M_Plane convert_from(const Plane &p_from) { + M_Plane ret = { M_Vector3::convert_from(p_from.normal), p_from.d }; + return ret; + } +}; + +#pragma pack(pop) + +#define DECL_TYPE_MARSHAL_TEMPLATES(m_type) \ + template <int> \ + _FORCE_INLINE_ m_type marshalled_in_##m_type##_impl(const M_##m_type *p_from); \ + \ + template <> \ + _FORCE_INLINE_ m_type marshalled_in_##m_type##_impl<0>(const M_##m_type *p_from) { \ + return M_##m_type::convert_to(*p_from); \ + } \ + \ + template <> \ + _FORCE_INLINE_ m_type marshalled_in_##m_type##_impl<1>(const M_##m_type *p_from) { \ + return *reinterpret_cast<const m_type *>(p_from); \ + } \ + \ + _FORCE_INLINE_ m_type marshalled_in_##m_type(const M_##m_type *p_from) { \ + return marshalled_in_##m_type##_impl<InteropLayout::MATCHES_##m_type>(p_from); \ + } \ + \ + template <int> \ + _FORCE_INLINE_ M_##m_type marshalled_out_##m_type##_impl(const m_type &p_from); \ + \ + template <> \ + _FORCE_INLINE_ M_##m_type marshalled_out_##m_type##_impl<0>(const m_type &p_from) { \ + return M_##m_type::convert_from(p_from); \ + } \ + \ + template <> \ + _FORCE_INLINE_ M_##m_type marshalled_out_##m_type##_impl<1>(const m_type &p_from) { \ + return *reinterpret_cast<const M_##m_type *>(&p_from); \ + } \ + \ + _FORCE_INLINE_ M_##m_type marshalled_out_##m_type(const m_type &p_from) { \ + return marshalled_out_##m_type##_impl<InteropLayout::MATCHES_##m_type>(p_from); \ + } + +DECL_TYPE_MARSHAL_TEMPLATES(Vector2) +DECL_TYPE_MARSHAL_TEMPLATES(Rect2) +DECL_TYPE_MARSHAL_TEMPLATES(Transform2D) +DECL_TYPE_MARSHAL_TEMPLATES(Vector3) +DECL_TYPE_MARSHAL_TEMPLATES(Basis) +DECL_TYPE_MARSHAL_TEMPLATES(Quat) +DECL_TYPE_MARSHAL_TEMPLATES(Transform) +DECL_TYPE_MARSHAL_TEMPLATES(AABB) +DECL_TYPE_MARSHAL_TEMPLATES(Color) +DECL_TYPE_MARSHAL_TEMPLATES(Plane) + +#define MARSHALLED_IN(m_type, m_from_ptr) (GDMonoMarshal::marshalled_in_##m_type(m_from_ptr)) +#define MARSHALLED_OUT(m_type, m_from) (GDMonoMarshal::marshalled_out_##m_type(m_from)) } // namespace GDMonoMarshal diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index bebc3b863f..b97a24b09c 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -32,10 +32,10 @@ #include <mono/metadata/exception.h> -#include "os/dir_access.h" -#include "os/os.h" -#include "project_settings.h" -#include "reference.h" +#include "core/os/dir_access.h" +#include "core/os/os.h" +#include "core/project_settings.h" +#include "core/reference.h" #include "../csharp_script.h" #include "../utils/macros.h" @@ -126,10 +126,11 @@ void MonoCache::clear_members() { class_RemoteAttribute = NULL; class_SyncAttribute = NULL; class_MasterAttribute = NULL; + class_PuppetAttribute = NULL; class_SlaveAttribute = NULL; class_RemoteSyncAttribute = NULL; class_MasterSyncAttribute = NULL; - class_SlaveSyncAttribute = NULL; + class_PuppetSyncAttribute = NULL; class_GodotMethodAttribute = NULL; field_GodotMethodAttribute_methodName = NULL; @@ -138,6 +139,7 @@ void MonoCache::clear_members() { field_Image_ptr = NULL; field_RID_ptr = NULL; + methodthunk_GodotObject_Dispose = NULL; methodthunk_Array_GetPtr = NULL; methodthunk_Dictionary_GetPtr = NULL; methodthunk_MarshalUtils_IsArrayGenericType = NULL; @@ -156,6 +158,7 @@ void MonoCache::cleanup() { } #define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class)) +#define GODOT_API_NS_CLAS(m_ns, m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(m_ns, #m_class)) void update_corlib_cache() { @@ -206,8 +209,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(Array, GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Array)); + CACHE_CLASS_AND_CHECK(Dictionary, GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Dictionary)); CACHE_CLASS_AND_CHECK(MarshalUtils, GODOT_API_CLASS(MarshalUtils)); #ifdef DEBUG_ENABLED @@ -223,10 +226,11 @@ void update_godot_api_cache() { CACHE_CLASS_AND_CHECK(RemoteAttribute, GODOT_API_CLASS(RemoteAttribute)); CACHE_CLASS_AND_CHECK(SyncAttribute, GODOT_API_CLASS(SyncAttribute)); CACHE_CLASS_AND_CHECK(MasterAttribute, GODOT_API_CLASS(MasterAttribute)); + CACHE_CLASS_AND_CHECK(PuppetAttribute, GODOT_API_CLASS(PuppetAttribute)); CACHE_CLASS_AND_CHECK(SlaveAttribute, GODOT_API_CLASS(SlaveAttribute)); CACHE_CLASS_AND_CHECK(RemoteSyncAttribute, GODOT_API_CLASS(RemoteSyncAttribute)); CACHE_CLASS_AND_CHECK(MasterSyncAttribute, GODOT_API_CLASS(MasterSyncAttribute)); - CACHE_CLASS_AND_CHECK(SlaveSyncAttribute, GODOT_API_CLASS(SlaveSyncAttribute)); + CACHE_CLASS_AND_CHECK(PuppetSyncAttribute, GODOT_API_CLASS(PuppetSyncAttribute)); CACHE_CLASS_AND_CHECK(GodotMethodAttribute, GODOT_API_CLASS(GodotMethodAttribute)); CACHE_FIELD_AND_CHECK(GodotMethodAttribute, methodName, CACHED_CLASS(GodotMethodAttribute)->get_field("methodName")); @@ -234,8 +238,9 @@ 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(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(GodotObject, Dispose, (GodotObject_Dispose)CACHED_CLASS(GodotObject)->get_method_thunk("Dispose", 0)); + CACHE_METHOD_THUNK_AND_CHECK(Array, GetPtr, (Array_GetPtr)GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Array)->get_method_thunk("GetPtr", 0)); + CACHE_METHOD_THUNK_AND_CHECK(Dictionary, GetPtr, (Dictionary_GetPtr)GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, 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)); @@ -246,7 +251,7 @@ void update_godot_api_cache() { CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, (DebugUtils_StackFrameInfo)GODOT_API_CLASS(DebuggingUtils)->get_method_thunk("GetStackFrameInfo", 4)); #endif - // TODO Move to CSharpLanguage::init() + // TODO Move to CSharpLanguage::init() and do handle disposal MonoObject *task_scheduler = mono_object_new(SCRIPTS_DOMAIN, GODOT_API_CLASS(GodotTaskScheduler)->get_mono_ptr()); GDMonoUtils::runtime_object_init(task_scheduler); mono_cache.task_scheduler_handle = MonoGCHandle::create_strong(task_scheduler); @@ -269,11 +274,48 @@ MonoObject *unmanaged_get_managed(Object *unmanaged) { } } - // Only called if the owner does not have a CSharpInstance + // If the owner does not have a CSharpInstance... + void *data = unmanaged->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()); if (data) { - return ((Map<Object *, Ref<MonoGCHandle> >::Element *)data)->value()->get_target(); + CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->value(); + + Ref<MonoGCHandle> &gchandle = script_binding.gchandle; + ERR_FAIL_COND_V(gchandle.is_null(), NULL); + + MonoObject *target = gchandle->get_target(); + + if (target) + return target; + + CSharpLanguage::get_singleton()->release_script_gchandle(gchandle); + + // Create a new one + +#ifdef DEBUG_ENABLED + CRASH_COND(script_binding.type_name == StringName()); + CRASH_COND(script_binding.wrapper_class == NULL); +#endif + + MonoObject *mono_object = GDMonoUtils::create_managed_for_godot_object(script_binding.wrapper_class, script_binding.type_name, unmanaged); + ERR_FAIL_NULL_V(mono_object, NULL); + + gchandle->set_handle(MonoGCHandle::new_strong_handle(mono_object), MonoGCHandle::STRONG_HANDLE); + + // Tie managed to unmanaged + Reference *ref = Object::cast_to<Reference>(unmanaged); + + if (ref) { + // Unsafe refcount increment. The managed instance also counts as a reference. + // This way if the unmanaged world has no references to our owner + // but the managed instance is alive, the refcount will be 1 instead of 0. + // See: godot_icall_Reference_Dtor(MonoObject *p_obj, Object *p_ptr) + + ref->reference(); + } + + return mono_object; } } @@ -303,6 +345,7 @@ MonoThread *get_current_thread() { void runtime_object_init(MonoObject *p_this_obj) { GD_MONO_BEGIN_RUNTIME_INVOKE; + // FIXME: Do not use mono_runtime_object_init, it aborts if an exception is thrown mono_runtime_object_init(p_this_obj); GD_MONO_END_RUNTIME_INVOKE; } @@ -622,4 +665,33 @@ MonoObject *property_get_value(MonoProperty *p_prop, void *p_obj, void **p_param return ret; } +uint64_t unbox_enum_value(MonoObject *p_boxed, MonoType *p_enum_basetype, bool &r_error) { + r_error = false; + switch (mono_type_get_type(p_enum_basetype)) { + case MONO_TYPE_BOOLEAN: + return (bool)GDMonoMarshal::unbox<MonoBoolean>(p_boxed) ? 1 : 0; + case MONO_TYPE_CHAR: + return GDMonoMarshal::unbox<uint16_t>(p_boxed); + case MONO_TYPE_U1: + return GDMonoMarshal::unbox<uint8_t>(p_boxed); + case MONO_TYPE_U2: + return GDMonoMarshal::unbox<uint16_t>(p_boxed); + case MONO_TYPE_U4: + return GDMonoMarshal::unbox<uint32_t>(p_boxed); + case MONO_TYPE_U8: + return GDMonoMarshal::unbox<uint64_t>(p_boxed); + case MONO_TYPE_I1: + return GDMonoMarshal::unbox<int8_t>(p_boxed); + case MONO_TYPE_I2: + return GDMonoMarshal::unbox<int16_t>(p_boxed); + case MONO_TYPE_I4: + return GDMonoMarshal::unbox<int32_t>(p_boxed); + case MONO_TYPE_I8: + return GDMonoMarshal::unbox<int64_t>(p_boxed); + default: + r_error = true; + return 0; + } +} + } // namespace GDMonoUtils diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h index bf8860c85a..ec3a57eb46 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -38,8 +38,8 @@ #include "../utils/thread_local.h" #include "gd_mono_header.h" -#include "object.h" -#include "reference.h" +#include "core/object.h" +#include "core/reference.h" #define UNLIKELY_UNHANDLED_EXCEPTION(m_exc) \ if (unlikely(m_exc != NULL)) { \ @@ -49,6 +49,7 @@ namespace GDMonoUtils { +typedef void (*GodotObject_Dispose)(MonoObject *, MonoObject **); typedef Array *(*Array_GetPtr)(MonoObject *, MonoObject **); typedef Dictionary *(*Dictionary_GetPtr)(MonoObject *, MonoObject **); typedef MonoObject *(*SignalAwaiter_SignalCallback)(MonoObject *, MonoArray *, MonoObject **); @@ -130,8 +131,9 @@ struct MonoCache { GDMonoClass *class_SyncAttribute; GDMonoClass *class_RemoteSyncAttribute; GDMonoClass *class_MasterSyncAttribute; - GDMonoClass *class_SlaveSyncAttribute; + GDMonoClass *class_PuppetSyncAttribute; GDMonoClass *class_MasterAttribute; + GDMonoClass *class_PuppetAttribute; GDMonoClass *class_SlaveAttribute; GDMonoClass *class_GodotMethodAttribute; GDMonoField *field_GodotMethodAttribute_methodName; @@ -141,6 +143,7 @@ struct MonoCache { GDMonoField *field_Image_ptr; GDMonoField *field_RID_ptr; + GodotObject_Dispose methodthunk_GodotObject_Dispose; Array_GetPtr methodthunk_Array_GetPtr; Dictionary_GetPtr methodthunk_Dictionary_GetPtr; IsArrayGenericType methodthunk_MarshalUtils_IsArrayGenericType; @@ -238,6 +241,8 @@ 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); +uint64_t unbox_enum_value(MonoObject *p_boxed, MonoType *p_enum_basetype, bool &r_error); + } // 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/register_types.cpp b/modules/mono/register_types.cpp index 4410996546..f6cb143e8e 100644 --- a/modules/mono/register_types.cpp +++ b/modules/mono/register_types.cpp @@ -30,7 +30,7 @@ #include "register_types.h" -#include "engine.h" +#include "core/engine.h" #include "csharp_script.h" diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp index 54720652fa..fa1fbebb16 100644 --- a/modules/mono/signal_awaiter_utils.cpp +++ b/modules/mono/signal_awaiter_utils.cpp @@ -42,8 +42,7 @@ Error connect_signal_awaiter(Object *p_source, const String &p_signal, Object *p ERR_FAIL_NULL_V(p_source, ERR_INVALID_DATA); ERR_FAIL_NULL_V(p_target, ERR_INVALID_DATA); - uint32_t awaiter_handle = MonoGCHandle::make_strong_handle(p_awaiter); - Ref<SignalAwaiterHandle> sa_con = memnew(SignalAwaiterHandle(awaiter_handle)); + Ref<SignalAwaiterHandle> sa_con = memnew(SignalAwaiterHandle(p_awaiter)); #ifdef DEBUG_ENABLED sa_con->set_connection_target(p_target); #endif @@ -119,8 +118,8 @@ void SignalAwaiterHandle::_bind_methods() { ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "_signal_callback", &SignalAwaiterHandle::_signal_callback, MethodInfo("_signal_callback")); } -SignalAwaiterHandle::SignalAwaiterHandle(uint32_t p_managed_handle) : - MonoGCHandle(p_managed_handle) { +SignalAwaiterHandle::SignalAwaiterHandle(MonoObject *p_managed) : + MonoGCHandle(MonoGCHandle::new_strong_handle(p_managed), STRONG_HANDLE) { #ifdef DEBUG_ENABLED conn_target_id = 0; @@ -142,7 +141,7 @@ SignalAwaiterHandle::~SignalAwaiterHandle() { if (exc) { GDMonoUtils::set_pending_exception(exc); - ERR_FAIL_V(); + ERR_FAIL(); } } } diff --git a/modules/mono/signal_awaiter_utils.h b/modules/mono/signal_awaiter_utils.h index a6a205ff8d..4ec860537b 100644 --- a/modules/mono/signal_awaiter_utils.h +++ b/modules/mono/signal_awaiter_utils.h @@ -31,8 +31,8 @@ #ifndef SIGNAL_AWAITER_UTILS_H #define SIGNAL_AWAITER_UTILS_H +#include "core/reference.h" #include "mono_gc_handle.h" -#include "reference.h" namespace SignalAwaiterUtils { @@ -64,7 +64,7 @@ public: } #endif - SignalAwaiterHandle(uint32_t p_managed_handle); + SignalAwaiterHandle(MonoObject *p_managed); ~SignalAwaiterHandle(); }; diff --git a/modules/mono/utils/macros.h b/modules/mono/utils/macros.h index 337a86870e..40b47e8648 100644 --- a/modules/mono/utils/macros.h +++ b/modules/mono/utils/macros.h @@ -33,6 +33,15 @@ // noreturn +#if __cpp_static_assert +#define GD_STATIC_ASSERT(m_cond) static_assert((m_cond), "Condition '" #m_cond "' failed") +#else +#define _GD_STATIC_ASSERT_VARNAME_CONCAT_B(m_ignore, m_name) m_name +#define _GD_STATIC_ASSERT_VARNAME_CONCAT_A(m_a, m_b) GD_STATIC_ASSERT_VARNAME_CONCAT_B(hello there, m_a##m_b) +#define _GD_STATIC_ASSERT_VARNAME_CONCAT(m_a, m_b) GD_STATIC_ASSERT_VARNAME_CONCAT_A(m_a, m_b) +#define GD_STATIC_ASSERT(m_cond) typedef int GD_STATIC_ASSERT_VARNAME_CONCAT(godot_static_assert_, __COUNTER__)[((m_cond) ? 1 : -1)] +#endif + #undef _NO_RETURN_ #ifdef __GNUC__ diff --git a/modules/mono/utils/mono_reg_utils.cpp b/modules/mono/utils/mono_reg_utils.cpp index 7b23cd7579..6bb6efa92a 100644 --- a/modules/mono/utils/mono_reg_utils.cpp +++ b/modules/mono/utils/mono_reg_utils.cpp @@ -32,7 +32,7 @@ #ifdef WINDOWS_ENABLED -#include "os/os.h" +#include "core/os/os.h" // Here, after os/os.h #include <windows.h> @@ -228,4 +228,4 @@ cleanup: } } // namespace MonoRegUtils -#endif WINDOWS_ENABLED +#endif // WINDOWS_ENABLED diff --git a/modules/mono/utils/mono_reg_utils.h b/modules/mono/utils/mono_reg_utils.h index edf31f5a07..26f7e2d3c2 100644 --- a/modules/mono/utils/mono_reg_utils.h +++ b/modules/mono/utils/mono_reg_utils.h @@ -33,7 +33,7 @@ #ifdef WINDOWS_ENABLED -#include "ustring.h" +#include "core/ustring.h" struct MonoRegInfo { diff --git a/drivers/windows/tcp_server_winsock.h b/modules/mono/utils/osx_utils.cpp index a6979db42d..f520706a0c 100644 --- a/drivers/windows/tcp_server_winsock.h +++ b/modules/mono/utils/osx_utils.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* tcp_server_winsock.h */ +/* osx_utils.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,34 +28,35 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef WINDOWS_ENABLED +#include "osx_utils.h" -#ifndef TCP_SERVER_WINSOCK_H -#define TCP_SERVER_WINSOCK_H +#include "core/print_string.h" -#include "core/io/tcp_server.h" +#ifdef OSX_ENABLED -class TCPServerWinsock : public TCP_Server { +#include <CoreFoundation/CoreFoundation.h> +#include <CoreServices/CoreServices.h> - int listen_sockfd; - IP::Type sock_type; +bool osx_is_app_bundle_installed(const String &p_bundle_id) { - static TCP_Server *_create(); + CFURLRef app_url = NULL; + CFStringRef bundle_id = CFStringCreateWithCString(NULL, p_bundle_id.utf8(), kCFStringEncodingUTF8); + OSStatus result = LSFindApplicationForInfo(kLSUnknownCreator, bundle_id, NULL, NULL, &app_url); + CFRelease(bundle_id); -public: - virtual Error listen(uint16_t p_port, const IP_Address &p_bind_address = IP_Address("*")); - virtual bool is_connection_available() const; - virtual Ref<StreamPeerTCP> take_connection(); + if (app_url) + CFRelease(app_url); - virtual void stop(); //stop listening + switch (result) { + case noErr: + return true; + case kLSApplicationNotFoundErr: + break; + default: + break; + } - static void make_default(); - static void cleanup(); - - TCPServerWinsock(); - ~TCPServerWinsock(); -}; - -#endif + return false; +} #endif diff --git a/core/helper/value_evaluator.h b/modules/mono/utils/osx_utils.h index 39177a7820..68479b4f44 100644 --- a/core/helper/value_evaluator.h +++ b/modules/mono/utils/osx_utils.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* value_evaluator.h */ +/* osx_utils.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,19 +28,14 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef VALUE_EVALUATOR_H -#define VALUE_EVALUATOR_H +#include "core/ustring.h" -#include "core/object.h" +#ifndef OSX_UTILS_H -class ValueEvaluator : public Object { +#ifdef OSX_ENABLED - GDCLASS(ValueEvaluator, Object); +bool osx_is_app_bundle_installed(const String &p_bundle_id); -public: - virtual double eval(const String &p_text) { - return p_text.to_double(); - } -}; +#endif -#endif // VALUE_EVALUATOR_H +#endif // OSX_UTILS_H diff --git a/modules/mono/utils/path_utils.cpp b/modules/mono/utils/path_utils.cpp index 4b77aeb54e..ea942a9a8e 100644 --- a/modules/mono/utils/path_utils.cpp +++ b/modules/mono/utils/path_utils.cpp @@ -30,10 +30,10 @@ #include "path_utils.h" -#include "os/dir_access.h" -#include "os/file_access.h" -#include "os/os.h" -#include "project_settings.h" +#include "core/os/dir_access.h" +#include "core/os/file_access.h" +#include "core/os/os.h" +#include "core/project_settings.h" #ifdef WINDOWS_ENABLED #define ENV_PATH_SEP ";" diff --git a/modules/mono/utils/path_utils.h b/modules/mono/utils/path_utils.h index 184cacfac7..3c7b36c0d4 100644 --- a/modules/mono/utils/path_utils.h +++ b/modules/mono/utils/path_utils.h @@ -31,7 +31,7 @@ #ifndef PATH_UTILS_H #define PATH_UTILS_H -#include "ustring.h" +#include "core/ustring.h" _FORCE_INLINE_ String path_join(const String &e1, const String &e2) { return e1.plus_file(e2); diff --git a/modules/mono/utils/string_utils.cpp b/modules/mono/utils/string_utils.cpp index 8691932f9a..6900866725 100644 --- a/modules/mono/utils/string_utils.cpp +++ b/modules/mono/utils/string_utils.cpp @@ -30,6 +30,8 @@ #include "string_utils.h" +#include "core/os/file_access.h" + namespace { int sfind(const String &p_text, int p_from) { @@ -128,6 +130,7 @@ String sformat(const String &p_text, const Variant &p1, const Variant &p2, const return new_string; } +#ifdef TOOLS_ENABLED bool is_csharp_keyword(const String &p_name) { // Reserved keywords @@ -156,3 +159,28 @@ bool is_csharp_keyword(const String &p_name) { String escape_csharp_keyword(const String &p_name) { return is_csharp_keyword(p_name) ? "@" + p_name : p_name; } +#endif + +Error read_all_file_utf8(const String &p_path, String &r_content) { + PoolVector<uint8_t> sourcef; + Error err; + FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); + ERR_FAIL_COND_V(err != OK, 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 source; + if (source.parse_utf8((const char *)w.ptr())) { + ERR_FAIL_V(ERR_INVALID_DATA); + } + + r_content = source; + return OK; +} diff --git a/modules/mono/utils/string_utils.h b/modules/mono/utils/string_utils.h index 5dddaee6e8..ee803bd720 100644 --- a/modules/mono/utils/string_utils.h +++ b/modules/mono/utils/string_utils.h @@ -31,8 +31,8 @@ #ifndef STRING_FORMAT_H #define STRING_FORMAT_H -#include "ustring.h" -#include "variant.h" +#include "core/ustring.h" +#include "core/variant.h" String sformat(const String &p_text, const Variant &p1 = Variant(), const Variant &p2 = Variant(), const Variant &p3 = Variant(), const Variant &p4 = Variant(), const Variant &p5 = Variant()); @@ -42,4 +42,6 @@ bool is_csharp_keyword(const String &p_name); String escape_csharp_keyword(const String &p_name); #endif +Error read_all_file_utf8(const String &p_path, String &r_content); + #endif // STRING_FORMAT_H diff --git a/modules/mono/utils/thread_local.h b/modules/mono/utils/thread_local.h index 84dae1d86b..d7d98c47e2 100644 --- a/modules/mono/utils/thread_local.h +++ b/modules/mono/utils/thread_local.h @@ -108,17 +108,23 @@ class ThreadLocal { return data; } + void _initialize(const T &p_init_val) { + init_val = p_init_val; + storage.alloc(&destr_callback); + } + public: - ThreadLocal() : - ThreadLocal(T()) {} + ThreadLocal() { + _initialize(T()); + } - ThreadLocal(const T &p_init_val) : - init_val(p_init_val) { - storage.alloc(&destr_callback); + ThreadLocal(const T &p_init_val) { + _initialize(p_init_val); } - ThreadLocal(const ThreadLocal &other) : - ThreadLocal(*other._tls_get_value()) {} + ThreadLocal(const ThreadLocal &other) { + _initialize(*other._tls_get_value()); + } ~ThreadLocal() { storage.free(); diff --git a/modules/ogg/SCsub b/modules/ogg/SCsub index 5e559bd4db..765a9fc11a 100644 --- a/modules/ogg/SCsub +++ b/modules/ogg/SCsub @@ -14,8 +14,11 @@ if env['builtin_libogg']: ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_ogg.add_source_files(env.modules_sources, thirdparty_sources) env_ogg.Append(CPPPATH=[thirdparty_dir]) + env_thirdparty = env_ogg.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + # Godot source files env_ogg.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/opensimplex/SCsub b/modules/opensimplex/SCsub new file mode 100644 index 0000000000..4235f6a0b9 --- /dev/null +++ b/modules/opensimplex/SCsub @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +Import('env') +Import('env_modules') + +env_opensimplex = env_modules.Clone() + +# Thirdparty source files +thirdparty_dir = "#thirdparty/misc/" +thirdparty_sources = [ + "open-simplex-noise.c", +] +thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] + +env_opensimplex.Append(CPPPATH=[thirdparty_dir]) + +env_thirdparty = env_opensimplex.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + +# Godot's own source files +env_opensimplex.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/opensimplex/config.py b/modules/opensimplex/config.py new file mode 100644 index 0000000000..c1010ad433 --- /dev/null +++ b/modules/opensimplex/config.py @@ -0,0 +1,14 @@ +def can_build(env, platform): + return True + +def configure(env): + pass + +def get_doc_classes(): + return [ + "NoiseTexture", + "OpenSimplexNoise" + ] + +def get_doc_path(): + return "doc_classes" diff --git a/modules/opensimplex/doc_classes/NoiseTexture.xml b/modules/opensimplex/doc_classes/NoiseTexture.xml new file mode 100644 index 0000000000..ba54160a90 --- /dev/null +++ b/modules/opensimplex/doc_classes/NoiseTexture.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="NoiseTexture" inherits="Texture" category="Core" version="3.1"> + <brief_description> + [OpenSimplexNoise] filled texture. + </brief_description> + <description> + Uses an [OpenSimplexNoise] to fill the texture data. You can specify the texture size but keep in mind that larger textures will take longer to generate and seamless noise only works with square sized textures. + NoiseTexture can also generate normalmap textures. + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <members> + <member name="as_normalmap" type="bool" setter="set_as_normalmap" getter="is_normalmap"> + If true, the resulting texture contains a normal map created from the original noise interpreted as a bump map. + </member> + <member name="height" type="int" setter="set_height" getter="get_height"> + Height of the generated texture. + </member> + <member name="noise" type="OpenSimplexNoise" setter="set_noise" getter="get_noise"> + The [OpenSimplexNoise] instance used to generate the noise. + </member> + <member name="seamless" type="bool" setter="set_seamless" getter="get_seamless"> + Whether the texture can be tiled without visible seams or not. Seamless textures take longer to generate. + </member> + <member name="width" type="int" setter="set_width" getter="get_width"> + Width of the generated texture. + </member> + </members> + <constants> + </constants> +</class> diff --git a/modules/opensimplex/doc_classes/OpenSimplexNoise.xml b/modules/opensimplex/doc_classes/OpenSimplexNoise.xml new file mode 100644 index 0000000000..31f13f341c --- /dev/null +++ b/modules/opensimplex/doc_classes/OpenSimplexNoise.xml @@ -0,0 +1,126 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="OpenSimplexNoise" inherits="Resource" category="Core" version="3.1"> + <brief_description> + Noise generator based on Open Simplex. + </brief_description> + <description> + This resource allows you to configure and sample a fractal noise space. Here is a brief usage example that configures an OpenSimplexNoise and gets samples at various positions and dimensions: + [codeblock] + var noise = OpenSimplexNoise.new() + + # Configure + noise.seed = randi() + noise.octaves = 4 + noise.period = 20.0 + noise.persistence = 0.8 + + # Sample + print("Values:") + print(noise.get_noise_2d(1.0, 1.0)) + print(noise.get_noise_3d(0.5, 3.0, 15.0)) + print(noise.get_noise_4d(0.5, 1.9, 4.7, 0.0)) + [/codeblock] + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + <method name="get_image"> + <return type="Image"> + </return> + <argument index="0" name="width" type="int"> + </argument> + <argument index="1" name="height" type="int"> + </argument> + <description> + Generate a noise image with the requested [code]width[/code] and [code]height[/code], based on the current noise parameters. + </description> + </method> + <method name="get_noise_2d"> + <return type="float"> + </return> + <argument index="0" name="x" type="float"> + </argument> + <argument index="1" name="y" type="float"> + </argument> + <description> + Returns the 2D noise value [code][-1,1][/code] at the given position. + </description> + </method> + <method name="get_noise_2dv"> + <return type="float"> + </return> + <argument index="0" name="pos" type="Vector2"> + </argument> + <description> + Returns the 2D noise value [code][-1,1][/code] at the given position. + </description> + </method> + <method name="get_noise_3d"> + <return type="float"> + </return> + <argument index="0" name="x" type="float"> + </argument> + <argument index="1" name="y" type="float"> + </argument> + <argument index="2" name="z" type="float"> + </argument> + <description> + Returns the 3D noise value [code][-1,1][/code] at the given position. + </description> + </method> + <method name="get_noise_3dv"> + <return type="float"> + </return> + <argument index="0" name="pos" type="Vector3"> + </argument> + <description> + Returns the 3D noise value [code][-1,1][/code] at the given position. + </description> + </method> + <method name="get_noise_4d"> + <return type="float"> + </return> + <argument index="0" name="x" type="float"> + </argument> + <argument index="1" name="y" type="float"> + </argument> + <argument index="2" name="z" type="float"> + </argument> + <argument index="3" name="w" type="float"> + </argument> + <description> + Returns the 4D noise value [code][-1,1][/code] at the given position. + </description> + </method> + <method name="get_seamless_image"> + <return type="Image"> + </return> + <argument index="0" name="size" type="int"> + </argument> + <description> + Generate a tileable noise image, based on the current noise parameters. Generated seamless images are always square ([code]size[/code] x [code]size[/code]). + </description> + </method> + </methods> + <members> + <member name="lacunarity" type="float" setter="set_lacunarity" getter="get_lacunarity"> + Difference in period between [member octaves]. + </member> + <member name="octaves" type="int" setter="set_octaves" getter="get_octaves"> + Number of OpenSimplex noise layers that are sampled to get the fractal noise. + </member> + <member name="period" type="float" setter="set_period" getter="get_period"> + Period of the base octave. A lower period results in a higher-frequency noise (more value changes across the same distance). + </member> + <member name="persistence" type="float" setter="set_persistence" getter="get_persistence"> + Contribution factor of the different octaves. A [code]persistence[/code] value of 1 means all the octaves have the same contribution, a value of 0.5 means each octave contributes half as much as the previous one. + </member> + <member name="seed" type="int" setter="set_seed" getter="get_seed"> + Seed used to generate random values, different seeds will generate different noise maps. + </member> + </members> + <constants> + </constants> +</class> diff --git a/modules/opensimplex/noise_texture.cpp b/modules/opensimplex/noise_texture.cpp new file mode 100644 index 0000000000..be522a9ab1 --- /dev/null +++ b/modules/opensimplex/noise_texture.cpp @@ -0,0 +1,233 @@ +/*************************************************************************/ +/* noise_texture.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 "noise_texture.h" + +#include "core/core_string_names.h" + +NoiseTexture::NoiseTexture() { + update_queued = false; + noise_thread = NULL; + regen_queued = false; + first_time = true; + + size = Vector2i(512, 512); + seamless = false; + as_normalmap = false; + flags = FLAGS_DEFAULT; + + noise = Ref<OpenSimplexNoise>(); + + texture = VS::get_singleton()->texture_create(); + + _queue_update(); +} + +NoiseTexture::~NoiseTexture() { + VS::get_singleton()->free(texture); +} + +void NoiseTexture::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_width", "width"), &NoiseTexture::set_width); + ClassDB::bind_method(D_METHOD("set_height", "height"), &NoiseTexture::set_height); + + ClassDB::bind_method(D_METHOD("set_noise", "noise"), &NoiseTexture::set_noise); + ClassDB::bind_method(D_METHOD("get_noise"), &NoiseTexture::get_noise); + + ClassDB::bind_method(D_METHOD("set_seamless", "seamless"), &NoiseTexture::set_seamless); + ClassDB::bind_method(D_METHOD("get_seamless"), &NoiseTexture::get_seamless); + + ClassDB::bind_method(D_METHOD("set_as_normalmap", "as_normalmap"), &NoiseTexture::set_as_normalmap); + ClassDB::bind_method(D_METHOD("is_normalmap"), &NoiseTexture::is_normalmap); + + ClassDB::bind_method(D_METHOD("_update_texture"), &NoiseTexture::_update_texture); + ClassDB::bind_method(D_METHOD("_generate_texture"), &NoiseTexture::_generate_texture); + ClassDB::bind_method(D_METHOD("_thread_done", "image"), &NoiseTexture::_thread_done); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,2048,1,or_greater"), "set_width", "get_width"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "height", PROPERTY_HINT_RANGE, "1,2048,1,or_greater"), "set_height", "get_height"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "seamless"), "set_seamless", "get_seamless"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "as_normalmap"), "set_as_normalmap", "is_normalmap"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "noise", PROPERTY_HINT_RESOURCE_TYPE, "OpenSimplexNoise"), "set_noise", "get_noise"); +} + +void NoiseTexture::_set_texture_data(const Ref<Image> &p_image) { + data = p_image; + if (data.is_valid()) { + VS::get_singleton()->texture_allocate(texture, size.x, size.y, 0, Image::FORMAT_RGBA8, VS::TEXTURE_TYPE_2D, flags); + VS::get_singleton()->texture_set_data(texture, p_image); + } + emit_changed(); +} + +void NoiseTexture::_thread_done(const Ref<Image> &p_image) { + + _set_texture_data(p_image); + Thread::wait_to_finish(noise_thread); + memdelete(noise_thread); + noise_thread = NULL; + if (regen_queued) { + noise_thread = Thread::create(_thread_function, this); + regen_queued = false; + } +} + +void NoiseTexture::_thread_function(void *p_ud) { + NoiseTexture *tex = (NoiseTexture *)p_ud; + tex->call_deferred("_thread_done", tex->_generate_texture()); +} + +void NoiseTexture::_queue_update() { + + if (update_queued) + return; + + update_queued = true; + call_deferred("_update_texture"); +} + +Ref<Image> NoiseTexture::_generate_texture() { + + update_queued = false; + + if (noise.is_null()) return Ref<Image>(); + + Ref<Image> image; + + if (seamless) { + image = noise->get_seamless_image(size.x); + } else { + image = noise->get_image(size.x, size.y); + } + + if (as_normalmap) { + image->bumpmap_to_normalmap(); + } + + return image; +} + +void NoiseTexture::_update_texture() { + bool use_thread = true; + if (first_time) { + use_thread = false; + first_time = false; + } +#ifdef NO_THREADS + use_thread = false; +#endif + if (use_thread) { + + if (!noise_thread) { + noise_thread = Thread::create(_thread_function, this); + regen_queued = false; + } else { + regen_queued = true; + } + + } else { + Ref<Image> image = _generate_texture(); + _set_texture_data(image); + } +} + +void NoiseTexture::set_noise(Ref<OpenSimplexNoise> p_noise) { + if (p_noise == noise) + return; + if (noise.is_valid()) { + noise->disconnect(CoreStringNames::get_singleton()->changed, this, "_update_texture"); + } + noise = p_noise; + if (noise.is_valid()) { + noise->connect(CoreStringNames::get_singleton()->changed, this, "_update_texture"); + } + _queue_update(); +} + +Ref<OpenSimplexNoise> NoiseTexture::get_noise() { + return noise; +} + +void NoiseTexture::set_width(int p_width) { + if (p_width == size.x) return; + size.x = p_width; + _queue_update(); +} + +void NoiseTexture::set_height(int p_height) { + if (p_height == size.y) return; + size.y = p_height; + _queue_update(); +} + +void NoiseTexture::set_seamless(bool p_seamless) { + if (p_seamless == seamless) return; + seamless = p_seamless; + _queue_update(); +} + +bool NoiseTexture::get_seamless() { + return seamless; +} + +void NoiseTexture::set_as_normalmap(bool p_as_normalmap) { + if (p_as_normalmap == as_normalmap) return; + as_normalmap = p_as_normalmap; + _queue_update(); +} + +bool NoiseTexture::is_normalmap() { + return as_normalmap; +} + +int NoiseTexture::get_width() const { + + return size.x; +} + +int NoiseTexture::get_height() const { + + return size.y; +} + +void NoiseTexture::set_flags(uint32_t p_flags) { + flags = p_flags; + VS::get_singleton()->texture_set_flags(texture, flags); +} + +uint32_t NoiseTexture::get_flags() const { + return flags; +} + +Ref<Image> NoiseTexture::get_data() const { + + return data; +} diff --git a/drivers/windows/packet_peer_udp_winsock.h b/modules/opensimplex/noise_texture.h index 8d575c2033..2a4c32d633 100644 --- a/drivers/windows/packet_peer_udp_winsock.h +++ b/modules/opensimplex/noise_texture.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* packet_peer_udp_winsock.h */ +/* noise_texture.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,62 +28,74 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef WINDOWS_ENABLED +#ifndef NOISE_TEXTURE_H +#define NOISE_TEXTURE_H -#ifndef PACKET_PEER_UDP_WINSOCK_H -#define PACKET_PEER_UDP_WINSOCK_H +#include "open_simplex_noise.h" -#include "io/packet_peer_udp.h" -#include "ring_buffer.h" +#include "core/image.h" +#include "core/reference.h" +#include "editor/editor_node.h" +#include "editor/editor_plugin.h" +#include "editor/property_editor.h" -class PacketPeerUDPWinsock : public PacketPeerUDP { +class NoiseTexture : public Texture { + GDCLASS(NoiseTexture, Texture) - enum { - PACKET_BUFFER_SIZE = 65536 - }; +private: + Ref<Image> data; - RingBuffer<uint8_t> rb; - uint8_t recv_buffer[PACKET_BUFFER_SIZE]; - uint8_t packet_buffer[PACKET_BUFFER_SIZE]; - IP_Address packet_ip; - int packet_port; - int queue_count; - int sockfd; - bool sock_blocking; - IP::Type sock_type; + Thread *noise_thread; - IP_Address peer_addr; - int peer_port; + bool first_time; + bool update_queued; + bool regen_queued; - _FORCE_INLINE_ int _get_socket(); + RID texture; + uint32_t flags; - static PacketPeerUDP *_create(); + Ref<OpenSimplexNoise> noise; + Vector2i size; + bool seamless; + bool as_normalmap; - void _set_sock_blocking(bool p_blocking); + void _thread_done(const Ref<Image> &p_image); + static void _thread_function(void *p_ud); - Error _poll(bool p_wait); + void _queue_update(); + Ref<Image> _generate_texture(); + void _update_texture(); + void _set_texture_data(const Ref<Image> &p_image); + +protected: + static void _bind_methods(); public: - 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); + void set_noise(Ref<OpenSimplexNoise> p_noise); + Ref<OpenSimplexNoise> get_noise(); + + void set_width(int p_width); + void set_height(int p_hieght); + + void set_seamless(bool p_seamless); + bool get_seamless(); + + void set_as_normalmap(bool p_seamless); + bool is_normalmap(); - virtual int get_max_packet_size() const; + int get_width() const; + int get_height() const; - virtual Error listen(int p_port, const IP_Address &p_bind_address = IP_Address("*"), int p_recv_buffer_size = 65536); - virtual void close(); - virtual Error wait(); - virtual bool is_listening() const; + virtual void set_flags(uint32_t p_flags); + virtual uint32_t get_flags() const; - virtual IP_Address get_packet_address() const; - virtual int get_packet_port() const; + virtual RID get_rid() const { return texture; } + virtual bool has_alpha() const { return false; } - virtual void set_dest_address(const IP_Address &p_address, int p_port); + virtual Ref<Image> get_data() const; - static void make_default(); - PacketPeerUDPWinsock(); - ~PacketPeerUDPWinsock(); + NoiseTexture(); + virtual ~NoiseTexture(); }; -#endif // PACKET_PEER_UDP_WINSOCK_H -#endif +#endif // NOISE_TEXTURE_H diff --git a/modules/opensimplex/open_simplex_noise.cpp b/modules/opensimplex/open_simplex_noise.cpp new file mode 100644 index 0000000000..bfc2732ff4 --- /dev/null +++ b/modules/opensimplex/open_simplex_noise.cpp @@ -0,0 +1,257 @@ +/*************************************************************************/ +/* open_simplex_noise.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 "open_simplex_noise.h" + +#include "core/core_string_names.h" + +OpenSimplexNoise::OpenSimplexNoise() { + + seed = 0; + persistence = 0.5; + octaves = 3; + period = 64; + lacunarity = 2.0; + + _init_seeds(); +} + +OpenSimplexNoise::~OpenSimplexNoise() { +} + +void OpenSimplexNoise::_init_seeds() { + for (int i = 0; i < 6; ++i) { + open_simplex_noise(seed + i * 2, &(contexts[i])); + } +} + +void OpenSimplexNoise::set_seed(int p_seed) { + + if (seed == p_seed) + return; + + seed = p_seed; + + _init_seeds(); + + emit_changed(); +} + +int OpenSimplexNoise::get_seed() { + + return seed; +} + +void OpenSimplexNoise::set_octaves(int p_octaves) { + if (p_octaves == octaves) return; + octaves = CLAMP(p_octaves, 1, 6); + emit_changed(); +} + +void OpenSimplexNoise::set_period(float p_period) { + if (p_period == period) return; + period = p_period; + emit_changed(); +} + +void OpenSimplexNoise::set_persistence(float p_persistence) { + if (p_persistence == persistence) return; + persistence = p_persistence; + emit_changed(); +} + +void OpenSimplexNoise::set_lacunarity(float p_lacunarity) { + if (p_lacunarity == lacunarity) return; + lacunarity = p_lacunarity; + emit_changed(); +} + +Ref<Image> OpenSimplexNoise::get_image(int p_width, int p_height) { + + PoolVector<uint8_t> data; + data.resize(p_width * p_height * 4); + + PoolVector<uint8_t>::Write wd8 = data.write(); + + for (int i = 0; i < p_height; i++) { + for (int j = 0; j < p_width; j++) { + float v = get_noise_2d(i, j); + v = v * 0.5 + 0.5; // Normalize [0..1] + uint8_t value = uint8_t(CLAMP(v * 255.0, 0, 255)); + wd8[(i * p_width + j) * 4 + 0] = value; + wd8[(i * p_width + j) * 4 + 1] = value; + wd8[(i * p_width + j) * 4 + 2] = value; + wd8[(i * p_width + j) * 4 + 3] = 255; + } + } + + Ref<Image> image = memnew(Image(p_width, p_height, false, Image::FORMAT_RGBA8, data)); + return image; +} + +Ref<Image> OpenSimplexNoise::get_seamless_image(int p_size) { + + PoolVector<uint8_t> data; + data.resize(p_size * p_size * 4); + + PoolVector<uint8_t>::Write wd8 = data.write(); + + for (int i = 0; i < p_size; i++) { + for (int j = 0; j < p_size; j++) { + + float ii = (float)i / (float)p_size; + float jj = (float)j / (float)p_size; + + ii *= 2.0 * Math_PI; + jj *= 2.0 * Math_PI; + + float radius = p_size / (2.0 * Math_PI); + + float x = radius * Math::sin(jj); + float y = radius * Math::cos(jj); + float z = radius * Math::sin(ii); + float w = radius * Math::cos(ii); + float v = get_noise_4d(x, y, z, w); + + v = v * 0.5 + 0.5; // Normalize [0..1] + uint8_t value = uint8_t(CLAMP(v * 255.0, 0, 255)); + wd8[(i * p_size + j) * 4 + 0] = value; + wd8[(i * p_size + j) * 4 + 1] = value; + wd8[(i * p_size + j) * 4 + 2] = value; + wd8[(i * p_size + j) * 4 + 3] = 255; + } + } + + Ref<Image> image = memnew(Image(p_size, p_size, false, Image::FORMAT_RGBA8, data)); + return image; +} + +void OpenSimplexNoise::_bind_methods() { + + ClassDB::bind_method(D_METHOD("get_seed"), &OpenSimplexNoise::get_seed); + ClassDB::bind_method(D_METHOD("set_seed", "seed"), &OpenSimplexNoise::set_seed); + + ClassDB::bind_method(D_METHOD("set_octaves", "octave_count"), &OpenSimplexNoise::set_octaves); + ClassDB::bind_method(D_METHOD("get_octaves"), &OpenSimplexNoise::get_octaves); + + ClassDB::bind_method(D_METHOD("set_period", "period"), &OpenSimplexNoise::set_period); + ClassDB::bind_method(D_METHOD("get_period"), &OpenSimplexNoise::get_period); + + ClassDB::bind_method(D_METHOD("set_persistence", "persistence"), &OpenSimplexNoise::set_persistence); + ClassDB::bind_method(D_METHOD("get_persistence"), &OpenSimplexNoise::get_persistence); + + ClassDB::bind_method(D_METHOD("set_lacunarity", "lacunarity"), &OpenSimplexNoise::set_lacunarity); + ClassDB::bind_method(D_METHOD("get_lacunarity"), &OpenSimplexNoise::get_lacunarity); + + ClassDB::bind_method(D_METHOD("get_image", "width", "height"), &OpenSimplexNoise::get_image); + ClassDB::bind_method(D_METHOD("get_seamless_image", "size"), &OpenSimplexNoise::get_seamless_image); + + ClassDB::bind_method(D_METHOD("get_noise_2d", "x", "y"), &OpenSimplexNoise::get_noise_2d); + ClassDB::bind_method(D_METHOD("get_noise_3d", "x", "y", "z"), &OpenSimplexNoise::get_noise_3d); + ClassDB::bind_method(D_METHOD("get_noise_4d", "x", "y", "z", "w"), &OpenSimplexNoise::get_noise_4d); + + ClassDB::bind_method(D_METHOD("get_noise_2dv", "pos"), &OpenSimplexNoise::get_noise_2dv); + ClassDB::bind_method(D_METHOD("get_noise_3dv", "pos"), &OpenSimplexNoise::get_noise_3dv); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "seed"), "set_seed", "get_seed"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "octaves", PROPERTY_HINT_RANGE, "1,6,1"), "set_octaves", "get_octaves"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "period", PROPERTY_HINT_RANGE, "0.1,256.0,0.1"), "set_period", "get_period"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "persistence", PROPERTY_HINT_RANGE, "0.0,1.0,0.001"), "set_persistence", "get_persistence"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "lacunarity", PROPERTY_HINT_RANGE, "0.1,4.0,0.01"), "set_lacunarity", "get_lacunarity"); +} + +float OpenSimplexNoise::get_noise_2d(float x, float y) { + + x /= period; + y /= period; + + float amp = 1.0; + float max = 1.0; + float sum = _get_octave_noise_2d(0, x, y); + + int i = 0; + while (++i < octaves) { + x *= lacunarity; + y *= lacunarity; + amp *= persistence; + max += amp; + sum += _get_octave_noise_2d(i, x, y) * amp; + } + + return sum / max; +} + +float OpenSimplexNoise::get_noise_3d(float x, float y, float z) { + + x /= period; + y /= period; + z /= period; + + float amp = 1.0; + float max = 1.0; + float sum = _get_octave_noise_3d(0, x, y, z); + + int i = 0; + while (++i < octaves) { + x *= lacunarity; + y *= lacunarity; + z *= lacunarity; + amp *= persistence; + max += amp; + sum += _get_octave_noise_3d(i, x, y, z) * amp; + } + + return sum / max; +} + +float OpenSimplexNoise::get_noise_4d(float x, float y, float z, float w) { + + x /= period; + y /= period; + z /= period; + w /= period; + + float amp = 1.0; + float max = 1.0; + float sum = _get_octave_noise_4d(0, x, y, z, w); + + int i = 0; + while (++i < octaves) { + x *= lacunarity; + y *= lacunarity; + z *= lacunarity; + w *= lacunarity; + amp *= persistence; + max += amp; + sum += _get_octave_noise_4d(i, x, y, z, w) * amp; + } + + return sum / max; +} diff --git a/modules/opensimplex/open_simplex_noise.h b/modules/opensimplex/open_simplex_noise.h new file mode 100644 index 0000000000..a9bee266e8 --- /dev/null +++ b/modules/opensimplex/open_simplex_noise.h @@ -0,0 +1,93 @@ +/*************************************************************************/ +/* open_simplex_noise.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 OPEN_SIMPLEX_NOISE_H +#define OPEN_SIMPLEX_NOISE_H + +#include "core/image.h" +#include "core/reference.h" +#include "scene/resources/texture.h" + +#include "thirdparty/misc/open-simplex-noise.h" + +class OpenSimplexNoise : public Resource { + GDCLASS(OpenSimplexNoise, Resource) + OBJ_SAVE_TYPE(OpenSimplexNoise); + + osn_context contexts[6]; + + int seed; + float persistence; // Controls details, value in [0,1]. Higher increases grain, lower increases smoothness. + int octaves; // Number of noise layers + float period; // Distance above which we start to see similarities. The higher, the longer "hills" will be on a terrain. + float lacunarity; // Controls period change across octaves. 2 is usually a good value to address all detail levels. + +public: + OpenSimplexNoise(); + ~OpenSimplexNoise(); + + void _init_seeds(); + + void set_seed(int seed); + int get_seed(); + + void set_octaves(int p_octaves); + int get_octaves() const { return octaves; } + + void set_period(float p_period); + float get_period() const { return period; } + + void set_persistence(float p_persistence); + float get_persistence() const { return persistence; } + + void set_lacunarity(float p_lacunarity); + float get_lacunarity() const { return lacunarity; } + + Ref<Image> get_image(int p_width, int p_height); + Ref<Image> get_seamless_image(int p_size); + + float get_noise_2d(float x, float y); + float get_noise_3d(float x, float y, float z); + float get_noise_4d(float x, float y, float z, float w); + + _FORCE_INLINE_ float _get_octave_noise_2d(int octave, float x, float y) { return open_simplex_noise2(&(contexts[octave]), x, y); } + _FORCE_INLINE_ float _get_octave_noise_3d(int octave, float x, float y, float z) { return open_simplex_noise3(&(contexts[octave]), x, y, z); } + _FORCE_INLINE_ float _get_octave_noise_4d(int octave, float x, float y, float z, float w) { return open_simplex_noise4(&(contexts[octave]), x, y, z, w); } + + // Convenience + + _FORCE_INLINE_ float get_noise_2dv(Vector2 v) { return get_noise_2d(v.x, v.y); } + _FORCE_INLINE_ float get_noise_3dv(Vector3 v) { return get_noise_3d(v.x, v.y, v.z); } + +protected: + static void _bind_methods(); +}; + +#endif // OPEN_SIMPLEX_NOISE_H diff --git a/modules/opensimplex/register_types.cpp b/modules/opensimplex/register_types.cpp new file mode 100644 index 0000000000..d1c77da257 --- /dev/null +++ b/modules/opensimplex/register_types.cpp @@ -0,0 +1,42 @@ +/*************************************************************************/ +/* register_types.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 "register_types.h" +#include "noise_texture.h" +#include "open_simplex_noise.h" + +void register_opensimplex_types() { + + ClassDB::register_class<OpenSimplexNoise>(); + ClassDB::register_class<NoiseTexture>(); +} + +void unregister_opensimplex_types() { +} diff --git a/modules/opensimplex/register_types.h b/modules/opensimplex/register_types.h new file mode 100644 index 0000000000..5e71a30ea6 --- /dev/null +++ b/modules/opensimplex/register_types.h @@ -0,0 +1,32 @@ +/*************************************************************************/ +/* register_types.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. */ +/*************************************************************************/ + +void register_opensimplex_types(); +void unregister_opensimplex_types(); diff --git a/modules/opus/SCsub b/modules/opus/SCsub index 6f643ef08c..508aec7057 100644 --- a/modules/opus/SCsub +++ b/modules/opus/SCsub @@ -3,7 +3,6 @@ Import('env') Import('env_modules') - stub = True env_opus = env_modules.Clone() @@ -198,7 +197,10 @@ if env['builtin_opus']: thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources + opus_sources_silk] - env_opus.add_source_files(env.modules_sources, thirdparty_sources) + # also requires libogg + if env['builtin_libogg']: + env_opus.Append(CPPPATH=["#thirdparty/libogg"]) + env_opus.Append(CFLAGS=["-DHAVE_CONFIG_H"]) thirdparty_include_paths = [ @@ -211,9 +213,9 @@ if env['builtin_opus']: ] env_opus.Append(CPPPATH=[thirdparty_dir + "/" + dir for dir in thirdparty_include_paths]) - # also requires libogg - if env['builtin_libogg']: - env_opus.Append(CPPPATH=["#thirdparty/libogg"]) + env_thirdparty = env_opus.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) if not stub: # Module files diff --git a/modules/opus/audio_stream_opus.cpp b/modules/opus/audio_stream_opus.cpp index 8323ff33ac..3920b3cd6e 100644 --- a/modules/opus/audio_stream_opus.cpp +++ b/modules/opus/audio_stream_opus.cpp @@ -142,7 +142,7 @@ Error AudioStreamPlaybackOpus::_load_stream() { } break; case OP_EBADLINK: // - Failed to find old data after seeking. case OP_EBADTIMESTAMP: // - Timestamp failed the validity checks. - case OP_EBADHEADER: { // - Invalid or mising Opus bitstream header. + case OP_EBADHEADER: { // - Invalid or missing Opus bitstream header. memdelete(f); f = NULL; ERR_FAIL_V(ERR_FILE_CORRUPT); @@ -208,7 +208,7 @@ Error AudioStreamPlaybackOpus::set_file(const String &p_file) { } break; case OP_EBADLINK: // - Failed to find old data after seeking. case OP_EBADTIMESTAMP: // - Timestamp failed the validity checks. - case OP_EBADHEADER: { // - Invalid or mising Opus bitstream header. + case OP_EBADHEADER: { // - Invalid or missing Opus bitstream header. memdelete(f); f = NULL; ERR_FAIL_V(ERR_FILE_CORRUPT); diff --git a/modules/opus/audio_stream_opus.h b/modules/opus/audio_stream_opus.h index 3ffdaf2c18..c004adeb77 100644 --- a/modules/opus/audio_stream_opus.h +++ b/modules/opus/audio_stream_opus.h @@ -31,8 +31,8 @@ #ifndef AUDIO_STREAM_OPUS_H #define AUDIO_STREAM_OPUS_H -#include "io/resource_loader.h" -#include "os/file_access.h" +#include "core/io/resource_loader.h" +#include "core/os/file_access.h" #include "scene/resources/audio_stream.h" #include <opus/opusfile.h> diff --git a/modules/pvr/SCsub b/modules/pvr/SCsub index ddca7a794e..2e4a792a36 100644 --- a/modules/pvr/SCsub +++ b/modules/pvr/SCsub @@ -17,8 +17,11 @@ thirdparty_sources = [ ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env_pvr.add_source_files(env.modules_sources, thirdparty_sources) env_pvr.Append(CPPPATH=[thirdparty_dir]) +env_thirdparty = env_pvr.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + # Godot source files env_pvr.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/pvr/texture_loader_pvr.cpp b/modules/pvr/texture_loader_pvr.cpp index 6ec44023c1..e6718eb4a2 100644 --- a/modules/pvr/texture_loader_pvr.cpp +++ b/modules/pvr/texture_loader_pvr.cpp @@ -31,7 +31,7 @@ #include "texture_loader_pvr.h" #include "PvrTcEncoder.h" #include "RgbaBitmap.h" -#include "os/file_access.h" +#include "core/os/file_access.h" #include <string.h> static void _pvrtc_decompress(Image *p_img); diff --git a/modules/pvr/texture_loader_pvr.h b/modules/pvr/texture_loader_pvr.h index 9369178336..c859a4cdda 100644 --- a/modules/pvr/texture_loader_pvr.h +++ b/modules/pvr/texture_loader_pvr.h @@ -31,7 +31,7 @@ #ifndef TEXTURE_LOADER_PVR_H #define TEXTURE_LOADER_PVR_H -#include "io/resource_loader.h" +#include "core/io/resource_loader.h" #include "scene/resources/texture.h" class ResourceFormatPVR : public ResourceFormatLoader { diff --git a/modules/recast/SCsub b/modules/recast/SCsub index f56be72b24..4a06653968 100644 --- a/modules/recast/SCsub +++ b/modules/recast/SCsub @@ -23,10 +23,11 @@ if env['builtin_recast']: ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_recast.add_source_files(env.modules_sources, thirdparty_sources) env_recast.Append(CPPPATH=[thirdparty_dir + "/Include"]) + env_thirdparty = env_recast.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + # Godot source files env_recast.add_source_files(env.modules_sources, "*.cpp") - -Export('env') diff --git a/modules/recast/navigation_mesh_editor_plugin.cpp b/modules/recast/navigation_mesh_editor_plugin.cpp index 8556b7aa0a..d121a82d9e 100644 --- a/modules/recast/navigation_mesh_editor_plugin.cpp +++ b/modules/recast/navigation_mesh_editor_plugin.cpp @@ -30,8 +30,8 @@ #include "navigation_mesh_editor_plugin.h" -#include "io/marshalls.h" -#include "io/resource_saver.h" +#include "core/io/marshalls.h" +#include "core/io/resource_saver.h" #include "scene/3d/mesh_instance.h" #include "scene/gui/box_container.h" @@ -103,24 +103,25 @@ void NavigationMeshEditor::_bind_methods() { NavigationMeshEditor::NavigationMeshEditor() { bake_hbox = memnew(HBoxContainer); + button_bake = memnew(ToolButton); - button_bake->set_text(TTR("Bake!")); + bake_hbox->add_child(button_bake); button_bake->set_toggle_mode(true); - button_reset = memnew(Button); - button_bake->set_tooltip(TTR("Bake the navigation mesh.") + "\n"); + button_bake->set_text(TTR("Bake NavMesh")); + button_bake->connect("pressed", this, "_bake_pressed"); - bake_info = memnew(Label); - bake_hbox->add_child(button_bake); + button_reset = memnew(ToolButton); bake_hbox->add_child(button_reset); + // No button text, we only use a revert icon which is set when entering the tree. + button_reset->set_tooltip(TTR("Clear the navigation mesh.")); + button_reset->connect("pressed", this, "_clear_pressed"); + + bake_info = memnew(Label); bake_hbox->add_child(bake_info); err_dialog = memnew(AcceptDialog); add_child(err_dialog); node = NULL; - - button_bake->connect("pressed", this, "_bake_pressed"); - button_reset->connect("pressed", this, "_clear_pressed"); - button_reset->set_tooltip(TTR("Clear the navigation mesh.")); } NavigationMeshEditor::~NavigationMeshEditor() { diff --git a/modules/recast/navigation_mesh_editor_plugin.h b/modules/recast/navigation_mesh_editor_plugin.h index 4f3e222002..914d9ab8b9 100644 --- a/modules/recast/navigation_mesh_editor_plugin.h +++ b/modules/recast/navigation_mesh_editor_plugin.h @@ -43,8 +43,8 @@ class NavigationMeshEditor : public Control { AcceptDialog *err_dialog; HBoxContainer *bake_hbox; - Button *button_bake; - Button *button_reset; + ToolButton *button_bake; + ToolButton *button_reset; Label *bake_info; NavigationMeshInstance *node; diff --git a/modules/recast/navigation_mesh_generator.h b/modules/recast/navigation_mesh_generator.h index 3588539ef1..2f2f57d721 100644 --- a/modules/recast/navigation_mesh_generator.h +++ b/modules/recast/navigation_mesh_generator.h @@ -31,9 +31,9 @@ #ifndef NAVIGATION_MESH_GENERATOR_H #define NAVIGATION_MESH_GENERATOR_H +#include "core/os/thread.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" -#include "os/thread.h" #include "scene/3d/mesh_instance.h" #include "scene/3d/navigation_mesh.h" #include "scene/resources/shape.h" diff --git a/modules/regex/SCsub b/modules/regex/SCsub index 4b8d5e9283..99c25add45 100644 --- a/modules/regex/SCsub +++ b/modules/regex/SCsub @@ -4,15 +4,16 @@ Import('env') Import('env_modules') env_regex = env_modules.Clone() -env_regex.Append(CPPFLAGS=["-DPCRE2_CODE_UNIT_WIDTH=0"]) -env_regex.add_source_files(env.modules_sources, "*.cpp") if env['builtin_pcre2']: jit_blacklist = ['javascript', 'uwp'] + thirdparty_dir = '#thirdparty/pcre2/src/' thirdparty_flags = ['-DPCRE2_STATIC', '-DHAVE_CONFIG_H'] + if 'platform' in env and env['platform'] not in jit_blacklist: thirdparty_flags.append('-DSUPPORT_JIT') + thirdparty_sources = [ "pcre2_auto_possess.c", "pcre2_chartables.c", @@ -42,15 +43,21 @@ if env['builtin_pcre2']: "pcre2_valid_utf.c", "pcre2_xclass.c", ] + thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] + env_regex.Append(CPPPATH=[thirdparty_dir]) env_regex.Append(CPPFLAGS=thirdparty_flags) + def pcre2_builtin(width): - env_pcre2 = env_modules.Clone() + env_pcre2 = env_regex.Clone() + env_pcre2.disable_warnings() env_pcre2["OBJSUFFIX"] = "_" + width + env_pcre2["OBJSUFFIX"] - env_pcre2.Append(CPPPATH=[thirdparty_dir]) env_pcre2.add_source_files(env.modules_sources, thirdparty_sources) - env_pcre2.Append(CPPFLAGS=thirdparty_flags) env_pcre2.Append(CPPFLAGS=["-DPCRE2_CODE_UNIT_WIDTH=" + width]) + pcre2_builtin("16") pcre2_builtin("32") + +env_regex.Append(CPPFLAGS=["-DPCRE2_CODE_UNIT_WIDTH=0"]) +env_regex.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/regex/regex.cpp b/modules/regex/regex.cpp index 733f32277b..bdd3e31eb8 100644 --- a/modules/regex/regex.cpp +++ b/modules/regex/regex.cpp @@ -205,6 +205,8 @@ Error RegEx::compile(const String &p_pattern) { code = pcre2_compile_16(p, pattern.length(), flags, &err, &offset, cctx); + pcre2_compile_context_free_16(cctx); + if (!code) { PCRE2_UCHAR16 buf[256]; pcre2_get_error_message_16(err, buf, 256); @@ -221,6 +223,8 @@ Error RegEx::compile(const String &p_pattern) { code = pcre2_compile_32(p, pattern.length(), flags, &err, &offset, cctx); + pcre2_compile_context_free_32(cctx); + if (!code) { PCRE2_UCHAR32 buf[256]; pcre2_get_error_message_32(err, buf, 256); @@ -285,6 +289,8 @@ Ref<RegExMatch> RegEx::search(const String &p_subject, int p_offset, int p_end) if (res < 0) { pcre2_match_data_free_32(match); + pcre2_match_context_free_32(mctx); + return NULL; } diff --git a/modules/regex/register_types.cpp b/modules/regex/register_types.cpp index 14eba69ee0..73e2c5022d 100644 --- a/modules/regex/register_types.cpp +++ b/modules/regex/register_types.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "register_types.h" -#include "class_db.h" +#include "core/class_db.h" #include "regex.h" void register_regex_types() { diff --git a/modules/squish/SCsub b/modules/squish/SCsub index 127f22d798..3be85a1efa 100644 --- a/modules/squish/SCsub +++ b/modules/squish/SCsub @@ -22,8 +22,11 @@ if env['builtin_squish']: thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_squish.add_source_files(env.modules_sources, thirdparty_sources) env_squish.Append(CPPPATH=[thirdparty_dir]) + env_thirdparty = env_squish.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + # Godot source files env_squish.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/squish/image_compress_squish.cpp b/modules/squish/image_compress_squish.cpp index 653dd82351..26cb76011c 100644 --- a/modules/squish/image_compress_squish.cpp +++ b/modules/squish/image_compress_squish.cpp @@ -30,7 +30,7 @@ #include "image_compress_squish.h" -#include "print_string.h" +#include "core/print_string.h" #include <squish.h> @@ -193,8 +193,8 @@ void image_compress_squish(Image *p_image, float p_lossy_quality, Image::Compres int src_ofs = p_image->get_mipmap_offset(i); squish::CompressImage(&rb[src_ofs], w, h, &wb[dst_ofs], squish_comp); dst_ofs += (MAX(4, bw) * MAX(4, bh)) >> shift; - w >>= 1; - h >>= 1; + w = MAX(w / 2, 1); + h = MAX(h / 2, 1); } rb = PoolVector<uint8_t>::Read(); diff --git a/modules/squish/image_compress_squish.h b/modules/squish/image_compress_squish.h index 6da947beea..dfebdc955f 100644 --- a/modules/squish/image_compress_squish.h +++ b/modules/squish/image_compress_squish.h @@ -31,7 +31,7 @@ #ifndef IMAGE_COMPRESS_SQUISH_H #define IMAGE_COMPRESS_SQUISH_H -#include "image.h" +#include "core/image.h" void image_compress_squish(Image *p_image, float p_lossy_quality, Image::CompressSource p_source); void image_decompress_squish(Image *p_image); diff --git a/modules/stb_vorbis/SCsub b/modules/stb_vorbis/SCsub index 897d05961c..d14939a3b1 100644 --- a/modules/stb_vorbis/SCsub +++ b/modules/stb_vorbis/SCsub @@ -3,8 +3,14 @@ Import('env') Import('env_modules') +env_stb_vorbis = env_modules.Clone() + # Thirdparty source files +thirdparty_sources = ["#thirdparty/misc/stb_vorbis.c"] -env_stb_vorbis = env_modules.Clone() +env_thirdparty = env_stb_vorbis.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) +# Godot's own source files env_stb_vorbis.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp index 0e533d3978..5dbe0b4b00 100644 --- a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp +++ b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp @@ -30,12 +30,7 @@ #include "audio_stream_ogg_vorbis.h" -#include "os/file_access.h" - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" -#include "thirdparty/misc/stb_vorbis.c" -#pragma GCC diagnostic pop +#include "core/os/file_access.h" void AudioStreamPlaybackOGGVorbis::_mix_internal(AudioFrame *p_buffer, int p_frames) { diff --git a/modules/stb_vorbis/audio_stream_ogg_vorbis.h b/modules/stb_vorbis/audio_stream_ogg_vorbis.h index d7bc7cc0d7..8b42111847 100644 --- a/modules/stb_vorbis/audio_stream_ogg_vorbis.h +++ b/modules/stb_vorbis/audio_stream_ogg_vorbis.h @@ -31,15 +31,10 @@ #ifndef AUDIO_STREAM_STB_VORBIS_H #define AUDIO_STREAM_STB_VORBIS_H -#include "io/resource_loader.h" +#include "core/io/resource_loader.h" #include "servers/audio/audio_stream.h" -#define STB_VORBIS_HEADER_ONLY -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" -#include "thirdparty/misc/stb_vorbis.c" -#pragma GCC diagnostic pop -#undef STB_VORBIS_HEADER_ONLY +#include "thirdparty/misc/stb_vorbis.h" class AudioStreamOGGVorbis; diff --git a/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp b/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp index c8acdb689a..74f2e68206 100644 --- a/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp +++ b/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp @@ -30,8 +30,8 @@ #include "resource_importer_ogg_vorbis.h" -#include "io/resource_saver.h" -#include "os/file_access.h" +#include "core/io/resource_saver.h" +#include "core/os/file_access.h" #include "scene/resources/texture.h" String ResourceImporterOGGVorbis::get_importer_name() const { diff --git a/modules/stb_vorbis/resource_importer_ogg_vorbis.h b/modules/stb_vorbis/resource_importer_ogg_vorbis.h index a1847545aa..82a03cd207 100644 --- a/modules/stb_vorbis/resource_importer_ogg_vorbis.h +++ b/modules/stb_vorbis/resource_importer_ogg_vorbis.h @@ -32,7 +32,7 @@ #define RESOURCEIMPORTEROGGVORBIS_H #include "audio_stream_ogg_vorbis.h" -#include "io/resource_import.h" +#include "core/io/resource_import.h" class ResourceImporterOGGVorbis : public ResourceImporter { GDCLASS(ResourceImporterOGGVorbis, ResourceImporter) diff --git a/modules/svg/SCsub b/modules/svg/SCsub index a41e0703bd..22f0b1e3eb 100644 --- a/modules/svg/SCsub +++ b/modules/svg/SCsub @@ -1,6 +1,9 @@ #!/usr/bin/env python Import('env') +Import('env_modules') + +env_svg = env_modules.Clone() # Thirdparty source files thirdparty_dir = "#thirdparty/nanosvg/" @@ -9,11 +12,15 @@ thirdparty_sources = [ ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env.add_source_files(env.modules_sources, thirdparty_sources) +env_svg.Append(CPPPATH=[thirdparty_dir]) +# FIXME: Needed in editor/editor_themes.cpp for now, but ideally there +# shouldn't be a dependency on modules/ and its own 3rd party deps. env.Append(CPPPATH=[thirdparty_dir]) env.Append(CCFLAGS=["-DSVG_ENABLED"]) -# Godot's own source files -env.add_source_files(env.modules_sources, "*.cpp") +env_thirdparty = env_svg.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) -Export('env') +# Godot's own source files +env_svg.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/svg/image_loader_svg.cpp b/modules/svg/image_loader_svg.cpp index 8ccd229f3d..ccb7d93885 100644 --- a/modules/svg/image_loader_svg.cpp +++ b/modules/svg/image_loader_svg.cpp @@ -30,10 +30,9 @@ #include "image_loader_svg.h" -#include "os/os.h" -#include "print_string.h" - -#include <ustring.h> +#include "core/os/os.h" +#include "core/print_string.h" +#include "core/ustring.h" void SVGRasterizer::rasterize(NSVGimage *p_image, float p_tx, float p_ty, float p_scale, unsigned char *p_dst, int p_w, int p_h, int p_stride) { nsvgRasterize(rasterizer, p_image, p_tx, p_ty, p_scale, p_dst, p_w, p_h, p_stride); diff --git a/modules/svg/image_loader_svg.h b/modules/svg/image_loader_svg.h index 63854da2f6..ff361ad800 100644 --- a/modules/svg/image_loader_svg.h +++ b/modules/svg/image_loader_svg.h @@ -31,8 +31,8 @@ #ifndef IMAGE_LOADER_SVG_H #define IMAGE_LOADER_SVG_H -#include "io/image_loader.h" -#include "ustring.h" +#include "core/io/image_loader.h" +#include "core/ustring.h" #include <nanosvg.h> #include <nanosvgrast.h> diff --git a/modules/tga/image_loader_tga.cpp b/modules/tga/image_loader_tga.cpp index d4fa88afa7..9bc24017fc 100644 --- a/modules/tga/image_loader_tga.cpp +++ b/modules/tga/image_loader_tga.cpp @@ -30,8 +30,8 @@ #include "image_loader_tga.h" -#include "os/os.h" -#include "print_string.h" +#include "core/os/os.h" +#include "core/print_string.h" Error ImageLoaderTGA::decode_tga_rle(const uint8_t *p_compressed_buffer, size_t p_pixel_size, uint8_t *p_uncompressed_buffer, size_t p_output_size) { Error error; diff --git a/modules/tga/image_loader_tga.h b/modules/tga/image_loader_tga.h index c4b10b7f49..0fe83a54a1 100644 --- a/modules/tga/image_loader_tga.h +++ b/modules/tga/image_loader_tga.h @@ -31,7 +31,7 @@ #ifndef IMAGE_LOADER_TGA_H #define IMAGE_LOADER_TGA_H -#include "io/image_loader.h" +#include "core/io/image_loader.h" /** @author SaracenOne diff --git a/modules/thekla_unwrap/SCsub b/modules/thekla_unwrap/SCsub index d23ba10d4c..c47c760d5f 100644 --- a/modules/thekla_unwrap/SCsub +++ b/modules/thekla_unwrap/SCsub @@ -54,11 +54,10 @@ if env['builtin_thekla_atlas']: ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_thekla_unwrap.add_source_files(env.modules_sources, thirdparty_sources) - env_thekla_unwrap.Append(CPPPATH=[thirdparty_dir, thirdparty_dir + "/poshlib", thirdparty_dir + "/nvcore", thirdparty_dir + "/nvmesh"]) + env_thekla_unwrap.Append(CPPPATH=[thirdparty_dir, thirdparty_dir + "poshlib", thirdparty_dir + "nvcore", thirdparty_dir + "nvmesh"]) # upstream uses c++11 - if (not env_thekla_unwrap.msvc): + if (not env.msvc): env_thekla_unwrap.Append(CXXFLAGS="-std=c++11") if env["platform"] == 'x11': @@ -78,5 +77,9 @@ if env['builtin_thekla_atlas']: env_thekla_unwrap.Append(CCFLAGS=["-DNV_OS_MINGW", "-DNV_CC_GNUC", "-DPOSH_COMPILER_GCC", "-U__STRICT_ANSI__"]) env.Append(LIBS=["dbghelp"]) + env_thirdparty = env_thekla_unwrap.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + # Godot source files env_thekla_unwrap.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/thekla_unwrap/config.py b/modules/thekla_unwrap/config.py index bd092bdc16..fad6095064 100644 --- a/modules/thekla_unwrap/config.py +++ b/modules/thekla_unwrap/config.py @@ -1,5 +1,6 @@ def can_build(env, platform): - return (env['tools'] and platform not in ["android", "ios"]) + #return (env['tools'] and platform not in ["android", "ios"]) + return False def configure(env): pass diff --git a/modules/thekla_unwrap/register_types.cpp b/modules/thekla_unwrap/register_types.cpp index c74cbd9d18..8e733d1ad2 100644 --- a/modules/thekla_unwrap/register_types.cpp +++ b/modules/thekla_unwrap/register_types.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "register_types.h" -#include "error_macros.h" +#include "core/error_macros.h" #include "thirdparty/thekla_atlas/thekla/thekla_atlas.h" #include <stdio.h> diff --git a/modules/theora/SCsub b/modules/theora/SCsub index 9015c2c354..98c4274a7e 100644 --- a/modules/theora/SCsub +++ b/modules/theora/SCsub @@ -70,7 +70,6 @@ if env['builtin_libtheora']: thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_theora.add_source_files(env.modules_sources, thirdparty_sources) env_theora.Append(CPPPATH=[thirdparty_dir]) # also requires libogg and libvorbis @@ -79,5 +78,9 @@ if env['builtin_libtheora']: if env['builtin_libvorbis']: env_theora.Append(CPPPATH=["#thirdparty/libvorbis"]) + env_thirdparty = env_theora.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + # Godot source files env_theora.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp index 68087ac01f..2a6bb0783b 100644 --- a/modules/theora/video_stream_theora.cpp +++ b/modules/theora/video_stream_theora.cpp @@ -30,8 +30,8 @@ #include "video_stream_theora.h" -#include "os/os.h" -#include "project_settings.h" +#include "core/os/os.h" +#include "core/project_settings.h" #include "thirdparty/misc/yuv2rgb.h" @@ -332,8 +332,8 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) { int w; int h; - w = (ti.pic_x + ti.frame_width + 1 & ~1) - (ti.pic_x & ~1); - h = (ti.pic_y + ti.frame_height + 1 & ~1) - (ti.pic_y & ~1); + w = ((ti.pic_x + ti.frame_width + 1) & ~1) - (ti.pic_x & ~1); + h = ((ti.pic_y + ti.frame_height + 1) & ~1) - (ti.pic_y & ~1); size.x = w; size.y = h; @@ -439,7 +439,7 @@ void VideoStreamPlaybackTheora::update(float p_delta) { } } - int tr = vorbis_synthesis_read(&vd, ret - to_read); + vorbis_synthesis_read(&vd, ret - to_read); audio_frames_wrote += ret - to_read; @@ -730,7 +730,6 @@ RES ResourceFormatLoaderTheora::load(const String &p_path, const String &p_origi if (r_error) { *r_error = ERR_CANT_OPEN; } - memdelete(f); return RES(); } diff --git a/modules/theora/video_stream_theora.h b/modules/theora/video_stream_theora.h index 1aba3f56da..4be723f85b 100644 --- a/modules/theora/video_stream_theora.h +++ b/modules/theora/video_stream_theora.h @@ -31,11 +31,11 @@ #ifndef VIDEO_STREAM_THEORA_H #define VIDEO_STREAM_THEORA_H -#include "io/resource_loader.h" -#include "os/file_access.h" -#include "os/semaphore.h" -#include "os/thread.h" -#include "ring_buffer.h" +#include "core/io/resource_loader.h" +#include "core/os/file_access.h" +#include "core/os/semaphore.h" +#include "core/os/thread.h" +#include "core/ring_buffer.h" #include "scene/resources/video_stream.h" #include "servers/audio_server.h" diff --git a/modules/tinyexr/SCsub b/modules/tinyexr/SCsub index 38fd00cc65..3e7bda2bca 100644 --- a/modules/tinyexr/SCsub +++ b/modules/tinyexr/SCsub @@ -13,8 +13,11 @@ thirdparty_sources = [ ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env_tinyexr.add_source_files(env.modules_sources, thirdparty_sources) env_tinyexr.Append(CPPPATH=[thirdparty_dir]) +env_thirdparty = env_tinyexr.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + # Godot's own source files env_tinyexr.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/tinyexr/image_loader_tinyexr.cpp b/modules/tinyexr/image_loader_tinyexr.cpp index 0abefe11ee..63f0781028 100644 --- a/modules/tinyexr/image_loader_tinyexr.cpp +++ b/modules/tinyexr/image_loader_tinyexr.cpp @@ -30,8 +30,8 @@ #include "image_loader_tinyexr.h" -#include "os/os.h" -#include "print_string.h" +#include "core/os/os.h" +#include "core/print_string.h" #include "thirdparty/tinyexr/tinyexr.h" @@ -129,15 +129,45 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, FileAccess *f, bool p_f PoolVector<uint8_t> imgdata; Image::Format format; + int output_channels = 0; if (idxA > 0) { imgdata.resize(exr_image.width * exr_image.height * 8); //RGBA16 format = Image::FORMAT_RGBAH; + output_channels = 4; } else { imgdata.resize(exr_image.width * exr_image.height * 6); //RGB16 format = Image::FORMAT_RGBH; + output_channels = 3; + } + + EXRTile single_image_tile; + int num_tiles; + int tile_width = 0; + int tile_height = 0; + + const EXRTile *exr_tiles; + + if (!exr_header.tiled) { + single_image_tile.images = exr_image.images; + single_image_tile.width = exr_image.width; + single_image_tile.height = exr_image.height; + single_image_tile.level_x = exr_image.width; + single_image_tile.level_y = exr_image.height; + single_image_tile.offset_x = 0; + single_image_tile.offset_y = 0; + + exr_tiles = &single_image_tile; + num_tiles = 1; + tile_width = exr_image.width; + tile_height = exr_image.height; + } else { + tile_width = exr_header.tile_size_x; + tile_height = exr_header.tile_size_y; + num_tiles = exr_image.num_tiles; + exr_tiles = exr_image.tiles; } { @@ -145,22 +175,51 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, FileAccess *f, bool p_f uint16_t *iw = (uint16_t *)wd.ptr(); // Assume `out_rgba` have enough memory allocated. - for (int i = 0; i < exr_image.width * exr_image.height; i++) { + for (int tile_index = 0; tile_index < num_tiles; tile_index++) { - Color color( - reinterpret_cast<float **>(exr_image.images)[idxR][i], - reinterpret_cast<float **>(exr_image.images)[idxG][i], - reinterpret_cast<float **>(exr_image.images)[idxB][i]); + const EXRTile &tile = exr_tiles[tile_index]; - if (p_force_linear) - color = color.to_linear(); + int tw = tile.width; + int th = tile.height; - *iw++ = Math::make_half_float(color.r); - *iw++ = Math::make_half_float(color.g); - *iw++ = Math::make_half_float(color.b); + const float *r_channel_start = reinterpret_cast<const float *>(tile.images[idxR]); + const float *g_channel_start = reinterpret_cast<const float *>(tile.images[idxG]); + const float *b_channel_start = reinterpret_cast<const float *>(tile.images[idxB]); + const float *a_channel_start = NULL; if (idxA > 0) { - *iw++ = Math::make_half_float(reinterpret_cast<float **>(exr_image.images)[idxA][i]); + a_channel_start = reinterpret_cast<const float *>(tile.images[idxA]); + } + + uint16_t *first_row_w = iw + (tile.offset_y * tile_height * exr_image.width + tile.offset_x * tile_width) * output_channels; + + for (int y = 0; y < th; y++) { + const float *r_channel = r_channel_start + y * tile_width; + const float *g_channel = g_channel_start + y * tile_width; + const float *b_channel = b_channel_start + y * tile_width; + const float *a_channel = NULL; + + if (a_channel_start) { + a_channel = a_channel_start + y * tile_width; + } + + uint16_t *row_w = first_row_w + (y * exr_image.width * output_channels); + + for (int x = 0; x < tw; x++) { + + Color color(*r_channel++, *g_channel++, *b_channel++); + + if (p_force_linear) + color = color.to_linear(); + + *row_w++ = Math::make_half_float(color.r); + *row_w++ = Math::make_half_float(color.g); + *row_w++ = Math::make_half_float(color.b); + + if (idxA > 0) { + *row_w++ = Math::make_half_float(*a_channel++); + } + } } } } diff --git a/modules/tinyexr/image_loader_tinyexr.h b/modules/tinyexr/image_loader_tinyexr.h index 6706e0972a..a6ef9000e5 100644 --- a/modules/tinyexr/image_loader_tinyexr.h +++ b/modules/tinyexr/image_loader_tinyexr.h @@ -31,7 +31,7 @@ #ifndef IMAGE_LOADER_TINYEXR_H #define IMAGE_LOADER_TINYEXR_H -#include "io/image_loader.h" +#include "core/io/image_loader.h" /** @author Juan Linietsky <reduzio@gmail.com> diff --git a/modules/upnp/SCsub b/modules/upnp/SCsub index cde231867f..2b15f7aee2 100644 --- a/modules/upnp/SCsub +++ b/modules/upnp/SCsub @@ -25,8 +25,12 @@ if env['builtin_miniupnpc']: ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_upnp.add_source_files(env.modules_sources, thirdparty_sources) env_upnp.Append(CPPPATH=[thirdparty_dir]) env_upnp.Append(CPPFLAGS=["-DMINIUPNP_STATICLIB"]) + env_thirdparty = env_upnp.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + +# Godot source files env_upnp.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/upnp/doc_classes/UPNP.xml b/modules/upnp/doc_classes/UPNP.xml index 30be9c836b..b98327c60d 100644 --- a/modules/upnp/doc_classes/UPNP.xml +++ b/modules/upnp/doc_classes/UPNP.xml @@ -69,7 +69,7 @@ </argument> <description> Discovers local [UPNPDevice]s. Clears the list of previously discovered devices. - Filters for IGD (InternetGatewayDevice) type devices by default, as those manage port forwarding. [code]timeout[/code] is the time to wait for responses in miliseconds. [code]ttl[/code] is the time-to-live; only touch this if you know what you're doing. + Filters for IGD (InternetGatewayDevice) type devices by default, as those manage port forwarding. [code]timeout[/code] is the time to wait for responses in milliseconds. [code]ttl[/code] is the time-to-live; only touch this if you know what you're doing. See [enum UPNPResult] for possible return values. </description> </method> diff --git a/modules/upnp/register_types.cpp b/modules/upnp/register_types.cpp index c79155c4d2..13088c94dd 100644 --- a/modules/upnp/register_types.cpp +++ b/modules/upnp/register_types.cpp @@ -29,7 +29,9 @@ /*************************************************************************/ #include "register_types.h" -#include "error_macros.h" + +#include "core/error_macros.h" + #include "upnp.h" #include "upnpdevice.h" diff --git a/modules/upnp/upnp.cpp b/modules/upnp/upnp.cpp index 32fdfe22f8..69e054f5c5 100644 --- a/modules/upnp/upnp.cpp +++ b/modules/upnp/upnp.cpp @@ -29,8 +29,10 @@ /*************************************************************************/ #include "upnp.h" -#include "miniupnpc/miniwget.h" -#include "upnpcommands.h" + +#include <miniupnpc/miniwget.h> +#include <miniupnpc/upnpcommands.h> + #include <stdlib.h> bool UPNP::is_common_device(const String &dev) const { diff --git a/modules/upnp/upnp.h b/modules/upnp/upnp.h index fb0c0f30a0..8f0a972c22 100644 --- a/modules/upnp/upnp.h +++ b/modules/upnp/upnp.h @@ -31,9 +31,11 @@ #ifndef GODOT_UPNP_H #define GODOT_UPNP_H -#include "miniupnpc/miniupnpc.h" +#include "core/reference.h" + #include "upnpdevice.h" -#include <reference.h> + +#include <miniupnpc/miniupnpc.h> class UPNP : public Reference { diff --git a/modules/upnp/upnpdevice.cpp b/modules/upnp/upnpdevice.cpp index a5959cf649..8f935fbd82 100644 --- a/modules/upnp/upnpdevice.cpp +++ b/modules/upnp/upnpdevice.cpp @@ -29,8 +29,10 @@ /*************************************************************************/ #include "upnpdevice.h" + #include "upnp.h" -#include "upnpcommands.h" + +#include <miniupnpc/upnpcommands.h> String UPNPDevice::query_external_address() const { ERR_FAIL_COND_V(!is_valid_gateway(), ""); diff --git a/modules/upnp/upnpdevice.h b/modules/upnp/upnpdevice.h index 25c9fe1ba3..e4c5eb691d 100644 --- a/modules/upnp/upnpdevice.h +++ b/modules/upnp/upnpdevice.h @@ -31,7 +31,7 @@ #ifndef GODOT_UPNPDEVICE_H #define GODOT_UPNPDEVICE_H -#include <reference.h> +#include "core/reference.h" class UPNPDevice : public Reference { diff --git a/modules/visual_script/SCsub b/modules/visual_script/SCsub index 96ee911ba0..3c3d2caa57 100644 --- a/modules/visual_script/SCsub +++ b/modules/visual_script/SCsub @@ -6,5 +6,3 @@ Import('env_modules') env_vs = env_modules.Clone() env_vs.add_source_files(env.modules_sources, "*.cpp") - -Export('env') diff --git a/modules/visual_script/doc_classes/VisualScript.xml b/modules/visual_script/doc_classes/VisualScript.xml index 28764aca40..70849c5a80 100644 --- a/modules/visual_script/doc_classes/VisualScript.xml +++ b/modules/visual_script/doc_classes/VisualScript.xml @@ -9,7 +9,7 @@ You are most likely to use this class via the Visual Script editor or when writing plugins for it. </description> <tutorials> - <link>http://docs.godotengine.org/en/3.0/getting_started/scripting/visual_script/index.html</link> + <link>https://docs.godotengine.org/en/latest/getting_started/scripting/visual_script/index.html</link> </tutorials> <demos> </demos> diff --git a/modules/visual_script/register_types.cpp b/modules/visual_script/register_types.cpp index 11401c0460..6e081817f1 100644 --- a/modules/visual_script/register_types.cpp +++ b/modules/visual_script/register_types.cpp @@ -31,7 +31,7 @@ #include "register_types.h" #include "core/engine.h" -#include "io/resource_loader.h" +#include "core/io/resource_loader.h" #include "visual_script.h" #include "visual_script_builtin_funcs.h" #include "visual_script_editor.h" diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp index bbdec7195f..186e9e63b1 100644 --- a/modules/visual_script/visual_script.cpp +++ b/modules/visual_script/visual_script.cpp @@ -30,8 +30,8 @@ #include "visual_script.h" -#include "os/os.h" -#include "project_settings.h" +#include "core/os/os.h" +#include "core/project_settings.h" #include "scene/main/node.h" #include "visual_script_nodes.h" @@ -48,20 +48,22 @@ bool VisualScriptNode::is_breakpoint() const { void VisualScriptNode::_notification(int p_what) { if (p_what == NOTIFICATION_POSTINITIALIZE) { - - int dvc = get_input_value_port_count(); - for (int i = 0; i < dvc; i++) { - Variant::Type expected = get_input_value_port_info(i).type; - Variant::CallError ce; - default_input_values.push_back(Variant::construct(expected, NULL, 0, ce, false)); - } + _update_input_ports(); } } -void VisualScriptNode::ports_changed_notify() { - +void VisualScriptNode::_update_input_ports() { default_input_values.resize(MAX(default_input_values.size(), get_input_value_port_count())); //let it grow as big as possible, we don't want to lose values on resize + int port_count = get_input_value_port_count(); + for (int i = 0; i < port_count; i++) { + Variant::Type expected = get_input_value_port_info(i).type; + Variant::CallError ce; + set_default_input_value(i, Variant::construct(expected, NULL, 0, ce, false)); + } +} +void VisualScriptNode::ports_changed_notify() { + _update_input_ports(); emit_signal("ports_changed"); } @@ -2697,11 +2699,11 @@ VisualScriptLanguage::VisualScriptLanguage() { _debug_parse_err_file = ""; _debug_call_stack_pos = 0; int dmcs = GLOBAL_DEF("debug/settings/visual_script/max_call_stack", 1024); + ProjectSettings::get_singleton()->set_custom_property_info("debug/settings/visual_script/max_call_stack", PropertyInfo(Variant::INT, "debug/settings/visual_script/max_call_stack", PROPERTY_HINT_RANGE, "1024,4096,1,or_greater")); //minimum is 1024 + if (ScriptDebugger::get_singleton()) { //debugging enabled! _debug_max_call_stack = dmcs; - if (_debug_max_call_stack < 1024) - _debug_max_call_stack = 1024; _call_stack = memnew_arr(CallLevel, _debug_max_call_stack + 1); } else { diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h index 13a8b909b0..bd666447a3 100644 --- a/modules/visual_script/visual_script.h +++ b/modules/visual_script/visual_script.h @@ -31,8 +31,8 @@ #ifndef VISUAL_SCRIPT_H #define VISUAL_SCRIPT_H -#include "os/thread.h" -#include "script_language.h" +#include "core/os/thread.h" +#include "core/script_language.h" class VisualScriptInstance; class VisualScriptNodeInstance; @@ -52,6 +52,7 @@ class VisualScriptNode : public Resource { Array _get_default_input_values() const; void validate_input_default_values(); + void _update_input_ports(); protected: void _notification(int p_what); diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp index 8e98b08b22..60bc54afe4 100644 --- a/modules/visual_script/visual_script_builtin_funcs.cpp +++ b/modules/visual_script/visual_script_builtin_funcs.cpp @@ -30,13 +30,13 @@ #include "visual_script_builtin_funcs.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" +#include "core/class_db.h" +#include "core/func_ref.h" +#include "core/io/marshalls.h" +#include "core/math/math_funcs.h" +#include "core/os/os.h" +#include "core/reference.h" +#include "core/variant_parser.h" const char *VisualScriptBuiltinFunc::func_name[VisualScriptBuiltinFunc::FUNC_MAX] = { "sin", @@ -260,7 +260,12 @@ PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const case MATH_SQRT: { return PropertyInfo(Variant::REAL, "s"); } break; - case MATH_ATAN2: + case MATH_ATAN2: { + if (p_idx == 0) + return PropertyInfo(Variant::REAL, "y"); + else + return PropertyInfo(Variant::REAL, "x"); + } break; case MATH_FMOD: case MATH_FPOSMOD: { if (p_idx == 0) diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index 4471fbd0c4..c5f2070963 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -30,13 +30,13 @@ #include "visual_script_editor.h" +#include "core/object.h" +#include "core/os/input.h" +#include "core/os/keyboard.h" #include "core/script_language.h" +#include "core/variant.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" @@ -321,7 +321,7 @@ protected: p_list->push_back(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, argt)); p_list->push_back(PropertyInfo(script->get_variable_info(var).type, "value", script->get_variable_info(var).hint, script->get_variable_info(var).hint_string, PROPERTY_USAGE_DEFAULT)); // Update this when PropertyHint changes - p_list->push_back(PropertyInfo(Variant::INT, "hint", PROPERTY_HINT_ENUM, "None,Range,ExpRange,Enum,ExpEasing,Length,SpriteFrame,KeyAccel,Flags,Layers2dRender,Layers2dPhysics,Layer3dRender,Layer3dPhysics,File,Dir,GlobalFile,GlobalDir,ResourceType,MultilineText,ColorNoAlpha,ImageCompressLossy,ImageCompressLossLess,ObjectId,String,NodePathToEditedNode,MethodOfVariantType,MethodOfBaseType,MethodOfInstance,MethodOfScript,PropertyOfVariantType,PropertyOfBaseType,PropertyOfInstance,PropertyOfScript,ObjectTooBig")); + p_list->push_back(PropertyInfo(Variant::INT, "hint", PROPERTY_HINT_ENUM, "None,Range,ExpRange,Enum,ExpEasing,Length,SpriteFrame,KeyAccel,Flags,Layers2dRender,Layers2dPhysics,Layer3dRender,Layer3dPhysics,File,Dir,GlobalFile,GlobalDir,ResourceType,MultilineText,PlaceholderText,ColorNoAlpha,ImageCompressLossy,ImageCompressLossLess,ObjectId,String,NodePathToEditedNode,MethodOfVariantType,MethodOfBaseType,MethodOfInstance,MethodOfScript,PropertyOfVariantType,PropertyOfBaseType,PropertyOfInstance,PropertyOfScript,ObjectTooBig,NodePathValidTypes")); p_list->push_back(PropertyInfo(Variant::STRING, "hint_string")); p_list->push_back(PropertyInfo(Variant::BOOL, "export")); } @@ -765,6 +765,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) { } void VisualScriptEditor::_update_members() { + ERR_FAIL_COND(!script.is_valid()); updating_members = true; @@ -1940,7 +1941,7 @@ void VisualScriptEditor::_draw_color_over_button(Object *obj, Color p_color) { button->draw_rect(Rect2(normal->get_offset(), button->get_size() - normal->get_minimum_size()), p_color); } -void VisualScriptEditor::_button_resource_previewed(const String &p_path, const Ref<Texture> &p_preview, Variant p_ud) { +void VisualScriptEditor::_button_resource_previewed(const String &p_path, const Ref<Texture> &p_preview, const Ref<Texture> &p_small_preview, Variant p_ud) { Array ud = p_ud; ERR_FAIL_COND(ud.size() != 2); @@ -2343,7 +2344,7 @@ void VisualScriptEditor::_graph_connected(const String &p_from, int p_from_slot, int from_port; if (!_get_out_slot(from_node, p_from_slot, from_port, from_seq)) - return; //can't connect this, it' s invalid + return; //can't connect this, it's invalid Ref<VisualScriptNode> to_node = script->get_node(edited_func, p_to.to_int()); ERR_FAIL_COND(!to_node.is_valid()); @@ -2352,7 +2353,7 @@ void VisualScriptEditor::_graph_connected(const String &p_from, int p_from_slot, int to_port; if (!_get_in_slot(to_node, p_to_slot, to_port, to_seq)) - return; //can't connect this, it' s invalid + return; //can't connect this, it's invalid ERR_FAIL_COND(from_seq != to_seq); @@ -2363,7 +2364,7 @@ void VisualScriptEditor::_graph_connected(const String &p_from, int p_from_slot, undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", edited_func, p_from.to_int(), from_port, p_to.to_int()); } else { - // disconect current, and connect the new one + // disconnect current, and connect the new one if (script->is_input_value_port_connected(edited_func, p_to.to_int(), to_port)) { int conn_from; int conn_port; @@ -2396,7 +2397,7 @@ void VisualScriptEditor::_graph_disconnected(const String &p_from, int p_from_sl int from_port; if (!_get_out_slot(from_node, p_from_slot, from_port, from_seq)) - return; //can't connect this, it' s invalid + return; //can't connect this, it's invalid Ref<VisualScriptNode> to_node = script->get_node(edited_func, p_to.to_int()); ERR_FAIL_COND(!to_node.is_valid()); @@ -2405,7 +2406,7 @@ void VisualScriptEditor::_graph_disconnected(const String &p_from, int p_from_sl int to_port; if (!_get_in_slot(to_node, p_to_slot, to_port, to_seq)) - return; //can't connect this, it' s invalid + return; //can't connect this, it's invalid ERR_FAIL_COND(from_seq != to_seq); @@ -2518,8 +2519,6 @@ void VisualScriptEditor::_port_action_menu(int p_option) { } ofs /= EDSCALE; - bool seq_connect = false; - Set<int> vn; switch (p_option) { @@ -2552,7 +2551,6 @@ void VisualScriptEditor::_port_action_menu(int p_option) { } } break; 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) { @@ -2603,7 +2601,6 @@ void VisualScriptEditor::connect_data(Ref<VisualScriptNode> vnode_old, Ref<Visua 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(); @@ -2643,7 +2640,7 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri } 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) { + if (vnode_old.is_valid() && p_connecting) { connect_seq(vnode_old, vnode_new, new_id); connect_data(vnode_old, vnode_new, new_id); } @@ -2657,7 +2654,6 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri Ref<VisualScriptNode> vnode; - seq_connect = false; if (p_category == String("method")) { Ref<VisualScriptFunctionCall> n; @@ -2683,38 +2679,32 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri Ref<VisualScriptCondition> n; n.instance(); vnode = n; - seq_connect = true; } if (p_text == "VisualScriptSwitch") { Ref<VisualScriptSwitch> n; n.instance(); vnode = n; - seq_connect = true; } else if (p_text == "VisualScriptSequence") { Ref<VisualScriptSequence> n; n.instance(); vnode = n; - seq_connect = true; } else if (p_text == "VisualScriptIterator") { Ref<VisualScriptIterator> n; n.instance(); vnode = n; - seq_connect = true; } else if (p_text == "VisualScriptWhile") { Ref<VisualScriptWhile> n; n.instance(); vnode = n; - seq_connect = true; } else if (p_text == "VisualScriptReturn") { Ref<VisualScriptReturn> n; n.instance(); vnode = n; - seq_connect = true; } } @@ -2817,7 +2807,7 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri } } Ref<VisualScriptNode> vnode_old = script->get_node(edited_func, port_action_node); - if (vnode_old.is_valid() && p_connecting == true) { + if (vnode_old.is_valid() && p_connecting) { connect_seq(vnode_old, vnode, port_action_new_node); connect_data(vnode_old, vnode, port_action_new_node); } @@ -2826,9 +2816,8 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri } 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) { + if (vnode_operator != NULL && !vnode_operator->has_input_sequence_port()) { return; } VisualScriptConstructor *vnode_constructor = Object::cast_to<VisualScriptConstructor>(vnode_new.ptr()); @@ -2838,10 +2827,10 @@ void VisualScriptEditor::connect_seq(Ref<VisualScriptNode> vnode_old, Ref<Visual if (vnode_old->get_output_sequence_port_count() <= 0) { return; } - if (vnode_new->has_input_sequence_port() == false) { + if (!vnode_new->has_input_sequence_port()) { 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; @@ -3030,11 +3019,15 @@ void VisualScriptEditor::_node_filter_changed(const String &p_text) { void VisualScriptEditor::_notification(int p_what) { - if (p_what == NOTIFICATION_READY) { + if (p_what == NOTIFICATION_READY || (p_what == NOTIFICATION_THEME_CHANGED && is_visible_in_tree())) { + 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"); + + if (p_what == NOTIFICATION_READY) { + variable_editor->connect("changed", this, "_update_members"); + signal_editor->connect("changed", this, "_update_members"); + } Ref<Theme> tm = EditorNode::get_singleton()->get_theme_base()->get_theme(); @@ -3068,8 +3061,12 @@ void VisualScriptEditor::_notification(int p_what) { node_styles[E->get().first] = frame_style; } } - } - if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { + + if (is_visible_in_tree() && script.is_valid()) { + _update_members(); + _update_graph(); + } + } else if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { left_vsplit->set_visible(is_visible_in_tree()); } } @@ -3367,11 +3364,6 @@ void VisualScriptEditor::_member_option(int p_option) { undo_redo->add_undo_method(script.ptr(), "data_connect", name, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port); } - /* - for(int i=0;i<script->function_get_argument_count(name);i++) { - undo_redo->add_undo_method(script.ptr(),"function_add_argument",name,script->function_get_argument_name(name,i),script->function_get_argument_type(name,i)); - } - */ undo_redo->add_do_method(this, "_update_members"); undo_redo->add_undo_method(this, "_update_members"); undo_redo->add_do_method(this, "_update_graph"); @@ -3615,8 +3607,7 @@ VisualScriptEditor::VisualScriptEditor() { edit_signal_dialog->set_title(TTR("Edit Signal Arguments:")); signal_editor = memnew(VisualScriptEditorSignalEdit); - edit_signal_edit = memnew(PropertyEditor); - edit_signal_edit->hide_top_label(); + edit_signal_edit = memnew(EditorInspector); edit_signal_dialog->add_child(edit_signal_edit); edit_signal_edit->edit(signal_editor); @@ -3627,8 +3618,7 @@ VisualScriptEditor::VisualScriptEditor() { edit_variable_dialog->set_title(TTR("Edit Variable:")); variable_editor = memnew(VisualScriptEditorVariableEdit); - edit_variable_edit = memnew(PropertyEditor); - edit_variable_edit->hide_top_label(); + edit_variable_edit = memnew(EditorInspector); edit_variable_dialog->add_child(edit_variable_edit); edit_variable_edit->edit(variable_editor); @@ -3663,7 +3653,7 @@ VisualScriptEditor::VisualScriptEditor() { 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"); + new_virtual_method_select->get_cancel(); member_popup = memnew(PopupMenu); add_child(member_popup); diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h index 8bfd147519..ce3245bc28 100644 --- a/modules/visual_script/visual_script_editor.h +++ b/modules/visual_script/visual_script_editor.h @@ -37,6 +37,7 @@ #include "scene/gui/graph_edit.h" #include "visual_script.h" #include "visual_script_property_selector.h" + class VisualScriptEditorSignalEdit; class VisualScriptEditorVariableEdit; @@ -93,7 +94,7 @@ class VisualScriptEditor : public ScriptEditorBase { VisualScriptEditorSignalEdit *signal_editor; AcceptDialog *edit_signal_dialog; - PropertyEditor *edit_signal_edit; + EditorInspector *edit_signal_edit; VisualScriptPropertySelector *method_select; VisualScriptPropertySelector *new_connect_node_select; @@ -102,7 +103,7 @@ class VisualScriptEditor : public ScriptEditorBase { VisualScriptEditorVariableEdit *variable_editor; AcceptDialog *edit_variable_dialog; - PropertyEditor *edit_variable_edit; + EditorInspector *edit_variable_edit; CustomPropertyEditor *default_value_edit; @@ -159,8 +160,6 @@ class VisualScriptEditor : public ScriptEditorBase { MemberType member_type; String member_name; - bool seq_connect = false; - PortAction port_action; int port_action_node; int port_action_output; @@ -177,7 +176,7 @@ class VisualScriptEditor : public ScriptEditorBase { 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); + void _selected_new_virtual_method(const String &p_text, const String &p_category, const bool p_connecting); int error_line; @@ -238,7 +237,7 @@ class VisualScriptEditor : public ScriptEditorBase { 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); + void _button_resource_previewed(const String &p_path, const Ref<Texture> &p_preview, const Ref<Texture> &p_small_preview, Variant p_ud); VisualScriptNode::TypeGuess _guess_output_type(int p_port_action_node, int p_port_action_output, Set<int> &visited_nodes); diff --git a/modules/visual_script/visual_script_flow_control.cpp b/modules/visual_script/visual_script_flow_control.cpp index 7535f37ffc..c3ab949d24 100644 --- a/modules/visual_script/visual_script_flow_control.cpp +++ b/modules/visual_script/visual_script_flow_control.cpp @@ -30,9 +30,9 @@ #include "visual_script_flow_control.h" -#include "io/resource_loader.h" -#include "os/keyboard.h" -#include "project_settings.h" +#include "core/io/resource_loader.h" +#include "core/os/keyboard.h" +#include "core/project_settings.h" ////////////////////////////////////////// ////////////////RETURN//////////////////// diff --git a/modules/visual_script/visual_script_func_nodes.cpp b/modules/visual_script/visual_script_func_nodes.cpp index f926d4e2eb..1913bfd8c7 100644 --- a/modules/visual_script/visual_script_func_nodes.cpp +++ b/modules/visual_script/visual_script_func_nodes.cpp @@ -30,9 +30,9 @@ #include "visual_script_func_nodes.h" -#include "engine.h" -#include "io/resource_loader.h" -#include "os/os.h" +#include "core/engine.h" +#include "core/io/resource_loader.h" +#include "core/os/os.h" #include "scene/main/node.h" #include "scene/main/scene_tree.h" #include "visual_script_nodes.h" diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp index d499512d93..99748af8a1 100644 --- a/modules/visual_script/visual_script_nodes.cpp +++ b/modules/visual_script/visual_script_nodes.cpp @@ -30,11 +30,11 @@ #include "visual_script_nodes.h" -#include "engine.h" -#include "global_constants.h" -#include "os/input.h" -#include "os/os.h" -#include "project_settings.h" +#include "core/engine.h" +#include "core/global_constants.h" +#include "core/os/input.h" +#include "core/os/os.h" +#include "core/project_settings.h" #include "scene/main/node.h" #include "scene/main/scene_tree.h" @@ -167,7 +167,7 @@ void VisualScriptFunction::_get_property_list(List<PropertyInfo> *p_list) const p_list->push_back(PropertyInfo(Variant::INT, "stack/size", PROPERTY_HINT_RANGE, "1,100000")); } p_list->push_back(PropertyInfo(Variant::BOOL, "stack/stackless")); - p_list->push_back(PropertyInfo(Variant::INT, "rpc/mode", PROPERTY_HINT_ENUM, "Disabled,Remote,Sync,Master,Slave")); + p_list->push_back(PropertyInfo(Variant::INT, "rpc/mode", PROPERTY_HINT_ENUM, "Disabled,Remote,Master,Puppet,Remote Sync,Master Sync,Puppet Sync")); } int VisualScriptFunction::get_output_sequence_port_count() const { @@ -853,7 +853,7 @@ public: virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Variant::CallError &r_error, String &r_error_str) { - if (instance->get_variable(variable, p_outputs[0]) == false) { + if (!instance->get_variable(variable, p_outputs[0])) { r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD; r_error_str = RTR("VariableGet not found in script: ") + "'" + String(variable) + "'"; return false; @@ -975,7 +975,7 @@ public: virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Variant::CallError &r_error, String &r_error_str) { - if (instance->set_variable(variable, *p_inputs[0]) == false) { + if (!instance->set_variable(variable, *p_inputs[0])) { r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD; r_error_str = RTR("VariableSet not found in script: ") + "'" + String(variable) + "'"; @@ -3708,18 +3708,18 @@ void register_visual_script_nodes() { for (List<MethodInfo>::Element *E = constructors.front(); E; E = E->next()) { if (E->get().arguments.size() > 0) { - - String name = "functions/constructors/" + Variant::get_type_name(Variant::Type(i)) + " ( "; + String name = "functions/constructors/" + Variant::get_type_name(Variant::Type(i)) + "("; for (int j = 0; j < E->get().arguments.size(); j++) { - if (j > 0) + if (j > 0) { name += ", "; - if (E->get().arguments.size() == 1) + } + if (E->get().arguments.size() == 1) { name += Variant::get_type_name(E->get().arguments[j].type); - else + } else { name += E->get().arguments[j].name; + } } - name += ") "; - + name += ")"; VisualScriptLanguage::singleton->add_register_func(name, create_constructor_node); Pair<Variant::Type, MethodInfo> pair; pair.first = Variant::Type(i); diff --git a/modules/visual_script/visual_script_property_selector.cpp b/modules/visual_script/visual_script_property_selector.cpp index f79c81ad88..e5d12cb495 100644 --- a/modules/visual_script/visual_script_property_selector.cpp +++ b/modules/visual_script/visual_script_property_selector.cpp @@ -30,13 +30,14 @@ #include "visual_script_property_selector.h" +#include "core/os/keyboard.h" +#include "editor/editor_node.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" @@ -148,7 +149,7 @@ void VisualScriptPropertySelector::_update_search() { Control::get_icon("PoolColorArray", "EditorIcons") }; - if (!seq_connect && visual_script_generic == false) { + if (!seq_connect && !visual_script_generic) { 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); @@ -176,10 +177,8 @@ void VisualScriptPropertySelector::_update_search() { 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"); + icon = EditorNode::get_singleton()->get_class_icon(E->get().name); } category->set_icon(0, icon); continue; @@ -191,15 +190,14 @@ void VisualScriptPropertySelector::_update_search() { if (type_filter.size() && type_filter.find(E->get().type) == -1) continue; - String get_text_raw = String(TTR("Get")) + String(" ") + E->get().name; + // capitalize() also converts underscore to space, we'll match again both possible styles + String get_text_raw = String(vformat(TTR("Get %s"), E->get().name)); String get_text = get_text_raw.capitalize(); - - String set_text_raw = String(TTR("Set ")) + String(" ") + E->get().name; + String set_text_raw = String(vformat(TTR("Set %s"), 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) { + + 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); @@ -212,9 +210,7 @@ void VisualScriptPropertySelector::_update_search() { item->set_metadata(2, connecting); } - if (input == String() || - set_text_raw.findn(input) != -1 && - set_text.findn(input) != -1) { + 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); @@ -232,7 +228,7 @@ void VisualScriptPropertySelector::_update_search() { } } - if (seq_connect == true && visual_script_generic == false) { + if (seq_connect && !visual_script_generic) { String text = search_box->get_text(); create_visualscript_item(String("VisualScriptCondition"), root, text, String("Condition")); create_visualscript_item(String("VisualScriptSwitch"), root, text, String("Switch")); @@ -289,10 +285,8 @@ void VisualScriptPropertySelector::_update_search() { 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"); + icon = EditorNode::get_singleton()->get_class_icon(rep); } category->set_icon(0, icon); @@ -310,31 +304,36 @@ void VisualScriptPropertySelector::_update_search() { continue; MethodInfo mi = E->get(); - String desc = mi.name.capitalize() + " ("; + String desc_arguments; + if (mi.arguments.size() > 0) { + desc_arguments = "("; + for (int i = 0; i < mi.arguments.size(); i++) { + + if (i > 0) { + desc_arguments += ", "; + } + if (mi.arguments[i].type == Variant::NIL) { + desc_arguments += "var"; + } else if (mi.arguments[i].name.find(":") != -1) { + desc_arguments += mi.arguments[i].name.get_slice(":", 1); + mi.arguments[i].name = mi.arguments[i].name.get_slice(":", 0); + } else { + desc_arguments += Variant::get_type_name(mi.arguments[i].type); + } + } + desc_arguments += ")"; + } + String desc_raw = mi.name + desc_arguments; + String desc = desc_raw.capitalize().replace("( ", "("); if (search_box->get_text() != String() && name.findn(search_box->get_text()) == -1 && - desc.findn(search_box->get_text()) == -1) + desc.findn(search_box->get_text()) == -1 && + desc_raw.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 += ")"; - + TreeItem *item = search_options->create_item(category ? category : root); item->set_text(0, desc); item->set_icon(0, get_icon("MemberMethod", "EditorIcons")); item->set_metadata(0, name); @@ -392,13 +391,13 @@ void VisualScriptPropertySelector::get_visual_node_names(const String &root_filt } 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) { + for (Set<String>::Element *F = filter.front(); F; F = F->next()) { + if (path.size() >= 2 && path[1].findn(F->get()) != -1) { is_filter = true; break; } } - if (is_filter == true) { + if (is_filter) { continue; } @@ -420,11 +419,16 @@ void VisualScriptPropertySelector::get_visual_node_names(const String &root_filt 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 "; + + Vector<String> desc = path[path.size() - 1].replace("(", "( ").replace(")", " )").replace(",", ", ").split(" "); + for (size_t i = 0; i < desc.size(); i++) { + desc.write[i] = desc[i].capitalize(); + if (desc[i].ends_with(",")) { + desc.write[i] = desc[i].replace(",", ", "); + } } - item->set_text(0, type_name + path[path.size() - 1].capitalize()); + + item->set_text(0, type_name + String("").join(desc)); item->set_icon(0, get_icon("VisualScript", "EditorIcons")); item->set_selectable(0, true); item->set_metadata(0, E->get()); @@ -581,6 +585,7 @@ void VisualScriptPropertySelector::select_from_base_type(const String &p_base, c type = Variant::NIL; script = 0; properties = true; + visual_script_generic = false; instance = NULL; virtuals_only = p_virtuals_only; @@ -601,6 +606,7 @@ void VisualScriptPropertySelector::select_from_script(const Ref<Script> &p_scrip type = Variant::NIL; script = p_script->get_instance_id(); properties = true; + visual_script_generic = false; instance = NULL; virtuals_only = false; @@ -620,6 +626,7 @@ void VisualScriptPropertySelector::select_from_basic_type(Variant::Type p_type, type = p_type; script = 0; properties = true; + visual_script_generic = false; instance = NULL; virtuals_only = false; @@ -638,6 +645,7 @@ void VisualScriptPropertySelector::select_from_action(const String &p_type, cons type = Variant::NIL; script = 0; properties = false; + visual_script_generic = false; instance = NULL; virtuals_only = false; @@ -656,6 +664,7 @@ void VisualScriptPropertySelector::select_from_instance(Object *p_instance, cons type = Variant::NIL; script = 0; properties = true; + visual_script_generic = false; instance = p_instance; virtuals_only = false; @@ -724,6 +733,7 @@ VisualScriptPropertySelector::VisualScriptPropertySelector() { search_options->set_hide_root(true); search_options->set_hide_folding(true); virtuals_only = false; + seq_connect = false; help_bit = memnew(EditorHelpBit); vbc->add_margin_child(TTR("Description:"), help_bit); help_bit->connect("request_hide", this, "_closed"); diff --git a/modules/visual_script/visual_script_property_selector.h b/modules/visual_script/visual_script_property_selector.h index 917ef9ae6d..f974ee3355 100644 --- a/modules/visual_script/visual_script_property_selector.h +++ b/modules/visual_script/visual_script_property_selector.h @@ -63,8 +63,7 @@ class VisualScriptPropertySelector : public ConfirmationDialog { ObjectID script; Object *instance; bool virtuals_only; - - bool seq_connect = false; + bool seq_connect; void _item_selected(); diff --git a/modules/visual_script/visual_script_yield_nodes.cpp b/modules/visual_script/visual_script_yield_nodes.cpp index a96e8408c0..a21fff67fe 100644 --- a/modules/visual_script/visual_script_yield_nodes.cpp +++ b/modules/visual_script/visual_script_yield_nodes.cpp @@ -30,7 +30,7 @@ #include "visual_script_yield_nodes.h" -#include "os/os.h" +#include "core/os/os.h" #include "scene/main/node.h" #include "scene/main/scene_tree.h" #include "visual_script_nodes.h" diff --git a/modules/vorbis/SCsub b/modules/vorbis/SCsub index 55a112585b..19587563ab 100644 --- a/modules/vorbis/SCsub +++ b/modules/vorbis/SCsub @@ -40,13 +40,16 @@ if env['builtin_libvorbis']: thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_vorbis.add_source_files(env.modules_sources, thirdparty_sources) env_vorbis.Append(CPPPATH=[thirdparty_dir]) # also requires libogg if env['builtin_libogg']: env_vorbis.Append(CPPPATH=["#thirdparty/libogg"]) + env_thirdparty = env_vorbis.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + if not stub: # Module files env_vorbis.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/vorbis/audio_stream_ogg_vorbis.h b/modules/vorbis/audio_stream_ogg_vorbis.h index 01de8a3143..73c4b5f3f4 100644 --- a/modules/vorbis/audio_stream_ogg_vorbis.h +++ b/modules/vorbis/audio_stream_ogg_vorbis.h @@ -31,9 +31,9 @@ #ifndef AUDIO_STREAM_OGG_VORBIS_H #define AUDIO_STREAM_OGG_VORBIS_H -#include "io/resource_loader.h" -#include "os/file_access.h" -#include "os/thread_safe.h" +#include "core/io/resource_loader.h" +#include "core/os/file_access.h" +#include "core/os/thread_safe.h" #include "scene/resources/audio_stream.h" #include <vorbis/vorbisfile.h> diff --git a/modules/webm/SCsub b/modules/webm/SCsub index 33561da098..cb35b926ab 100644 --- a/modules/webm/SCsub +++ b/modules/webm/SCsub @@ -6,17 +6,16 @@ Import('env_modules') env_webm = env_modules.Clone() # Thirdparty source files -thirdparty_libsimplewebm_dir = "#thirdparty/libsimplewebm/" -thirdparty_libsimplewebm_sources = [ +thirdparty_dir = "#thirdparty/libsimplewebm/" +thirdparty_sources = [ "libwebm/mkvparser/mkvparser.cc", "OpusVorbisDecoder.cpp", "VPXDecoder.cpp", "WebMDemuxer.cpp", ] -thirdparty_libsimplewebm_sources = [thirdparty_libsimplewebm_dir + file for file in thirdparty_libsimplewebm_sources] +thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env_webm.add_source_files(env.modules_sources, thirdparty_libsimplewebm_sources) -env_webm.Append(CPPPATH=[thirdparty_libsimplewebm_dir, thirdparty_libsimplewebm_dir + "libwebm/"]) +env_webm.Append(CPPPATH=[thirdparty_dir, thirdparty_dir + "libwebm/"]) # upstream uses c++11 if (not env_webm.msvc): @@ -31,8 +30,12 @@ if env['builtin_opus']: env_webm.Append(CPPPATH=["#thirdparty/opus"]) if env['builtin_libvpx']: - Export('env_webm') + env_webm.Append(CPPPATH=["#thirdparty/libvpx"]) SConscript("libvpx/SCsub") +env_thirdparty = env_webm.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + # Godot source files env_webm.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/webm/libvpx/SCsub b/modules/webm/libvpx/SCsub index c681e2b34f..98e38b9027 100644 --- a/modules/webm/libvpx/SCsub +++ b/modules/webm/libvpx/SCsub @@ -1,5 +1,10 @@ #!/usr/bin/env python +Import('env') +Import('env_modules') + +# Thirdparty sources + libvpx_dir = "#thirdparty/libvpx/" libvpx_sources = [ @@ -38,7 +43,6 @@ libvpx_sources = [ "vp8/decoder/decodemv.c", "vp8/decoder/detokenize.c", "vp8/decoder/onyxd_if.c", - "vp8/decoder/threading.c", "vp9/vp9_dx_iface.c", @@ -102,6 +106,10 @@ libvpx_sources = [ "vpx_util/vpx_thread.c" ] +libvpx_sources_mt = [ + "vp8/decoder/threading.c", +] + libvpx_sources_intrin_x86 = [ "vp8/common/x86/filter_x86.c", "vp8/common/x86/loopfilter_x86.c", @@ -231,6 +239,7 @@ libvpx_sources_arm_neon_gas_apple = [ ] libvpx_sources = [libvpx_dir + file for file in libvpx_sources] +libvpx_sources_mt = [libvpx_dir + file for file in libvpx_sources_mt] libvpx_sources_intrin_x86 = [libvpx_dir + file for file in libvpx_sources_intrin_x86] libvpx_sources_intrin_x86_mmx = [libvpx_dir + file for file in libvpx_sources_intrin_x86_mmx] libvpx_sources_intrin_x86_sse2 = [libvpx_dir + file for file in libvpx_sources_intrin_x86_sse2] @@ -245,14 +254,12 @@ libvpx_sources_arm_neon_armasm_ms = [libvpx_dir + file for file in libvpx_source libvpx_sources_arm_neon_gas_apple = [libvpx_dir + file for file in libvpx_sources_arm_neon_gas_apple] -Import('env') -Import('env_webm') - -env_webm.Append(CPPPATH=[libvpx_dir]) - -env_libvpx = env.Clone() +env_libvpx = env_modules.Clone() +env_libvpx.disable_warnings() env_libvpx.Append(CPPPATH=[libvpx_dir]) +webm_multithread = env["platform"] != 'javascript' + cpu_bits = env["bits"] webm_cpu_x86 = False webm_cpu_arm = False @@ -338,8 +345,12 @@ if webm_simd_optimizations == False: print("WebM SIMD optimizations are disabled. Check if your CPU architecture, CPU bits or platform are supported!") env_libvpx.add_source_files(env.modules_sources, libvpx_sources) + +if webm_multithread: + env_libvpx.add_source_files(env.modules_sources, libvpx_sources_mt) + if webm_cpu_x86: - is_clang_or_gcc = ('gcc' in env["CC"]) or ('clang' in env["CC"]) or ("OSXCROSS_ROOT" in os.environ) + is_clang_or_gcc = ('gcc' in os.path.basename(env["CC"])) or ('clang' in os.path.basename(env["CC"])) or ("OSXCROSS_ROOT" in os.environ) env_libvpx_mmx = env_libvpx.Clone() if cpu_bits == '32' and is_clang_or_gcc: diff --git a/modules/webm/video_stream_webm.cpp b/modules/webm/video_stream_webm.cpp index 1bb9a43886..675fc97b55 100644 --- a/modules/webm/video_stream_webm.cpp +++ b/modules/webm/video_stream_webm.cpp @@ -35,9 +35,9 @@ #include "mkvparser/mkvparser.h" -#include "os/file_access.h" -#include "os/os.h" -#include "project_settings.h" +#include "core/os/file_access.h" +#include "core/os/os.h" +#include "core/project_settings.h" #include "thirdparty/misc/yuv2rgb.h" @@ -453,7 +453,6 @@ RES ResourceFormatLoaderWebm::load(const String &p_path, const String &p_origina if (r_error) { *r_error = ERR_CANT_OPEN; } - memdelete(f); return RES(); } diff --git a/modules/webm/video_stream_webm.h b/modules/webm/video_stream_webm.h index dcf88092c5..3739a73114 100644 --- a/modules/webm/video_stream_webm.h +++ b/modules/webm/video_stream_webm.h @@ -31,7 +31,7 @@ #ifndef VIDEO_STREAM_WEBM_H #define VIDEO_STREAM_WEBM_H -#include "io/resource_loader.h" +#include "core/io/resource_loader.h" #include "scene/resources/video_stream.h" class WebMFrame; diff --git a/modules/webp/SCsub b/modules/webp/SCsub index 21ae0ce7c2..8a4307fbe1 100644 --- a/modules/webp/SCsub +++ b/modules/webp/SCsub @@ -127,8 +127,11 @@ if env['builtin_libwebp']: ] thirdparty_sources = [thirdparty_dir + "src/" + file for file in thirdparty_sources] - env_webp.add_source_files(env.modules_sources, thirdparty_sources) env_webp.Append(CPPPATH=[thirdparty_dir, thirdparty_dir + "src/"]) + env_thirdparty = env_webp.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + # Godot source files env_webp.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/webp/image_loader_webp.cpp b/modules/webp/image_loader_webp.cpp index 42b2c77777..6734ae90d9 100644 --- a/modules/webp/image_loader_webp.cpp +++ b/modules/webp/image_loader_webp.cpp @@ -30,9 +30,9 @@ #include "image_loader_webp.h" -#include "io/marshalls.h" -#include "os/os.h" -#include "print_string.h" +#include "core/io/marshalls.h" +#include "core/os/os.h" +#include "core/print_string.h" #include <stdlib.h> #include <webp/decode.h> diff --git a/modules/webp/image_loader_webp.h b/modules/webp/image_loader_webp.h index f051fed4b8..256c787a16 100644 --- a/modules/webp/image_loader_webp.h +++ b/modules/webp/image_loader_webp.h @@ -31,7 +31,7 @@ #ifndef IMAGE_LOADER_WEBP_H #define IMAGE_LOADER_WEBP_H -#include "io/image_loader.h" +#include "core/io/image_loader.h" /** @author Juan Linietsky <reduzio@gmail.com> diff --git a/modules/websocket/SCsub b/modules/websocket/SCsub index c0985b3245..b67a836fe8 100644 --- a/modules/websocket/SCsub +++ b/modules/websocket/SCsub @@ -7,7 +7,7 @@ Import('env_modules') env_lws = env_modules.Clone() -if env['builtin_libwebsockets']: +if env['builtin_libwebsockets'] and not env["platform"] == "javascript": # already builtin for javascript thirdparty_dir = "#thirdparty/libwebsockets/" helper_dir = "win32helpers/" thirdparty_sources = [ @@ -61,34 +61,33 @@ if env['builtin_libwebsockets']: "tls/mbedtls/mbedtls-server.c" ] - if env_lws["platform"] == "android": # Builtin getifaddrs + if env["platform"] == "android": # Builtin getifaddrs thirdparty_sources += ["misc/getifaddrs.c"] - if env_lws["platform"] == "windows" or env_lws["platform"] == "uwp": # Winsock + if env["platform"] == "windows" or env["platform"] == "uwp": # Winsock thirdparty_sources += ["plat/lws-plat-win.c", helper_dir + "getopt.c", helper_dir + "getopt_long.c", helper_dir + "gettimeofday.c"] else: # Unix socket thirdparty_sources += ["plat/lws-plat-unix.c"] - thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - if env_lws["platform"] == "javascript": # No need to add third party libraries at all - pass - else: - env_lws.add_source_files(env.modules_sources, thirdparty_sources) - env_lws.Append(CPPPATH=[thirdparty_dir]) + env_lws.Append(CPPPATH=[thirdparty_dir]) + + if env['builtin_mbedtls']: + mbedtls_includes = "#thirdparty/mbedtls/include" + env_lws.Prepend(CPPPATH=[mbedtls_includes]) - wrapper_includes = ["#thirdparty/libwebsockets/tls/mbedtls/wrapper/include/" + inc for inc in ["internal", "openssl", "platform", ""]] - env_lws.Prepend(CPPPATH=wrapper_includes) + wrapper_includes = ["#thirdparty/libwebsockets/tls/mbedtls/wrapper/include/" + inc for inc in ["internal", "openssl", "platform", ""]] + env_lws.Prepend(CPPPATH=wrapper_includes) - if env['builtin_mbedtls']: - mbedtls_includes = "#thirdparty/mbedtls/include" - env_lws.Prepend(CPPPATH=[mbedtls_includes]) + if env["platform"] == "windows" or env["platform"] == "uwp": + env_lws.Append(CPPPATH=[thirdparty_dir + helper_dir]) - if env_lws["platform"] == "windows" or env_lws["platform"] == "uwp": - env_lws.Append(CPPPATH=[thirdparty_dir + helper_dir]) + if env["platform"] == "uwp": + env_lws.Append(CCFLAGS=["/DLWS_MINGW_SUPPORT"]) - if env_lws["platform"] == "uwp": - env_lws.Append(CCFLAGS=["/DLWS_MINGW_SUPPORT"]) + env_thirdparty = env_lws.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) env_lws.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/websocket/doc_classes/WebSocketClient.xml b/modules/websocket/doc_classes/WebSocketClient.xml index 2e11e1a44c..b3ea535495 100644 --- a/modules/websocket/doc_classes/WebSocketClient.xml +++ b/modules/websocket/doc_classes/WebSocketClient.xml @@ -25,14 +25,18 @@ </argument> <description> Connect to the given URL requesting one of the given [code]protocols[/code] as sub-protocol. - If [code]true[/code] is passed as [code]gd_mp_api[/code], the client will behave like a network peer for the [MultiplayerAPI]. Note: connnections to non Godot servers will not work, and [signal data_received] will not be emitted when this option is true. + If [code]true[/code] is passed as [code]gd_mp_api[/code], the client will behave like a network peer for the [MultiplayerAPI]. Note: connections to non Godot servers will not work, and [signal data_received] will not be emitted when this option is true. </description> </method> <method name="disconnect_from_host"> <return type="void"> </return> + <argument index="0" name="code" type="int" default="1000"> + </argument> + <argument index="1" name="reason" type="String" default=""""> + </argument> <description> - Disconnect from the server if currently connected. + Disconnect this client from the connected host. See [method WebSocketPeer.close] for more info. </description> </method> </methods> @@ -43,8 +47,10 @@ </members> <signals> <signal name="connection_closed"> + <argument index="0" name="was_clean_close" type="bool"> + </argument> <description> - Emitted when the connection to the server is closed. + Emitted when the connection to the server is closed. [code]was_clean_close[/code] will be [code]true[/code] if the connection was shutdown cleanly. </description> </signal> <signal name="connection_error"> @@ -64,6 +70,15 @@ Emitted when a WebSocket message is received. Note: This signal is NOT emitted when used as high level multiplayer peer. </description> </signal> + <signal name="server_close_request"> + <argument index="0" name="code" type="int"> + </argument> + <argument index="1" name="reason" type="String"> + </argument> + <description> + Emitted when the server requests a clean close. You should keep polling until you get a [signal connection_closed] signal to achieve the clean close. See [method WebSocketPeer.close] for more details. + </description> + </signal> </signals> <constants> </constants> diff --git a/modules/websocket/doc_classes/WebSocketPeer.xml b/modules/websocket/doc_classes/WebSocketPeer.xml index 85a08e0c0b..cc59706916 100644 --- a/modules/websocket/doc_classes/WebSocketPeer.xml +++ b/modules/websocket/doc_classes/WebSocketPeer.xml @@ -15,8 +15,14 @@ <method name="close"> <return type="void"> </return> + <argument index="0" name="code" type="int" default="1000"> + </argument> + <argument index="1" name="reason" type="String" default=""""> + </argument> <description> - Close this WebSocket connection, actively disconnecting the peer. + Close this WebSocket connection. [code]code[/code] is the status code for the closure (see RFC6455 section 7.4 for a list of valid status codes). [reason] is the human readable reason for closing the connection (can be any UTF8 string, must be less than 123 bytes). + Note: To achieve a clean close, you will need to keep polling until either [signal WebSocketClient.connection_closed] or [signal WebSocketServer.client_disconnected] is received. + Note: HTML5 export might not support all status codes. Please refer to browsers-specific documentation for more details. </description> </method> <method name="get_connected_host" qualifiers="const"> diff --git a/modules/websocket/doc_classes/WebSocketServer.xml b/modules/websocket/doc_classes/WebSocketServer.xml index a1061e446b..ba66fdd89b 100644 --- a/modules/websocket/doc_classes/WebSocketServer.xml +++ b/modules/websocket/doc_classes/WebSocketServer.xml @@ -18,8 +18,12 @@ </return> <argument index="0" name="id" type="int"> </argument> + <argument index="1" name="code" type="int" default="1000"> + </argument> + <argument index="2" name="reason" type="String" default=""""> + </argument> <description> - Disconnects the given peer. + Disconnects the peer identified by [code]id[/code] from the server. See [method WebSocketPeer.close] for more info. </description> </method> <method name="get_peer_address" qualifiers="const"> @@ -68,7 +72,7 @@ <description> Start listening on the given port. You can specify the desired subprotocols via the "protocols" array. If the list empty (default), "binary" will be used. - You can use this server as a network peer for [MultiplayerAPI] by passing true as "gd_mp_api". Note: [signal data_received] will not be fired and clients other than Godot will not work in this case. + You can use this server as a network peer for [MultiplayerAPI] by passing [code]true[/code] as [code]gd_mp_api[/code]. Note: [signal data_received] will not be fired and clients other than Godot will not work in this case. </description> </method> <method name="stop"> @@ -80,6 +84,17 @@ </method> </methods> <signals> + <signal name="client_close_request"> + <argument index="0" name="id" type="int"> + </argument> + <argument index="1" name="code" type="int"> + </argument> + <argument index="2" name="reason" type="String"> + </argument> + <description> + Emitted when a client requests a clean close. You should keep polling until you get a [signal client_disconnected] signal with the same [code]id[/code] to achieve the clean close. See [method WebSocketPeer.close] for more details. + </description> + </signal> <signal name="client_connected"> <argument index="0" name="id" type="int"> </argument> @@ -92,8 +107,10 @@ <signal name="client_disconnected"> <argument index="0" name="id" type="int"> </argument> + <argument index="1" name="was_clean_close" type="bool"> + </argument> <description> - Emitted when a client disconnects. + Emitted when a client disconnects. [code]was_clean_close[/code] will be [code]true[/code] if the connection was shutdown cleanly. </description> </signal> <signal name="data_received"> diff --git a/modules/websocket/emws_client.cpp b/modules/websocket/emws_client.cpp index 00c36ebb47..8255ed7116 100644 --- a/modules/websocket/emws_client.cpp +++ b/modules/websocket/emws_client.cpp @@ -55,32 +55,31 @@ EMSCRIPTEN_KEEPALIVE void _esws_on_error(void *obj) { EMSCRIPTEN_KEEPALIVE void _esws_on_close(void *obj, int code, char *reason, int was_clean) { EMWSClient *client = static_cast<EMWSClient *>(obj); + client->_on_close_request(code, String(reason)); client->_is_connecting = false; - client->_on_disconnect(); + client->_on_disconnect(was_clean != 0); } } Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) { + String proto_string = p_protocols.join(","); String str = "ws://"; - String proto_string = ""; if (p_ssl) str = "wss://"; str += p_host + ":" + itos(p_port) + p_path; - for (int i = 0; i < p_protocols.size(); i++) { - proto_string += p_protocols[i]; - proto_string += ","; - } - if (proto_string == "") - proto_string = "binary,"; - - proto_string = proto_string.substr(0, proto_string.length() - 1); _is_connecting = true; /* clang-format off */ int peer_sock = EM_ASM_INT({ - var socket = new WebSocket(UTF8ToString($1), UTF8ToString($2).split(",")); + var proto_str = UTF8ToString($2); + var socket = null; + if (proto_str) { + socket = new WebSocket(UTF8ToString($1), proto_str.split(",")); + } else { + socket = new WebSocket(UTF8ToString($1)); + } var c_ptr = Module.IDHandler.get($0); socket.binaryType = "arraybuffer"; @@ -147,7 +146,7 @@ Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, if (!Module.IDHandler.has($0)) return; // Godot Object is gone! var was_clean = 0; - if (event.was_clean) + if (event.wasClean) was_clean = 1; ccall("_esws_on_close", "void", @@ -184,9 +183,9 @@ NetworkedMultiplayerPeer::ConnectionStatus EMWSClient::get_connection_status() c return CONNECTION_DISCONNECTED; }; -void EMWSClient::disconnect_from_host() { +void EMWSClient::disconnect_from_host(int p_code, String p_reason) { - _peer->close(); + _peer->close(p_code, p_reason); }; IP_Address EMWSClient::get_connected_host() const { diff --git a/modules/websocket/emws_client.h b/modules/websocket/emws_client.h index 6c2fa23b53..b20633baff 100644 --- a/modules/websocket/emws_client.h +++ b/modules/websocket/emws_client.h @@ -48,7 +48,7 @@ public: Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>()); Ref<WebSocketPeer> get_peer(int p_peer_id) const; - void disconnect_from_host(); + void disconnect_from_host(int p_code = 1000, String p_reason = ""); IP_Address get_connected_host() const; uint16_t get_connected_port() const; virtual ConnectionStatus get_connection_status() const; diff --git a/modules/websocket/emws_peer.cpp b/modules/websocket/emws_peer.cpp index e0b987b4d7..68f41165eb 100644 --- a/modules/websocket/emws_peer.cpp +++ b/modules/websocket/emws_peer.cpp @@ -130,15 +130,17 @@ bool EMWSPeer::is_connected_to_host() const { return peer_sock != -1; }; -void EMWSPeer::close() { +void EMWSPeer::close(int p_code, String p_reason) { if (peer_sock != -1) { /* clang-format off */ EM_ASM({ var sock = Module.IDHandler.get($0); - sock.close(); + var code = $1; + var reason = UTF8ToString($2); + sock.close(code, reason); Module.IDHandler.remove($0); - }, peer_sock); + }, peer_sock, p_code, p_reason.utf8().get_data()); /* clang-format on */ } peer_sock = -1; diff --git a/modules/websocket/emws_peer.h b/modules/websocket/emws_peer.h index e06f725265..a4b2c8f50b 100644 --- a/modules/websocket/emws_peer.h +++ b/modules/websocket/emws_peer.h @@ -63,7 +63,7 @@ public: virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size); virtual int get_max_packet_size() const { return PACKET_BUFFER_SIZE; }; - virtual void close(); + virtual void close(int p_code = 1000, String p_reason = ""); virtual bool is_connected_to_host() const; virtual IP_Address get_connected_host() const; virtual uint16_t get_connected_port() const; diff --git a/modules/websocket/emws_server.cpp b/modules/websocket/emws_server.cpp index 3eb93e4152..db02162699 100644 --- a/modules/websocket/emws_server.cpp +++ b/modules/websocket/emws_server.cpp @@ -68,7 +68,10 @@ int EMWSServer::get_peer_port(int p_peer_id) const { return 0; } -void EMWSServer::disconnect_peer(int p_peer_id) { +void EMWSServer::disconnect_peer(int p_peer_id, int p_code, String p_reason) { +} + +void EMWSServer::poll() { } EMWSServer::EMWSServer() { diff --git a/modules/websocket/emws_server.h b/modules/websocket/emws_server.h index 9ec4ce72c8..74b689a29b 100644 --- a/modules/websocket/emws_server.h +++ b/modules/websocket/emws_server.h @@ -48,7 +48,7 @@ public: Ref<WebSocketPeer> get_peer(int p_id) const; IP_Address get_peer_address(int p_peer_id) const; int get_peer_port(int p_peer_id) const; - void disconnect_peer(int p_peer_id); + void disconnect_peer(int p_peer_id, int p_code = 1000, String p_reason = ""); virtual void poll(); virtual PoolVector<String> get_protocols() const; diff --git a/modules/websocket/lws_client.cpp b/modules/websocket/lws_client.cpp index ac31daa108..d71d091720 100644 --- a/modules/websocket/lws_client.cpp +++ b/modules/websocket/lws_client.cpp @@ -48,12 +48,10 @@ Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, ERR_FAIL_COND_V(!addr.is_valid(), ERR_INVALID_PARAMETER); - // prepare protocols - if (p_protocols.size() == 0) // default to binary protocol - p_protocols.append("binary"); + // Prepare protocols _lws_make_protocols(this, &LWSClient::_lws_gd_callback, p_protocols, &_lws_ref); - // init lws client + // Init lws client struct lws_context_creation_info info; struct lws_client_connect_info i; @@ -78,20 +76,11 @@ Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, ERR_FAIL_V(FAILED); } - char abuf[1024]; - char hbuf[1024]; - char pbuf[2048]; - String addr_str = (String)addr; - strncpy(abuf, addr_str.ascii().get_data(), 1024); - strncpy(hbuf, p_host.utf8().get_data(), 1024); - strncpy(pbuf, p_path.utf8().get_data(), 2048); - i.context = context; - i.protocol = _lws_ref->lws_names; - i.address = abuf; - i.host = hbuf; - i.path = pbuf; - i.port = p_port; + if (p_protocols.size() > 0) + i.protocol = _lws_ref->lws_names; + else + i.protocol = NULL; if (p_ssl) { i.ssl_connection = LCCSCF_USE_SSL; @@ -101,7 +90,16 @@ Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, i.ssl_connection = 0; } + // This String needs to survive till we call lws_client_connect_via_info + String addr_str = (String)addr; + + i.address = addr_str.ascii().get_data(); + i.host = p_host.utf8().get_data(); + i.path = p_path.utf8().get_data(); + i.port = p_port; + lws_client_connect_via_info(&i); + return OK; }; @@ -128,19 +126,28 @@ int LWSClient::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi peer->set_wsi(wsi); peer_data->peer_id = 0; peer_data->force_close = false; + peer_data->clean_close = false; _on_connect(lws_get_protocol(wsi)->name); break; case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: _on_error(); destroy_context(); - return -1; // we should close the connection (would probably happen anyway) + return -1; // We should close the connection (would probably happen anyway) + + case LWS_CALLBACK_WS_PEER_INITIATED_CLOSE: { + int code; + String reason = peer->get_close_reason(in, len, code); + peer_data->clean_close = true; + _on_close_request(code, reason); + return 0; + } case LWS_CALLBACK_CLIENT_CLOSED: peer->close(); destroy_context(); - _on_disconnect(); - return 0; // we can end here + _on_disconnect(peer_data->clean_close); + return 0; // We can end here case LWS_CALLBACK_CLIENT_RECEIVE: peer->read_wsi(in, len); @@ -149,8 +156,10 @@ int LWSClient::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi break; case LWS_CALLBACK_CLIENT_WRITEABLE: - if (peer_data->force_close) + if (peer_data->force_close) { + peer->send_close_status(wsi); return -1; + } peer->write_wsi(); break; @@ -178,13 +187,12 @@ NetworkedMultiplayerPeer::ConnectionStatus LWSClient::get_connection_status() co return CONNECTION_CONNECTING; } -void LWSClient::disconnect_from_host() { +void LWSClient::disconnect_from_host(int p_code, String p_reason) { if (context == NULL) return; - _peer->close(); - destroy_context(); + _peer->close(p_code, p_reason); }; IP_Address LWSClient::get_connected_host() const { @@ -207,6 +215,7 @@ LWSClient::~LWSClient() { invalidate_lws_ref(); // We do not want any more callback disconnect_from_host(); + destroy_context(); _peer = Ref<LWSPeer>(); }; diff --git a/modules/websocket/lws_client.h b/modules/websocket/lws_client.h index 8850683cb5..1bbc19f352 100644 --- a/modules/websocket/lws_client.h +++ b/modules/websocket/lws_client.h @@ -46,7 +46,7 @@ class LWSClient : public WebSocketClient { public: Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>()); Ref<WebSocketPeer> get_peer(int p_peer_id) const; - void disconnect_from_host(); + void disconnect_from_host(int p_code = 1000, String p_reason = ""); IP_Address get_connected_host() const; uint16_t get_connected_port() const; virtual ConnectionStatus get_connection_status() const; diff --git a/modules/websocket/lws_helper.cpp b/modules/websocket/lws_helper.cpp new file mode 100644 index 0000000000..b5216615e9 --- /dev/null +++ b/modules/websocket/lws_helper.cpp @@ -0,0 +1,157 @@ +/*************************************************************************/ +/* lws_helper.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. */ +/*************************************************************************/ + +#if !defined(JAVASCRIPT_ENABLED) + +#include "lws_helper.h" + +_LWSRef *_lws_create_ref(void *obj) { + + _LWSRef *out = (_LWSRef *)memalloc(sizeof(_LWSRef)); + out->is_destroying = false; + out->free_context = false; + out->is_polling = false; + out->obj = obj; + out->is_valid = true; + out->lws_structs = NULL; + out->lws_names = NULL; + return out; +} + +void _lws_free_ref(_LWSRef *ref) { + // Free strings and structs + memfree(ref->lws_structs); + memfree(ref->lws_names); + // Free ref + memfree(ref); +} + +bool _lws_destroy(struct lws_context *context, _LWSRef *ref) { + if (context == NULL || ref->is_destroying) + return false; + + if (ref->is_polling) { + ref->free_context = true; + return false; + } + + ref->is_destroying = true; + lws_context_destroy(context); + _lws_free_ref(ref); + return true; +} + +bool _lws_poll(struct lws_context *context, _LWSRef *ref) { + + ERR_FAIL_COND_V(context == NULL, false); + ERR_FAIL_COND_V(ref == NULL, false); + + ref->is_polling = true; + lws_service(context, 0); + ref->is_polling = false; + + if (!ref->free_context) + return false; // Nothing to do + + bool is_valid = ref->is_valid; // Might have been destroyed by poll + + _lws_destroy(context, ref); // Will destroy context and ref + + return is_valid; // If the object should NULL its context and ref +} + +/* + * Prepare the protocol_structs to be fed to context. + * Also prepare the protocol string used by the client. + */ +void _lws_make_protocols(void *p_obj, lws_callback_function *p_callback, PoolVector<String> p_names, _LWSRef **r_lws_ref) { + // The input strings might go away after this call, we need to copy them. + // We will clear them when destroying the context. + int i; + int len = p_names.size(); + size_t data_size = sizeof(struct LWSPeer::PeerData); + PoolVector<String>::Read pnr = p_names.read(); + + // This is a reference connecting the object with lws keep track of status, mallocs, etc. + // Must survive as long the context. + // Must be freed manually when context creation fails. + _LWSRef *ref = _lws_create_ref(p_obj); + + // LWS protocol structs. + ref->lws_structs = (struct lws_protocols *)memalloc(sizeof(struct lws_protocols) * (len + 2)); + memset(ref->lws_structs, 0, sizeof(struct lws_protocols) * (len + 2)); + + CharString strings = p_names.join(",").ascii(); + int str_len = strings.length(); + + // Joined string of protocols, double the size: comma separated first, NULL separated last + ref->lws_names = (char *)memalloc((str_len + 1) * 2); // Plus the terminator + + char *names_ptr = ref->lws_names; + struct lws_protocols *structs_ptr = ref->lws_structs; + + // Comma separated protocols string to be used in client Sec-WebSocket-Protocol header + if (str_len > 0) + copymem(names_ptr, strings.get_data(), str_len); + names_ptr[str_len] = '\0'; // NULL terminator + + // NULL terminated protocol strings to be used in protocol structs + if (str_len > 0) + copymem(&names_ptr[str_len + 1], strings.get_data(), str_len); + names_ptr[(str_len * 2) + 1] = '\0'; // NULL terminator + int pos = str_len + 1; + + // The first protocol is the default for any http request (before upgrade). + // It is also used as the websocket protocol when no subprotocol is specified. + structs_ptr[0].name = "default"; + structs_ptr[0].callback = p_callback; + structs_ptr[0].per_session_data_size = data_size; + structs_ptr[0].rx_buffer_size = LWS_BUF_SIZE; + structs_ptr[0].tx_packet_size = LWS_PACKET_SIZE; + // Add user defined protocols + for (i = 0; i < len; i++) { + structs_ptr[i + 1].name = (const char *)&names_ptr[pos]; + structs_ptr[i + 1].callback = p_callback; + structs_ptr[i + 1].per_session_data_size = data_size; + structs_ptr[i + 1].rx_buffer_size = LWS_BUF_SIZE; + structs_ptr[i + 1].tx_packet_size = LWS_PACKET_SIZE; + pos += pnr[i].ascii().length() + 1; + names_ptr[pos - 1] = '\0'; + } + // Add protocols terminator + structs_ptr[len + 1].name = NULL; + structs_ptr[len + 1].callback = NULL; + structs_ptr[len + 1].per_session_data_size = 0; + structs_ptr[len + 1].rx_buffer_size = 0; + + *r_lws_ref = ref; +} + +#endif diff --git a/modules/websocket/lws_helper.h b/modules/websocket/lws_helper.h index 85a1e3769f..fd8f85371b 100644 --- a/modules/websocket/lws_helper.h +++ b/modules/websocket/lws_helper.h @@ -49,126 +49,11 @@ struct _LWSRef { char *lws_names; }; -static _LWSRef *_lws_create_ref(void *obj) { - - _LWSRef *out = (_LWSRef *)memalloc(sizeof(_LWSRef)); - out->is_destroying = false; - out->free_context = false; - out->is_polling = false; - out->obj = obj; - out->is_valid = true; - out->lws_structs = NULL; - out->lws_names = NULL; - return out; -} - -static void _lws_free_ref(_LWSRef *ref) { - // Free strings and structs - memfree(ref->lws_structs); - memfree(ref->lws_names); - // Free ref - memfree(ref); -} - -static bool _lws_destroy(struct lws_context *context, _LWSRef *ref) { - if (context == NULL || ref->is_destroying) - return false; - - if (ref->is_polling) { - ref->free_context = true; - return false; - } - - ref->is_destroying = true; - lws_context_destroy(context); - _lws_free_ref(ref); - return true; -} - -static bool _lws_poll(struct lws_context *context, _LWSRef *ref) { - - ERR_FAIL_COND_V(context == NULL, false); - ERR_FAIL_COND_V(ref == NULL, false); - - ref->is_polling = true; - lws_service(context, 0); - ref->is_polling = false; - - if (!ref->free_context) - return false; // Nothing to do - - bool is_valid = ref->is_valid; // Might have been destroyed by poll - - _lws_destroy(context, ref); // Will destroy context and ref - - return is_valid; // If the object should NULL its context and ref -} - -/* - * prepare the protocol_structs to be fed to context - * also prepare the protocol string used by the client - */ -static void _lws_make_protocols(void *p_obj, lws_callback_function *p_callback, PoolVector<String> p_names, _LWSRef **r_lws_ref) { - /* the input strings might go away after this call, - * we need to copy them. Will clear them when - * destroying the context */ - int i; - int len = p_names.size(); - size_t data_size = sizeof(struct LWSPeer::PeerData); - PoolVector<String>::Read pnr = p_names.read(); - - /* - * This is a reference connecting the object with lws - * keep track of status, mallocs, etc. - * Must survive as long the context - * Must be freed manually when context creation fails. - */ - _LWSRef *ref = _lws_create_ref(p_obj); - - /* LWS protocol structs */ - ref->lws_structs = (struct lws_protocols *)memalloc(sizeof(struct lws_protocols) * (len + 2)); - memset(ref->lws_structs, 0, sizeof(struct lws_protocols) * (len + 2)); - - CharString strings = p_names.join(",").ascii(); - int str_len = strings.length(); - - /* Joined string of protocols, double the size: comma separated first, NULL separated last */ - ref->lws_names = (char *)memalloc((str_len + 1) * 2); /* plus the terminator */ - - char *names_ptr = ref->lws_names; - struct lws_protocols *structs_ptr = ref->lws_structs; - - copymem(names_ptr, strings.get_data(), str_len); - names_ptr[str_len] = '\0'; /* NULL terminator */ - /* NULL terminated strings to be used in protocol structs */ - copymem(&names_ptr[str_len + 1], strings.get_data(), str_len); - names_ptr[(str_len * 2) + 1] = '\0'; /* NULL terminator */ - int pos = str_len + 1; - - /* the first protocol is always http-only */ - structs_ptr[0].name = "http-only"; - structs_ptr[0].callback = p_callback; - structs_ptr[0].per_session_data_size = data_size; - structs_ptr[0].rx_buffer_size = LWS_BUF_SIZE; - structs_ptr[0].tx_packet_size = LWS_PACKET_SIZE; - /* add user defined protocols */ - for (i = 0; i < len; i++) { - structs_ptr[i + 1].name = (const char *)&names_ptr[pos]; - structs_ptr[i + 1].callback = p_callback; - structs_ptr[i + 1].per_session_data_size = data_size; - structs_ptr[i + 1].rx_buffer_size = LWS_BUF_SIZE; - structs_ptr[i + 1].tx_packet_size = LWS_PACKET_SIZE; - pos += pnr[i].ascii().length() + 1; - names_ptr[pos - 1] = '\0'; - } - /* add protocols terminator */ - structs_ptr[len + 1].name = NULL; - structs_ptr[len + 1].callback = NULL; - structs_ptr[len + 1].per_session_data_size = 0; - structs_ptr[len + 1].rx_buffer_size = 0; - - *r_lws_ref = ref; -} +_LWSRef *_lws_create_ref(void *obj); +void _lws_free_ref(_LWSRef *ref); +bool _lws_destroy(struct lws_context *context, _LWSRef *ref); +bool _lws_poll(struct lws_context *context, _LWSRef *ref); +void _lws_make_protocols(void *p_obj, lws_callback_function *p_callback, PoolVector<String> p_names, _LWSRef **r_lws_ref); /* clang-format off */ #define LWS_HELPER(CNAME) \ diff --git a/modules/websocket/lws_peer.cpp b/modules/websocket/lws_peer.cpp index 0989357258..b5c130b308 100644 --- a/modules/websocket/lws_peer.cpp +++ b/modules/websocket/lws_peer.cpp @@ -30,6 +30,7 @@ #ifndef JAVASCRIPT_ENABLED #include "lws_peer.h" + #include "core/io/ip.h" // Needed for socket_helpers on Android at least. UNIXes has it, just include if not windows @@ -38,7 +39,7 @@ #include <sys/socket.h> #endif -#include "drivers/unix/socket_helpers.h" +#include "drivers/unix/net_socket_posix.h" void LWSPeer::set_wsi(struct lws *p_wsi) { ERR_FAIL_COND(wsi != NULL); @@ -60,7 +61,6 @@ 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 = in_size; uint8_t is_string = lws_frame_is_binary(wsi) ? 0 : 1; @@ -88,7 +88,6 @@ Error LWSPeer::write_wsi() { ERR_FAIL_COND_V(!is_connected_to_host(), FAILED); - PeerData *peer_data = (PeerData *)(lws_wsi_user(wsi)); PoolVector<uint8_t> tmp; int left = rbw.data_left(); uint32_t to_write = 0; @@ -119,7 +118,6 @@ 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); rbw.write((uint8_t *)&p_buffer_size, 4); rbw.write(p_buffer, MIN(p_buffer_size, rbw.space_left())); out_count++; @@ -132,8 +130,6 @@ Error LWSPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { ERR_FAIL_COND_V(!is_connected_to_host(), FAILED); - PeerData *peer_data = (PeerData *)lws_wsi_user(wsi); - if (in_count == 0) return ERR_UNAVAILABLE; @@ -178,11 +174,49 @@ bool LWSPeer::is_connected_to_host() const { return wsi != NULL; }; -void LWSPeer::close() { +String LWSPeer::get_close_reason(void *in, size_t len, int &r_code) { + String s; + r_code = 0; + if (len < 2) // From docs this should not happen + return s; + + const uint8_t *b = (const uint8_t *)in; + r_code = b[0] << 8 | b[1]; + + if (len > 2) { + const char *utf8 = (const char *)&b[2]; + s.parse_utf8(utf8, len - 2); + } + return s; +} + +void LWSPeer::send_close_status(struct lws *p_wsi) { + if (close_code == -1) + return; + + int len = close_reason.size(); + ERR_FAIL_COND(len > 123); // Maximum allowed reason size in bytes + + lws_close_status code = (lws_close_status)close_code; + unsigned char *reason = len > 0 ? (unsigned char *)close_reason.utf8().ptrw() : NULL; + + lws_close_reason(p_wsi, code, reason, len); + + close_code = -1; + close_reason = ""; +} + +void LWSPeer::close(int p_code, String p_reason) { if (wsi != NULL) { + close_code = p_code; + close_reason = p_reason; PeerData *data = ((PeerData *)lws_wsi_user(wsi)); data->force_close = true; - lws_callback_on_writable(wsi); // notify that we want to disconnect + data->clean_close = true; + lws_callback_on_writable(wsi); // Notify that we want to disconnect + } else { + close_code = -1; + close_reason = ""; } wsi = NULL; rbw.resize(0); @@ -198,7 +232,7 @@ IP_Address LWSPeer::get_connected_host() const { ERR_FAIL_COND_V(!is_connected_to_host(), IP_Address()); IP_Address ip; - int port = 0; + uint16_t port = 0; struct sockaddr_storage addr; socklen_t len = sizeof(addr); @@ -209,7 +243,7 @@ IP_Address LWSPeer::get_connected_host() const { int ret = getpeername(fd, (struct sockaddr *)&addr, &len); ERR_FAIL_COND_V(ret != 0, IP_Address()); - _set_ip_addr_port(ip, port, &addr); + NetSocketPosix::_set_ip_port(&addr, ip, port); return ip; }; @@ -219,7 +253,7 @@ uint16_t LWSPeer::get_connected_port() const { ERR_FAIL_COND_V(!is_connected_to_host(), 0); IP_Address ip; - int port = 0; + uint16_t port = 0; struct sockaddr_storage addr; socklen_t len = sizeof(addr); @@ -230,7 +264,7 @@ uint16_t LWSPeer::get_connected_port() const { int ret = getpeername(fd, (struct sockaddr *)&addr, &len); ERR_FAIL_COND_V(ret != 0, 0); - _set_ip_addr_port(ip, port, &addr); + NetSocketPosix::_set_ip_port(&addr, ip, port); return port; }; diff --git a/modules/websocket/lws_peer.h b/modules/websocket/lws_peer.h index d7d46e3076..571445db01 100644 --- a/modules/websocket/lws_peer.h +++ b/modules/websocket/lws_peer.h @@ -53,10 +53,14 @@ private: WriteMode write_mode; bool _was_string; + int close_code; + String close_reason; + public: struct PeerData { uint32_t peer_id; bool force_close; + bool clean_close; }; RingBuffer<uint8_t> rbw; @@ -71,7 +75,7 @@ public: virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size); virtual int get_max_packet_size() const { return PACKET_BUFFER_SIZE; }; - virtual void close(); + virtual void close(int p_code = 1000, String p_reason = ""); virtual bool is_connected_to_host() const; virtual IP_Address get_connected_host() const; virtual uint16_t get_connected_port() const; @@ -83,6 +87,8 @@ public: void set_wsi(struct lws *wsi); Error read_wsi(void *in, size_t len); Error write_wsi(); + void send_close_status(struct lws *wsi); + String get_close_reason(void *in, size_t len, int &r_code); LWSPeer(); ~LWSPeer(); diff --git a/modules/websocket/lws_server.cpp b/modules/websocket/lws_server.cpp index bb724bce9c..58fa043346 100644 --- a/modules/websocket/lws_server.cpp +++ b/modules/websocket/lws_server.cpp @@ -41,9 +41,6 @@ Error LWSServer::listen(int p_port, PoolVector<String> p_protocols, bool gd_mp_a struct lws_context_creation_info info; memset(&info, 0, sizeof info); - if (p_protocols.size() == 0) // default to binary protocol - p_protocols.append(String("binary")); - // Prepare lws protocol structs _lws_make_protocols(this, &LWSServer::_lws_gd_callback, p_protocols, &_lws_ref); @@ -93,20 +90,36 @@ int LWSServer::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi peer_data->peer_id = id; peer_data->force_close = false; - + peer_data->clean_close = false; _on_connect(id, lws_get_protocol(wsi)->name); break; } + case LWS_CALLBACK_WS_PEER_INITIATED_CLOSE: { + if (peer_data == NULL) + return 0; + + int32_t id = peer_data->peer_id; + if (_peer_map.has(id)) { + int code; + Ref<LWSPeer> peer = _peer_map[id]; + String reason = peer->get_close_reason(in, len, code); + peer_data->clean_close = true; + _on_close_request(id, code, reason); + } + return 0; + } + case LWS_CALLBACK_CLOSED: { if (peer_data == NULL) return 0; int32_t id = peer_data->peer_id; + bool clean = peer_data->clean_close; if (_peer_map.has(id)) { _peer_map[id]->close(); _peer_map.erase(id); } - _on_disconnect(id); + _on_disconnect(id, clean); return 0; // we can end here } @@ -121,10 +134,15 @@ int LWSServer::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi } case LWS_CALLBACK_SERVER_WRITEABLE: { - if (peer_data->force_close) + int id = peer_data->peer_id; + if (peer_data->force_close) { + if (_peer_map.has(id)) { + Ref<LWSPeer> peer = _peer_map[id]; + peer->send_close_status(wsi); + } return -1; + } - int id = peer_data->peer_id; if (_peer_map.has(id)) static_cast<Ref<LWSPeer> >(_peer_map[id])->write_wsi(); break; @@ -167,10 +185,10 @@ int LWSServer::get_peer_port(int p_peer_id) const { return _peer_map[p_peer_id]->get_connected_port(); } -void LWSServer::disconnect_peer(int p_peer_id) { +void LWSServer::disconnect_peer(int p_peer_id, int p_code, String p_reason) { ERR_FAIL_COND(!has_peer(p_peer_id)); - get_peer(p_peer_id)->close(); + get_peer(p_peer_id)->close(p_code, p_reason); } LWSServer::LWSServer() { diff --git a/modules/websocket/lws_server.h b/modules/websocket/lws_server.h index 9e3fb9b775..346773ebc4 100644 --- a/modules/websocket/lws_server.h +++ b/modules/websocket/lws_server.h @@ -54,7 +54,7 @@ public: Ref<WebSocketPeer> get_peer(int p_id) const; IP_Address get_peer_address(int p_peer_id) const; int get_peer_port(int p_peer_id) const; - void disconnect_peer(int p_peer_id); + void disconnect_peer(int p_peer_id, int p_code = 1000, String p_reason = ""); virtual void poll() { _lws_poll(); } LWSServer(); diff --git a/modules/websocket/register_types.cpp b/modules/websocket/register_types.cpp index 721f3cc330..538cd40454 100644 --- a/modules/websocket/register_types.cpp +++ b/modules/websocket/register_types.cpp @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "register_types.h" -#include "error_macros.h" +#include "core/error_macros.h" #ifdef JAVASCRIPT_ENABLED #include "emscripten.h" #include "emws_client.h" diff --git a/modules/websocket/websocket_client.cpp b/modules/websocket/websocket_client.cpp index 7701163085..f9b94dc519 100644 --- a/modules/websocket/websocket_client.cpp +++ b/modules/websocket/websocket_client.cpp @@ -107,12 +107,17 @@ void WebSocketClient::_on_connect(String p_protocol) { } } -void WebSocketClient::_on_disconnect() { +void WebSocketClient::_on_close_request(int p_code, String p_reason) { + + emit_signal("server_close_request", p_code, p_reason); +} + +void WebSocketClient::_on_disconnect(bool p_was_clean) { if (_is_multiplayer) { emit_signal("connection_failed"); } else { - emit_signal("connection_closed"); + emit_signal("connection_closed", p_was_clean); } } @@ -127,7 +132,7 @@ void WebSocketClient::_on_error() { void WebSocketClient::_bind_methods() { ClassDB::bind_method(D_METHOD("connect_to_url", "url", "protocols", "gd_mp_api"), &WebSocketClient::connect_to_url, DEFVAL(PoolVector<String>()), DEFVAL(false)); - ClassDB::bind_method(D_METHOD("disconnect_from_host"), &WebSocketClient::disconnect_from_host); + ClassDB::bind_method(D_METHOD("disconnect_from_host", "code", "reason"), &WebSocketClient::disconnect_from_host, DEFVAL(1000), DEFVAL("")); ClassDB::bind_method(D_METHOD("set_verify_ssl_enabled", "enabled"), &WebSocketClient::set_verify_ssl_enabled); ClassDB::bind_method(D_METHOD("is_verify_ssl_enabled"), &WebSocketClient::is_verify_ssl_enabled); @@ -135,6 +140,7 @@ void WebSocketClient::_bind_methods() { ADD_SIGNAL(MethodInfo("data_received")); ADD_SIGNAL(MethodInfo("connection_established", PropertyInfo(Variant::STRING, "protocol"))); - ADD_SIGNAL(MethodInfo("connection_closed")); + ADD_SIGNAL(MethodInfo("server_close_request", PropertyInfo(Variant::INT, "code"), PropertyInfo(Variant::STRING, "reason"))); + ADD_SIGNAL(MethodInfo("connection_closed", PropertyInfo(Variant::BOOL, "was_clean_close"))); ADD_SIGNAL(MethodInfo("connection_error")); } diff --git a/modules/websocket/websocket_client.h b/modules/websocket/websocket_client.h index 6165f37d40..948f128e9f 100644 --- a/modules/websocket/websocket_client.h +++ b/modules/websocket/websocket_client.h @@ -53,7 +53,7 @@ public: virtual void poll() = 0; virtual Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>()) = 0; - virtual void disconnect_from_host() = 0; + virtual void disconnect_from_host(int p_code = 1000, String p_reason = "") = 0; virtual IP_Address get_connected_host() const = 0; virtual uint16_t get_connected_port() const = 0; @@ -62,7 +62,8 @@ public: void _on_peer_packet(); void _on_connect(String p_protocol); - void _on_disconnect(); + void _on_close_request(int p_code, String p_reason); + void _on_disconnect(bool p_was_clean); void _on_error(); WebSocketClient(); diff --git a/modules/websocket/websocket_multiplayer.cpp b/modules/websocket/websocket_multiplayer.cpp index b948c439df..873658559a 100644 --- a/modules/websocket/websocket_multiplayer.cpp +++ b/modules/websocket/websocket_multiplayer.cpp @@ -313,7 +313,7 @@ void WebSocketMultiplayerPeer::_process_multiplayer(Ref<WebSocketPeer> p_peer, u } else if (to < 0) { // All but one, for us if not excluded - if (_peer_id != -p_peer_id) + if (_peer_id != -(int32_t)p_peer_id) _store_pkt(from, to, in_buffer, data_size); } else { diff --git a/modules/websocket/websocket_peer.cpp b/modules/websocket/websocket_peer.cpp index 61f783e377..3ecb32ce19 100644 --- a/modules/websocket/websocket_peer.cpp +++ b/modules/websocket/websocket_peer.cpp @@ -42,7 +42,7 @@ void WebSocketPeer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_write_mode", "mode"), &WebSocketPeer::set_write_mode); ClassDB::bind_method(D_METHOD("is_connected_to_host"), &WebSocketPeer::is_connected_to_host); ClassDB::bind_method(D_METHOD("was_string_packet"), &WebSocketPeer::was_string_packet); - ClassDB::bind_method(D_METHOD("close"), &WebSocketPeer::close); + ClassDB::bind_method(D_METHOD("close", "code", "reason"), &WebSocketPeer::close, DEFVAL(1000), DEFVAL("")); ClassDB::bind_method(D_METHOD("get_connected_host"), &WebSocketPeer::get_connected_host); ClassDB::bind_method(D_METHOD("get_connected_port"), &WebSocketPeer::get_connected_port); diff --git a/modules/websocket/websocket_peer.h b/modules/websocket/websocket_peer.h index ad451e9cc7..5918fda3c2 100644 --- a/modules/websocket/websocket_peer.h +++ b/modules/websocket/websocket_peer.h @@ -58,7 +58,7 @@ public: virtual WriteMode get_write_mode() const = 0; virtual void set_write_mode(WriteMode p_mode) = 0; - virtual void close() = 0; + virtual void close(int p_code = 1000, String p_reason = "") = 0; virtual bool is_connected_to_host() const = 0; virtual IP_Address get_connected_host() const = 0; diff --git a/modules/websocket/websocket_server.cpp b/modules/websocket/websocket_server.cpp index 53dd7b51b7..c631ed70d5 100644 --- a/modules/websocket/websocket_server.cpp +++ b/modules/websocket/websocket_server.cpp @@ -46,9 +46,10 @@ void WebSocketServer::_bind_methods() { ClassDB::bind_method(D_METHOD("has_peer", "id"), &WebSocketServer::has_peer); ClassDB::bind_method(D_METHOD("get_peer_address", "id"), &WebSocketServer::get_peer_address); ClassDB::bind_method(D_METHOD("get_peer_port", "id"), &WebSocketServer::get_peer_port); - ClassDB::bind_method(D_METHOD("disconnect_peer", "id"), &WebSocketServer::disconnect_peer); + ClassDB::bind_method(D_METHOD("disconnect_peer", "id", "code", "reason"), &WebSocketServer::disconnect_peer, DEFVAL(1000), DEFVAL("")); - ADD_SIGNAL(MethodInfo("client_disconnected", PropertyInfo(Variant::INT, "id"))); + ADD_SIGNAL(MethodInfo("client_close_request", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::INT, "code"), PropertyInfo(Variant::STRING, "reason"))); + ADD_SIGNAL(MethodInfo("client_disconnected", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::BOOL, "was_clean_close"))); ADD_SIGNAL(MethodInfo("client_connected", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "protocol"))); ADD_SIGNAL(MethodInfo("data_received", PropertyInfo(Variant::INT, "id"))); } @@ -85,13 +86,18 @@ void WebSocketServer::_on_connect(int32_t p_peer_id, String p_protocol) { } } -void WebSocketServer::_on_disconnect(int32_t p_peer_id) { +void WebSocketServer::_on_disconnect(int32_t p_peer_id, bool p_was_clean) { if (_is_multiplayer) { // Send delete to clients _send_del(p_peer_id); emit_signal("peer_disconnected", p_peer_id); } else { - emit_signal("client_disconnected", p_peer_id); + emit_signal("client_disconnected", p_peer_id, p_was_clean); } } + +void WebSocketServer::_on_close_request(int32_t p_peer_id, int p_code, String p_reason) { + + emit_signal("client_close_request", p_peer_id, p_code, p_reason); +} diff --git a/modules/websocket/websocket_server.h b/modules/websocket/websocket_server.h index 64935f8a58..156f25897c 100644 --- a/modules/websocket/websocket_server.h +++ b/modules/websocket/websocket_server.h @@ -54,11 +54,12 @@ public: virtual IP_Address get_peer_address(int p_peer_id) const = 0; virtual int get_peer_port(int p_peer_id) const = 0; - virtual void disconnect_peer(int p_peer_id) = 0; + virtual void disconnect_peer(int p_peer_id, int p_code = 1000, String p_reason = "") = 0; void _on_peer_packet(int32_t p_peer_id); void _on_connect(int32_t p_peer_id, String p_protocol); - void _on_disconnect(int32_t p_peer_id); + void _on_disconnect(int32_t p_peer_id, bool p_was_clean); + void _on_close_request(int32_t p_peer_id, int p_code, String p_reason); WebSocketServer(); ~WebSocketServer(); diff --git a/modules/xatlas_unwrap/SCsub b/modules/xatlas_unwrap/SCsub new file mode 100644 index 0000000000..ad364d5aaf --- /dev/null +++ b/modules/xatlas_unwrap/SCsub @@ -0,0 +1,46 @@ +#!/usr/bin/env python + +import platform + +Import('env') +Import('env_modules') + +env_xatlas_unwrap = env_modules.Clone() + +# Thirdparty source files +if env['builtin_xatlas']: + thirdparty_dir = "#thirdparty/xatlas/" + thirdparty_sources = [ + "xatlas.cpp", + ] + thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] + + env_xatlas_unwrap.Append(CPPPATH=[thirdparty_dir]) + + # upstream uses c++11 + if (not env.msvc): + env_xatlas_unwrap.Append(CXXFLAGS="-std=c++11") + + if env["platform"] == 'x11': + # if not specifically one of the *BSD, then use LINUX as default + if platform.system() == "FreeBSD": + env_xatlas_unwrap.Append(CCFLAGS=["-DNV_OS_FREEBSD", "-DPOSH_COMPILER_GCC"]) + elif platform.system() == "OpenBSD": + env_xatlas_unwrap.Append(CCFLAGS=["-DNV_OS_OPENBSD", "-DPOSH_COMPILER_GCC"]) + else: + env_xatlas_unwrap.Append(CCFLAGS=["-DNV_OS_LINUX", "-DPOSH_COMPILER_GCC"]) + elif env["platform"] == 'osx': + env_xatlas_unwrap.Append(CCFLAGS=["-DNV_OS_DARWIN", "-DPOSH_COMPILER_GCC"]) + elif env["platform"] == 'windows': + if env.msvc: + env_xatlas_unwrap.Append(CCFLAGS=["-DNV_OS_WIN32", "-DNV_CC_MSVC", "-DPOSH_COMPILER_MSVC" ]) + else: + env_xatlas_unwrap.Append(CCFLAGS=["-DNV_OS_MINGW", "-DNV_CC_GNUC", "-DPOSH_COMPILER_GCC", "-U__STRICT_ANSI__"]) + env.Append(LIBS=["dbghelp"]) + + env_thirdparty = env_xatlas_unwrap.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + +# Godot source files +env_xatlas_unwrap.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/xatlas_unwrap/config.py b/modules/xatlas_unwrap/config.py new file mode 100644 index 0000000000..2dda5db7e0 --- /dev/null +++ b/modules/xatlas_unwrap/config.py @@ -0,0 +1,6 @@ +def can_build(env, platform): + #return False #xatlas is buggy + return (env['tools'] and platform not in ["android", "ios"]) + +def configure(env): + pass diff --git a/modules/xatlas_unwrap/register_types.cpp b/modules/xatlas_unwrap/register_types.cpp new file mode 100644 index 0000000000..57eea4eda6 --- /dev/null +++ b/modules/xatlas_unwrap/register_types.cpp @@ -0,0 +1,143 @@ +/*************************************************************************/ +/* register_types.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 "register_types.h" +#include "core/error_macros.h" +#include "thirdparty/xatlas/xatlas.h" + +#include <stdio.h> +#include <stdlib.h> +extern bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, const int *p_face_materials, int p_index_count, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y); + +bool xatlas_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, const int *p_face_materials, int p_index_count, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y) { + + //set up input mesh + xatlas::InputMesh input_mesh; + input_mesh.indexData = malloc(sizeof(int) * p_index_count); + input_mesh.indexCount = p_index_count; + input_mesh.indexFormat = xatlas::IndexFormat::Float; //really xatlas? + input_mesh.faceMaterialData = (uint16_t *)malloc(sizeof(uint16_t) * p_index_count); + + for (int i = 0; i < p_index_count; i++) { + int *index = (int *)input_mesh.indexData; + index[i] = p_indices[i]; + } + for (int i = 0; i < p_index_count / 3; i++) { + uint16_t *mat_index = (uint16_t *)input_mesh.faceMaterialData; + mat_index[i] = p_face_materials[i]; + } + + input_mesh.vertexCount = p_vertex_count; + input_mesh.vertexPositionData = malloc(sizeof(float) * p_vertex_count * 3); + input_mesh.vertexPositionStride = sizeof(float) * 3; + input_mesh.vertexNormalData = malloc(sizeof(float) * p_vertex_count * 3); + input_mesh.vertexNormalStride = sizeof(float) * 3; + + //material is a better hint than this i guess? + input_mesh.vertexUvData = NULL; + input_mesh.vertexUvStride = 0; + + for (int i = 0; i < p_vertex_count * 3; i++) { + float *vertex_ptr = (float *)input_mesh.vertexPositionData; + float *normal_ptr = (float *)input_mesh.vertexNormalData; + + vertex_ptr[i] = p_vertices[i]; + normal_ptr[i] = p_normals[i]; + } + + xatlas::CharterOptions chart_options; + xatlas::PackerOptions pack_options; + + pack_options.method = xatlas::PackMethod::TexelArea; + pack_options.texelArea = 1.0 / p_texel_size; + pack_options.quality = 3; + + xatlas::Atlas *atlas = xatlas::Create(); + printf("adding mesh..\n"); + xatlas::AddMeshError err = xatlas::AddMesh(atlas, input_mesh); + ERR_EXPLAINC(xatlas::StringForEnum(err.code)); + ERR_FAIL_COND_V(err.code != xatlas::AddMeshErrorCode::Success, false); + + printf("generate..\n"); + xatlas::Generate(atlas, chart_options, pack_options); + + *r_size_hint_x = xatlas::GetWidth(atlas); + *r_size_hint_y = xatlas::GetHeight(atlas); + + float w = *r_size_hint_x; + float h = *r_size_hint_y; + + if (w == 0 || h == 0) { + return false; //could not bake + } + + const xatlas::OutputMesh *const *output_meshes = xatlas::GetOutputMeshes(atlas); + + const xatlas::OutputMesh *output = output_meshes[0]; + + *r_vertex = (int *)malloc(sizeof(int) * output->vertexCount); + *r_uv = (float *)malloc(sizeof(float) * output->vertexCount * 2); + *r_index = (int *)malloc(sizeof(int) * output->indexCount); + + float max_x = 0; + float max_y = 0; + for (int i = 0; i < output->vertexCount; i++) { + (*r_vertex)[i] = output->vertexArray[i].xref; + (*r_uv)[i * 2 + 0] = output->vertexArray[i].uv[0] / w; + (*r_uv)[i * 2 + 1] = output->vertexArray[i].uv[1] / h; + max_x = MAX(max_x, output->vertexArray[i].uv[0]); + max_y = MAX(max_y, output->vertexArray[i].uv[1]); + } + + printf("final texsize: %f,%f - max %f,%f\n", w, h, max_x, max_y); + *r_vertex_count = output->vertexCount; + + for (int i = 0; i < output->indexCount; i++) { + (*r_index)[i] = output->indexArray[i]; + } + + *r_index_count = output->indexCount; + + //xatlas::Destroy(atlas); + free((void *)input_mesh.indexData); + free((void *)input_mesh.vertexPositionData); + free((void *)input_mesh.vertexNormalData); + free((void *)input_mesh.faceMaterialData); + printf("done"); + return true; +} + +void register_xatlas_unwrap_types() { + + array_mesh_lightmap_unwrap_callback = xatlas_mesh_lightmap_unwrap_callback; +} + +void unregister_xatlas_unwrap_types() { +} diff --git a/modules/xatlas_unwrap/register_types.h b/modules/xatlas_unwrap/register_types.h new file mode 100644 index 0000000000..fd8d56fa53 --- /dev/null +++ b/modules/xatlas_unwrap/register_types.h @@ -0,0 +1,32 @@ +/*************************************************************************/ +/* register_types.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. */ +/*************************************************************************/ + +void register_xatlas_unwrap_types(); +void unregister_xatlas_unwrap_types(); diff --git a/platform/SCsub b/platform/SCsub index 0f9c2047a0..aa83154ee0 100644 --- a/platform/SCsub +++ b/platform/SCsub @@ -29,5 +29,3 @@ platform_sources.append('register_platform_apis.gen.cpp') lib = env.add_library('platform', platform_sources) env.Prepend(LIBS=lib) - -Export('env') diff --git a/platform/android/SCsub b/platform/android/SCsub index 31fee5722c..da2219b9e4 100644 --- a/platform/android/SCsub +++ b/platform/android/SCsub @@ -1,12 +1,12 @@ #!/usr/bin/env python +Import('env') + import shutil from compat import open_utf8 from distutils.version import LooseVersion from detect import get_ndk_version -Import('env') - android_files = [ 'os_android.cpp', @@ -18,17 +18,16 @@ android_files = [ 'dir_access_jandroid.cpp', 'thread_jandroid.cpp', 'audio_driver_jandroid.cpp', - 'ifaddrs_android.cpp', - 'android_native_app_glue.c', 'java_glue.cpp', - 'cpu-features.c', 'java_class_wrapper.cpp', # 'power_android.cpp' ] -# env.Depends('#core/math/vector3.h', 'vector3_psp.h') - -#obj = env.SharedObject('godot_android.cpp') +thirdparty_files = [ + 'ifaddrs_android.cpp', + 'android_native_app_glue.c', + 'cpu-features.c', +] env_android = env.Clone() if env['target'] == "profile": @@ -38,6 +37,11 @@ android_objects = [] for x in android_files: android_objects.append(env_android.SharedObject(x)) +env_thirdparty = env_android.Clone() +env_thirdparty.disable_warnings() +for x in thirdparty_files: + android_objects.append(env_thirdparty.SharedObject(x)) + prog = None abspath = env.Dir(".").abspath @@ -174,4 +178,4 @@ if lib_arch_dir != '': ndk_version = get_ndk_version(env["ANDROID_NDK_ROOT"]) if ndk_version != None and LooseVersion(ndk_version) >= LooseVersion("15.0.4075724"): stl_lib_path = str(env['ANDROID_NDK_ROOT']) + '/sources/cxx-stl/llvm-libc++/libs/' + lib_arch_dir + '/libc++_shared.so' - env_android.Command(out_dir + '/libc++_shared.so', stl_lib_path, Copy("$TARGET", "$SOURCE"))
\ No newline at end of file + env_android.Command(out_dir + '/libc++_shared.so', stl_lib_path, Copy("$TARGET", "$SOURCE")) diff --git a/platform/android/audio_driver_jandroid.cpp b/platform/android/audio_driver_jandroid.cpp index 46bd691290..4fab40d534 100644 --- a/platform/android/audio_driver_jandroid.cpp +++ b/platform/android/audio_driver_jandroid.cpp @@ -30,8 +30,8 @@ #include "audio_driver_jandroid.h" -#include "os/os.h" -#include "project_settings.h" +#include "core/os/os.h" +#include "core/project_settings.h" #include "thread_jandroid.h" #ifndef ANDROID_NATIVE_ACTIVITY diff --git a/platform/android/audio_driver_opensl.cpp b/platform/android/audio_driver_opensl.cpp index 28e3ea962f..21c61f6ca0 100644 --- a/platform/android/audio_driver_opensl.cpp +++ b/platform/android/audio_driver_opensl.cpp @@ -38,12 +38,7 @@ /* Structure for passing information to callback function */ void AudioDriverOpenSL::_buffer_callback( - SLAndroidSimpleBufferQueueItf queueItf - /* SLuint32 eventFlags, - const void * pBuffer, - SLuint32 bufferSize, - SLuint32 dataUsed*/ -) { + SLAndroidSimpleBufferQueueItf queueItf) { bool mix = true; @@ -85,7 +80,6 @@ void AudioDriverOpenSL::_buffer_callbacks( AudioDriverOpenSL *ad = (AudioDriverOpenSL *)pContext; - //ad->_buffer_callback(queueItf,eventFlags,pBuffer,bufferSize,dataUsed); ad->_buffer_callback(queueItf); } @@ -98,12 +92,9 @@ const char *AudioDriverOpenSL::get_name() const { Error AudioDriverOpenSL::init() { - SLresult - res; + SLresult res; SLEngineOption EngineOption[] = { - (SLuint32)SL_ENGINEOPTION_THREADSAFE, - (SLuint32)SL_BOOLEAN_TRUE - + { (SLuint32)SL_ENGINEOPTION_THREADSAFE, (SLuint32)SL_BOOLEAN_TRUE } }; res = slCreateEngine(&sl, 1, EngineOption, 0, NULL, NULL); if (res != SL_RESULT_SUCCESS) { @@ -126,8 +117,6 @@ void AudioDriverOpenSL::start() { mutex = Mutex::create(); active = false; - SLint32 numOutputs = 0; - SLuint32 deviceID = 0; SLresult res; buffer_size = 1024; diff --git a/platform/android/audio_driver_opensl.h b/platform/android/audio_driver_opensl.h index 88cb122414..39e1315a02 100644 --- a/platform/android/audio_driver_opensl.h +++ b/platform/android/audio_driver_opensl.h @@ -31,7 +31,7 @@ #ifndef AUDIO_DRIVER_OPENSL_H #define AUDIO_DRIVER_OPENSL_H -#include "os/mutex.h" +#include "core/os/mutex.h" #include "servers/audio_server.h" #include <SLES/OpenSLES.h> @@ -70,19 +70,10 @@ class AudioDriverOpenSL : public AudioDriver { static AudioDriverOpenSL *s_ad; void _buffer_callback( - SLAndroidSimpleBufferQueueItf queueItf - /* SLuint32 eventFlags, - const void * pBuffer, - SLuint32 bufferSize, - SLuint32 dataUsed*/ - ); + SLAndroidSimpleBufferQueueItf queueItf); static void _buffer_callbacks( SLAndroidSimpleBufferQueueItf queueItf, - /*SLuint32 eventFlags, - const void * pBuffer, - SLuint32 bufferSize, - SLuint32 dataUsed,*/ void *pContext); public: diff --git a/platform/android/build.gradle.template b/platform/android/build.gradle.template index cc45fee95f..18ffc74fc3 100644 --- a/platform/android/build.gradle.template +++ b/platform/android/build.gradle.template @@ -1,10 +1,11 @@ buildscript { repositories { + google() jcenter() $$GRADLE_REPOSITORY_URLS$$ } dependencies { - classpath 'com.android.tools.build:gradle:2.3.3' + classpath 'com.android.tools.build:gradle:3.2.0' $$GRADLE_CLASSPATH$$ } } @@ -13,9 +14,9 @@ apply plugin: 'com.android.application' allprojects { repositories { - jcenter() mavenCentral() google() + jcenter() $$GRADLE_REPOSITORY_URLS$$ } } @@ -32,7 +33,7 @@ android { } compileSdkVersion 27 - buildToolsVersion "27.0.3" + buildToolsVersion "28.0.3" useLibrary 'org.apache.http.legacy' packagingOptions { @@ -75,9 +76,11 @@ android { $$GRADLE_JNI_DIRS$$ ] } + applicationVariants.all { variant -> - // ApplicationVariant is undocumented, but this method is widely used; may break with another version of the Android Gradle plugin - variant.outputs.get(0).setOutputFile(new File("${projectDir}/../../../bin", "android_${variant.name}.apk")) + variant.outputs.all { output -> + output.outputFileName = "../../../../../../../bin/android_${variant.name}.apk" + } } } diff --git a/platform/android/detect.py b/platform/android/detect.py index b22e85b2c1..e4cab2fb23 100644 --- a/platform/android/detect.py +++ b/platform/android/detect.py @@ -186,7 +186,7 @@ def configure(env): env.PrependENVPath('PATH', tools_path) ccache_path = os.environ.get("CCACHE") - if ccache_path == None: + if ccache_path is None: env['CC'] = compiler_path + '/clang' env['CXX'] = compiler_path + '/clang++' else: @@ -258,9 +258,10 @@ def configure(env): if ndk_version != None and LooseVersion(ndk_version) >= LooseVersion("15.0.4075724"): if LooseVersion(ndk_version) >= LooseVersion("17.1.4828580"): env.Append(LINKFLAGS=['-Wl,--exclude-libs,libgcc.a','-Wl,--exclude-libs,libatomic.a','-nostdlib++']) + else: + env.Append(LINKFLAGS=[env["ANDROID_NDK_ROOT"] +"/sources/cxx-stl/llvm-libc++/libs/"+arch_subpath+"/libandroid_support.a"]) env.Append(LINKFLAGS=['-shared', '--sysroot=' + lib_sysroot, '-Wl,--warn-shared-textrel']) env.Append(LIBPATH=[env["ANDROID_NDK_ROOT"] + "/sources/cxx-stl/llvm-libc++/libs/"+arch_subpath+"/"]) - env.Append(LINKFLAGS=[env["ANDROID_NDK_ROOT"] +"/sources/cxx-stl/llvm-libc++/libs/"+arch_subpath+"/libandroid_support.a"]) env.Append(LINKFLAGS=[env["ANDROID_NDK_ROOT"] +"/sources/cxx-stl/llvm-libc++/libs/"+arch_subpath+"/libc++_shared.so"]) else: env.Append(LINKFLAGS=['-shared', '--sysroot=' + lib_sysroot, '-Wl,--warn-shared-textrel']) @@ -292,7 +293,7 @@ def configure(env): # Return NDK version string in source.properties (adapted from the Chromium project). def get_ndk_version(path): - if path == None: + if path is None: return None prop_file_path = os.path.join(path, "source.properties") try: diff --git a/platform/android/dir_access_android.h b/platform/android/dir_access_android.h index 085d7160cd..3ac0bd6332 100644 --- a/platform/android/dir_access_android.h +++ b/platform/android/dir_access_android.h @@ -33,7 +33,7 @@ #ifdef ANDROID_NATIVE_ACTIVITY -#include "os/dir_access.h" +#include "core/os/dir_access.h" #include <android/asset_manager.h> #include <android/log.h> #include <android_native_app_glue.h> diff --git a/platform/android/dir_access_jandroid.cpp b/platform/android/dir_access_jandroid.cpp index ee5ae156b7..6a95277585 100644 --- a/platform/android/dir_access_jandroid.cpp +++ b/platform/android/dir_access_jandroid.cpp @@ -31,8 +31,8 @@ #ifndef ANDROID_NATIVE_ACTIVITY #include "dir_access_jandroid.h" +#include "core/print_string.h" #include "file_access_jandroid.h" -#include "print_string.h" #include "thread_jandroid.h" jobject DirAccessJAndroid::io = NULL; diff --git a/platform/android/dir_access_jandroid.h b/platform/android/dir_access_jandroid.h index 8dc52ab9c8..1653fb0aa5 100644 --- a/platform/android/dir_access_jandroid.h +++ b/platform/android/dir_access_jandroid.h @@ -33,8 +33,8 @@ #ifndef ANDROID_NATIVE_ACTIVITY +#include "core/os/dir_access.h" #include "java_glue.h" -#include "os/dir_access.h" #include <stdio.h> class DirAccessJAndroid : public DirAccess { diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index b76b0d5dbe..3766f732e4 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -30,17 +30,17 @@ #include "export.h" +#include "core/io/marshalls.h" +#include "core/io/zip_io.h" +#include "core/os/file_access.h" +#include "core/os/os.h" +#include "core/project_settings.h" +#include "core/version.h" #include "editor/editor_export.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" -#include "io/marshalls.h" -#include "io/zip_io.h" -#include "os/file_access.h" -#include "os/os.h" #include "platform/android/logo.gen.h" #include "platform/android/run_icon.gen.h" -#include "project_settings.h" -#include "version.h" #include <string.h> @@ -194,8 +194,8 @@ static const char *android_perms[] = { }; struct LauncherIcon { - char *option_id; - char *export_path; + const char *option_id; + const char *export_path; }; static const LauncherIcon launcher_icons[] = { @@ -344,6 +344,7 @@ class EditorExportAndroid : public EditorExportPlatform { } d.name = vendor + " " + device; + if (device == String()) continue; } ndevices.push_back(d); @@ -394,7 +395,7 @@ class EditorExportAndroid : public EditorExportPlatform { return aname; } - String get_package_name(const String &p_package) { + String get_package_name(const String &p_package) const { String pname = p_package; String basename = ProjectSettings::get_singleton()->get("application/config/name"); @@ -419,6 +420,70 @@ class EditorExportAndroid : public EditorExportPlatform { return pname; } + bool is_package_name_valid(const String &p_package, String *r_error = NULL) const { + + String pname = p_package; + + if (pname.length() == 0) { + if (r_error) { + *r_error = "Package name is missing."; + } + return false; + } + + int segments = 0; + bool first = true; + for (int i = 0; i < pname.length(); i++) { + CharType c = pname[i]; + if (first && c == '.') { + if (r_error) { + *r_error = "Package segments must be of non-zero length."; + } + return false; + } + if (c == '.') { + segments++; + first = true; + continue; + } + if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_')) { + if (r_error) { + *r_error = "The character '" + String::chr(c) + "' is not allowed in Android application package names."; + } + return false; + } + if (first && (c >= '0' && c <= '9')) { + if (r_error) { + *r_error = "A digit cannot be the first character in a package segment."; + } + return false; + } + if (first && c == '_') { + if (r_error) { + *r_error = "The character '" + String::chr(c) + "' cannot be the first character in a package segment."; + } + return false; + } + first = false; + } + + if (segments == 0) { + if (r_error) { + *r_error = "The package must have at least one '.' separator."; + } + return false; + } + + if (first) { + if (r_error) { + *r_error = "Package segments must be of non-zero length."; + } + return false; + } + + return true; + } + static bool _should_compress_asset(const String &p_path, const Vector<uint8_t> &p_data) { /* @@ -576,11 +641,11 @@ class EditorExportAndroid : public EditorExportPlatform { uint32_t ofs = 8; uint32_t string_count = 0; - uint32_t styles_count = 0; + //uint32_t styles_count = 0; uint32_t string_flags = 0; uint32_t string_data_offset = 0; - uint32_t styles_offset = 0; + //uint32_t styles_offset = 0; uint32_t string_table_begins = 0; uint32_t string_table_ends = 0; Vector<uint8_t> stable_extra; @@ -630,16 +695,16 @@ class EditorExportAndroid : public EditorExportPlatform { int iofs = ofs + 8; string_count = decode_uint32(&p_manifest[iofs]); - styles_count = decode_uint32(&p_manifest[iofs + 4]); + //styles_count = decode_uint32(&p_manifest[iofs + 4]); string_flags = decode_uint32(&p_manifest[iofs + 8]); string_data_offset = decode_uint32(&p_manifest[iofs + 12]); - styles_offset = decode_uint32(&p_manifest[iofs + 16]); + //styles_offset = decode_uint32(&p_manifest[iofs + 16]); /* printf("string count: %i\n",string_count); printf("flags: %i\n",string_flags); printf("sdata ofs: %i\n",string_data_offset); printf("styles ofs: %i\n",styles_offset); - */ + */ uint32_t st_offset = iofs + 20; string_table.resize(string_count); uint32_t string_end = 0; @@ -759,7 +824,6 @@ class EditorExportAndroid : public EditorExportPlatform { // save manifest ending so we can restore it Vector<uint8_t> manifest_end; uint32_t manifest_cur_size = p_manifest.size(); - uint32_t node_size = size; manifest_end.resize(p_manifest.size() - ofs); memcpy(manifest_end.ptrw(), &p_manifest[ofs], manifest_end.size()); @@ -1054,7 +1118,12 @@ public: if (api == 0) r_features->push_back("etc"); else*/ - r_features->push_back("etc2"); + String driver = ProjectSettings::get_singleton()->get("rendering/quality/driver/driver_name"); + if (driver == "GLES2") { + r_features->push_back("etc"); + } else { + r_features->push_back("etc2"); + } Vector<String> abis = get_enabled_abis(p_preset); for (int i = 0; i < abis.size(); ++i) { @@ -1080,8 +1149,9 @@ public: r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_normal"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_large"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_xlarge"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/opengl_debug"), false)); - for (int i = 0; i < sizeof(launcher_icons) / sizeof(launcher_icons[0]); ++i) { + for (unsigned int i = 0; i < sizeof(launcher_icons) / sizeof(launcher_icons[0]); ++i) { r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_icons[i].option_id, PROPERTY_HINT_FILE, "*.png"), "")); } @@ -1337,7 +1407,7 @@ public: if (!FileAccess::exists(adb)) { valid = false; - err += "ADB executable not configured in editor settings.\n"; + err += "ADB executable not configured in the Editor Settings.\n"; } String js = EditorSettings::get_singleton()->get("export/android/jarsigner"); @@ -1345,7 +1415,7 @@ public: if (!FileAccess::exists(js)) { valid = false; - err += "OpenJDK 6 jarsigner not configured in editor settings.\n"; + err += "OpenJDK 8 jarsigner not configured in the Editor Settings.\n"; } String dk = EditorSettings::get_singleton()->get("export/android/debug_keystore"); @@ -1353,7 +1423,7 @@ public: if (!FileAccess::exists(dk)) { valid = false; - err += "Debug Keystore not configured in editor settings.\n"; + err += "Debug keystore not configured in the Editor Settings.\n"; } bool apk_expansion = p_preset->get("apk_expansion/enable"); @@ -1372,16 +1442,27 @@ public: if (apk_expansion_pkey == "") { valid = false; - err += "Invalid public key for apk expansion.\n"; + err += "Invalid public key for APK expansion.\n"; } } + String pn = p_preset->get("package/unique_name"); + String pn_err; + + if (!is_package_name_valid(get_package_name(pn), &pn_err)) { + + valid = false; + err += "Invalid package name - " + pn_err + "\n"; + } + r_error = err; return valid; } - virtual String get_binary_extension(const Ref<EditorExportPreset> &p_preset) const { - return "apk"; + virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const { + List<String> list; + list.push_back("apk"); + return list; } virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) { @@ -1433,6 +1514,7 @@ public: bool use_32_fb = p_preset->get("graphics/32_bits_framebuffer"); bool immersive = p_preset->get("screen/immersive_mode"); + bool debug_opengl = p_preset->get("screen/opengl_debug"); bool _signed = p_preset->get("package/signed"); @@ -1485,7 +1567,7 @@ public: if (file == "res/drawable/icon.png") { bool found = false; - for (int i = 0; i < sizeof(launcher_icons) / sizeof(launcher_icons[0]); ++i) { + for (unsigned int i = 0; i < sizeof(launcher_icons) / sizeof(launcher_icons[0]); ++i) { String icon_path = String(p_preset->get(launcher_icons[i].option_id)).strip_edges(); if (icon_path != "" && icon_path.ends_with(".png")) { FileAccess *f = FileAccess::open(icon_path, FileAccess::READ); @@ -1591,8 +1673,11 @@ public: String apkfname = "main." + itos(version_code) + "." + get_package_name(package_name) + ".obb"; String fullpath = p_path.get_base_dir().plus_file(apkfname); err = save_pack(p_preset, fullpath); + if (err != OK) { + unzClose(pkg); EditorNode::add_io_error("Could not write expansion package file: " + apkfname); + return OK; } @@ -1616,7 +1701,7 @@ public: APKExportData ed; ed.ep = &ep; ed.apk = unaligned_apk; - for (int i = 0; i < sizeof(launcher_icons) / sizeof(launcher_icons[0]); ++i) { + for (unsigned int i = 0; i < sizeof(launcher_icons) / sizeof(launcher_icons[0]); ++i) { String icon_path = String(p_preset->get(launcher_icons[i].option_id)).strip_edges(); if (icon_path != "" && icon_path.ends_with(".png") && FileAccess::exists(icon_path)) { Vector<uint8_t> data = FileAccess::get_file_as_array(icon_path); @@ -1631,6 +1716,9 @@ public: if (immersive) cl.push_back("--use_immersive"); + if (debug_opengl) + cl.push_back("--debug_opengl"); + if (cl.size()) { //add comandline Vector<uint8_t> clf; @@ -1677,7 +1765,7 @@ public: String jarsigner = EditorSettings::get_singleton()->get("export/android/jarsigner"); if (!FileAccess::exists(jarsigner)) { - EditorNode::add_io_error("'jarsigner' could not be found.\nPlease supply a path in the editor settings.\nResulting apk is unsigned."); + EditorNode::add_io_error("'jarsigner' could not be found.\nPlease supply a path in the Editor Settings.\nThe resulting APK is unsigned."); return OK; } @@ -1689,14 +1777,14 @@ public: password = EditorSettings::get_singleton()->get("export/android/debug_keystore_pass"); user = EditorSettings::get_singleton()->get("export/android/debug_keystore_user"); - ep.step("Signing Debug APK...", 103); + ep.step("Signing debug APK...", 103); } else { keystore = release_keystore; password = release_password; user = release_username; - ep.step("Signing Release APK...", 103); + ep.step("Signing release APK...", 103); } if (!FileAccess::exists(keystore)) { @@ -1739,7 +1827,7 @@ public: OS::get_singleton()->execute(jarsigner, args, true, NULL, NULL, &retval); if (retval) { - EditorNode::add_io_error("'jarsigner' verification of APK failed. Make sure to use jarsigner from Java 6."); + EditorNode::add_io_error("'jarsigner' verification of APK failed. Make sure to use a jarsigner from OpenJDK 8."); return ERR_CANT_CREATE; } } diff --git a/platform/android/file_access_android.cpp b/platform/android/file_access_android.cpp index c2eed50e4c..4c7436a5dc 100644 --- a/platform/android/file_access_android.cpp +++ b/platform/android/file_access_android.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "file_access_android.h" -#include "print_string.h" +#include "core/print_string.h" AAssetManager *FileAccessAndroid::asset_manager = NULL; diff --git a/platform/android/file_access_android.h b/platform/android/file_access_android.h index 03f4c59521..1ee8697fa4 100644 --- a/platform/android/file_access_android.h +++ b/platform/android/file_access_android.h @@ -31,7 +31,7 @@ #ifndef FILE_ACCESS_ANDROID_H #define FILE_ACCESS_ANDROID_H -#include "os/file_access.h" +#include "core/os/file_access.h" #include <android/asset_manager.h> #include <android/log.h> #include <stdio.h> diff --git a/platform/android/file_access_jandroid.cpp b/platform/android/file_access_jandroid.cpp index 214e273d9b..573200bcf9 100644 --- a/platform/android/file_access_jandroid.cpp +++ b/platform/android/file_access_jandroid.cpp @@ -31,7 +31,7 @@ #ifndef ANDROID_NATIVE_ACTIVITY #include "file_access_jandroid.h" -#include "os/os.h" +#include "core/os/os.h" #include "thread_jandroid.h" #include <unistd.h> diff --git a/platform/android/file_access_jandroid.h b/platform/android/file_access_jandroid.h index 72f81ee02e..39c201ba85 100644 --- a/platform/android/file_access_jandroid.h +++ b/platform/android/file_access_jandroid.h @@ -33,8 +33,8 @@ #ifndef ANDROID_NATIVE_ACTIVITY +#include "core/os/file_access.h" #include "java_glue.h" -#include "os/file_access.h" class FileAccessJAndroid : public FileAccess { static jobject io; diff --git a/platform/android/globals/global_defaults.cpp b/platform/android/globals/global_defaults.cpp index a315f80452..efeb8598e5 100644 --- a/platform/android/globals/global_defaults.cpp +++ b/platform/android/globals/global_defaults.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "global_defaults.h" -#include "project_settings.h" +#include "core/project_settings.h" void register_android_global_defaults() { } diff --git a/platform/android/godot_android.cpp b/platform/android/godot_android.cpp index 061e05f5ee..c46c6f7804 100644 --- a/platform/android/godot_android.cpp +++ b/platform/android/godot_android.cpp @@ -30,11 +30,11 @@ #ifdef ANDROID_NATIVE_ACTIVITY -#include "engine.h" +#include "core/engine.h" +#include "core/project_settings.h" #include "file_access_android.h" #include "main/main.h" #include "os_android.h" -#include "project_settings.h" #include <EGL/egl.h> #include <android/log.h> @@ -408,7 +408,7 @@ static void engine_draw_frame(struct engine *engine) { // Just fill the screen with a color. //glClearColor(0,1,0,1); //glClear(GL_COLOR_BUFFER_BIT); - if (engine->os && engine->os->main_loop_iterate() == true) { + if (engine->os && engine->os->main_loop_iterate()) { engine->requested_quit = true; return; //should exit instead diff --git a/platform/android/java/gradle/wrapper/gradle-wrapper.properties b/platform/android/java/gradle/wrapper/gradle-wrapper.properties index fe37fa74a9..6fb3a79546 100644 --- a/platform/android/java/gradle/wrapper/gradle-wrapper.properties +++ b/platform/android/java/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip diff --git a/platform/android/java/src/org/godotengine/godot/Godot.java b/platform/android/java/src/org/godotengine/godot/Godot.java index ef798fc790..88194f00d1 100644 --- a/platform/android/java/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/src/org/godotengine/godot/Godot.java @@ -59,6 +59,9 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.net.Uri; import android.media.MediaPlayer; +import android.content.ClipboardManager; +import android.content.ClipData; + import java.lang.reflect.Method; import java.util.List; import java.util.ArrayList; @@ -103,6 +106,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC private TextView mAverageSpeed; private TextView mTimeRemaining; private ProgressBar mPB; + private ClipboardManager mClipboard; private View mDashboard; private View mCellMessage; @@ -112,6 +116,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC private boolean use_32_bits = false; private boolean use_immersive = false; + private boolean use_debug_opengl = false; private boolean mStatePaused; private int mState; private boolean keep_screen_on = true; @@ -180,6 +185,9 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC protected void onMainActivityResult(int requestCode, int resultCode, Intent data) { } + protected void onMainRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + } + protected void onMainPause() {} protected void onMainResume() {} protected void onMainDestroy() {} @@ -247,6 +255,13 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC } }; + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + for (int i = 0; i < singleton_count; i++) { + singletons[i].onMainRequestPermissionsResult(requestCode, permissions, grantResults); + } + }; + public void onVideoInit() { boolean use_gl3 = getGLESVersionCode() >= 0x00030000; @@ -264,7 +279,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC // ...add to FrameLayout layout.addView(edittext); - mView = new GodotView(getApplication(), io, use_gl3, use_32_bits, this); + mView = new GodotView(getApplication(), io, use_gl3, use_32_bits, use_debug_opengl, this); layout.addView(mView, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); edittext.setView(mView); io.setEdit(edittext); @@ -441,6 +456,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC Window window = getWindow(); //window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); + mClipboard = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE); //check for apk expansion API if (true) { @@ -456,6 +472,8 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC boolean has_extra = i < command_line.length - 1; if (command_line[i].equals("--use_depth_32")) { use_32_bits = true; + } else if (command_line[i].equals("--debug_opengl")) { + use_debug_opengl = true; } else if (command_line[i].equals("--use_immersive")) { use_immersive = true; if (Build.VERSION.SDK_INT >= 19.0) { // check if the application runs on an android 4.4+ @@ -607,6 +625,24 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC } } + public String getClipboard() { + + String copiedText = ""; + + if (mClipboard.getPrimaryClip() != null) { + ClipData.Item item = mClipboard.getPrimaryClip().getItemAt(0); + copiedText = item.getText().toString(); + } + + return copiedText; + } + + public void setClipboard(String p_text) { + + ClipData clip = ClipData.newPlainText("myLabel", p_text); + mClipboard.setPrimaryClip(clip); + } + @Override protected void onResume() { super.onResume(); diff --git a/platform/android/java/src/org/godotengine/godot/GodotView.java b/platform/android/java/src/org/godotengine/godot/GodotView.java index 23723c2696..4cb4db33de 100644 --- a/platform/android/java/src/org/godotengine/godot/GodotView.java +++ b/platform/android/java/src/org/godotengine/godot/GodotView.java @@ -81,16 +81,18 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { private static boolean firsttime = true; private static boolean use_gl3 = false; private static boolean use_32 = false; + private static boolean use_debug_opengl = false; private Godot activity; private InputManagerCompat mInputManager; - public GodotView(Context context, GodotIO p_io, boolean p_use_gl3, boolean p_use_32_bits, Godot p_activity) { + public GodotView(Context context, GodotIO p_io, boolean p_use_gl3, boolean p_use_32_bits, boolean p_use_debug_opengl, Godot p_activity) { super(context); ctx = context; io = p_io; use_gl3 = p_use_gl3; use_32 = p_use_32_bits; + use_debug_opengl = p_use_debug_opengl; activity = p_activity; @@ -202,48 +204,65 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { return i; } } - onInputDeviceAdded(device_id); - return joy_devices.size() - 1; + + return -1; } @Override public void onInputDeviceAdded(int deviceId) { - joystick joy = new joystick(); - joy.device_id = deviceId; - final int id = joy_devices.size(); - InputDevice device = mInputManager.getInputDevice(deviceId); - final String name = device.getName(); - joy.name = device.getName(); - joy.axes = new ArrayList<InputDevice.MotionRange>(); - joy.hats = new ArrayList<InputDevice.MotionRange>(); - List<InputDevice.MotionRange> ranges = device.getMotionRanges(); - Collections.sort(ranges, new RangeComparator()); - for (InputDevice.MotionRange range : ranges) { - if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) { - joy.hats.add(range); - } else { - joy.axes.add(range); + int id = find_joy_device(deviceId); + + // Check if the device has not been already added + if (id < 0) { + InputDevice device = mInputManager.getInputDevice(deviceId); + + id = joy_devices.size(); + + joystick joy = new joystick(); + joy.device_id = deviceId; + joy.name = device.getName(); + joy.axes = new ArrayList<InputDevice.MotionRange>(); + joy.hats = new ArrayList<InputDevice.MotionRange>(); + + List<InputDevice.MotionRange> ranges = device.getMotionRanges(); + Collections.sort(ranges, new RangeComparator()); + + for (InputDevice.MotionRange range : ranges) { + if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) { + joy.hats.add(range); + } else { + joy.axes.add(range); + } } + + joy_devices.add(joy); + + final int device_id = id; + final String name = joy.name; + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.joyconnectionchanged(device_id, true, name); + } + }); } - joy_devices.add(joy); - queueEvent(new Runnable() { - @Override - public void run() { - GodotLib.joyconnectionchanged(id, true, name); - } - }); } @Override public void onInputDeviceRemoved(int deviceId) { - final int id = find_joy_device(deviceId); - joy_devices.remove(id); - queueEvent(new Runnable() { - @Override - public void run() { - GodotLib.joyconnectionchanged(id, false, ""); - } - }); + final int device_id = find_joy_device(deviceId); + + // Check if the evice has not been already removed + if (device_id > -1) { + joy_devices.remove(device_id); + + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.joyconnectionchanged(device_id, false, ""); + } + }); + } } @Override @@ -264,15 +283,18 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { if ((source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK || (source & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD || (source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) { final int button = get_godot_button(keyCode); - final int device = find_joy_device(event.getDeviceId()); + final int device_id = find_joy_device(event.getDeviceId()); - queueEvent(new Runnable() { - @Override - public void run() { - GodotLib.joybutton(device, button, false); - } - }); - return true; + // Check if the device exists + if (device_id > -1) { + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.joybutton(device_id, button, false); + } + }); + return true; + } } else { final int chr = event.getUnicodeChar(0); queueEvent(new Runnable() { @@ -282,6 +304,7 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { } }); }; + return super.onKeyUp(keyCode, event); }; @@ -306,18 +329,20 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { if (event.getRepeatCount() > 0) // ignore key echo return true; - final int button = get_godot_button(keyCode); - final int device = find_joy_device(event.getDeviceId()); - //Log.e(TAG, String.format("joy button down! button %x, %d, device %d", keyCode, button, device)); - queueEvent(new Runnable() { - @Override - public void run() { - GodotLib.joybutton(device, button, true); - } - }); - return true; + final int button = get_godot_button(keyCode); + final int device_id = find_joy_device(event.getDeviceId()); + // Check if the device exists + if (device_id > -1) { + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.joybutton(device_id, button, true); + } + }); + return true; + } } else { final int chr = event.getUnicodeChar(0); queueEvent(new Runnable() { @@ -327,6 +352,7 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { } }); }; + return super.onKeyDown(keyCode, event); } @@ -336,33 +362,35 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK && event.getAction() == MotionEvent.ACTION_MOVE) { final int device_id = find_joy_device(event.getDeviceId()); - joystick joy = joy_devices.get(device_id); - for (int i = 0; i < joy.axes.size(); i++) { - InputDevice.MotionRange range = joy.axes.get(i); - final float value = (event.getAxisValue(range.getAxis()) - range.getMin()) / range.getRange() * 2.0f - 1.0f; - //Log.e(TAG, String.format("axis event: %d, value %f", i, value)); - final int idx = i; - queueEvent(new Runnable() { - @Override - public void run() { - GodotLib.joyaxis(device_id, idx, value); - } - }); - } + // Check if the device exists + if (device_id > -1) { + joystick joy = joy_devices.get(device_id); + + for (int i = 0; i < joy.axes.size(); i++) { + InputDevice.MotionRange range = joy.axes.get(i); + final float value = (event.getAxisValue(range.getAxis()) - range.getMin()) / range.getRange() * 2.0f - 1.0f; + final int idx = i; + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.joyaxis(device_id, idx, value); + } + }); + } - for (int i = 0; i < joy.hats.size(); i += 2) { - final int hatX = Math.round(event.getAxisValue(joy.hats.get(i).getAxis())); - final int hatY = Math.round(event.getAxisValue(joy.hats.get(i + 1).getAxis())); - //Log.e(TAG, String.format("HAT EVENT %d, %d", hatX, hatY)); - queueEvent(new Runnable() { - @Override - public void run() { - GodotLib.joyhat(device_id, hatX, hatY); - } - }); + for (int i = 0; i < joy.hats.size(); i += 2) { + final int hatX = Math.round(event.getAxisValue(joy.hats.get(i).getAxis())); + final int hatY = Math.round(event.getAxisValue(joy.hats.get(i + 1).getAxis())); + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.joyhat(device_id, hatX, hatY); + } + }); + } + return true; } - return true; }; return super.onGenericMotionEvent(event); @@ -406,6 +434,9 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { setRenderer(new Renderer()); } + private static final int _EGL_CONTEXT_FLAGS_KHR = 0x30FC; + private static final int _EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR = 0x00000001; + private static class ContextFactory implements GLSurfaceView.EGLContextFactory { private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098; public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) { @@ -415,9 +446,16 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { Log.w(TAG, "creating OpenGL ES 2.0 context :"); checkEglError("Before eglCreateContext", egl); - int[] attrib_list2 = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE }; - int[] attrib_list3 = { EGL_CONTEXT_CLIENT_VERSION, 3, EGL10.EGL_NONE }; - EGLContext context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, use_gl3 ? attrib_list3 : attrib_list2); + EGLContext context; + if (use_debug_opengl) { + int[] attrib_list2 = { EGL_CONTEXT_CLIENT_VERSION, 2, _EGL_CONTEXT_FLAGS_KHR, _EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, EGL10.EGL_NONE }; + int[] attrib_list3 = { EGL_CONTEXT_CLIENT_VERSION, 3, _EGL_CONTEXT_FLAGS_KHR, _EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, EGL10.EGL_NONE }; + context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, use_gl3 ? attrib_list3 : attrib_list2); + } else { + int[] attrib_list2 = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE }; + int[] attrib_list3 = { EGL_CONTEXT_CLIENT_VERSION, 3, EGL10.EGL_NONE }; + context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, use_gl3 ? attrib_list3 : attrib_list2); + } checkEglError("After eglCreateContext", egl); return context; } diff --git a/platform/android/java_class_wrapper.h b/platform/android/java_class_wrapper.h index 648c147ca8..ea3760452f 100644 --- a/platform/android/java_class_wrapper.h +++ b/platform/android/java_class_wrapper.h @@ -31,7 +31,7 @@ #ifndef JAVA_CLASS_WRAPPER_H #define JAVA_CLASS_WRAPPER_H -#include "reference.h" +#include "core/reference.h" #include <android/log.h> #include <jni.h> diff --git a/platform/android/java_glue.cpp b/platform/android/java_glue.cpp index 8bb1c38345..07e4048c12 100644 --- a/platform/android/java_glue.cpp +++ b/platform/android/java_glue.cpp @@ -33,16 +33,16 @@ #include "java_glue.h" #include "android/asset_manager_jni.h" #include "audio_driver_jandroid.h" +#include "core/engine.h" #include "core/os/keyboard.h" +#include "core/project_settings.h" #include "dir_access_jandroid.h" -#include "engine.h" #include "file_access_android.h" #include "file_access_jandroid.h" #include "java_class_wrapper.h" #include "main/input_default.h" #include "main/main.h" #include "os_android.h" -#include "project_settings.h" #include "thread_jandroid.h" #include <unistd.h> @@ -589,8 +589,6 @@ TST tst; static bool initialized = false; static int step = 0; -static bool resized = false; -static bool resized_reload = false; static Size2 new_size; static Vector3 accelerometer; static Vector3 gravity; @@ -607,6 +605,8 @@ static jobject _godot_instance; static jmethodID _openURI = 0; static jmethodID _getDataDir = 0; static jmethodID _getLocale = 0; +static jmethodID _getClipboard = 0; +static jmethodID _setClipboard = 0; static jmethodID _getModel = 0; static jmethodID _getScreenDPI = 0; static jmethodID _showKeyboard = 0; @@ -646,6 +646,19 @@ static String _get_locale() { return String(env->GetStringUTFChars(s, NULL)); } +static String _get_clipboard() { + JNIEnv *env = ThreadAndroid::get_env(); + jstring s = (jstring)env->CallObjectMethod(_godot_instance, _getClipboard); + return String(env->GetStringUTFChars(s, NULL)); +} + +static void _set_clipboard(const String &p_text) { + + JNIEnv *env = ThreadAndroid::get_env(); + jstring jStr = env->NewStringUTF(p_text.utf8().get_data()); + env->CallVoidMethod(_godot_instance, _setClipboard, jStr); +} + static String _get_model() { JNIEnv *env = ThreadAndroid::get_env(); @@ -774,8 +787,9 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en _setKeepScreenOn = env->GetMethodID(cls, "setKeepScreenOn", "(Z)V"); _alertDialog = env->GetMethodID(cls, "alert", "(Ljava/lang/String;Ljava/lang/String;)V"); _getGLESVersionCode = env->GetMethodID(cls, "getGLESVersionCode", "()I"); + _getClipboard = env->GetMethodID(cls, "getClipboard", "()Ljava/lang/String;"); + _setClipboard = env->GetMethodID(cls, "setClipboard", "(Ljava/lang/String;)V"); - jclass clsio = env->FindClass("org/godotengine/godot/Godot"); if (cls) { jclass c = env->GetObjectClass(gob); _openURI = env->GetMethodID(c, "openURI", "(Ljava/lang/String;)I"); @@ -807,7 +821,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en AudioDriverAndroid::setup(gob); } - os_android = new OS_Android(_gfx_init_func, env, _open_uri, _get_user_data_dir, _get_locale, _get_model, _get_screen_dpi, _show_vk, _hide_vk, _get_vk_height, _set_screen_orient, _get_unique_id, _get_system_dir, _get_gles_version_code, _play_video, _is_video_playing, _pause_video, _stop_video, _set_keep_screen_on, _alert, p_use_apk_expansion); + os_android = new OS_Android(_gfx_init_func, env, _open_uri, _get_user_data_dir, _get_locale, _get_model, _get_screen_dpi, _show_vk, _hide_vk, _get_vk_height, _set_screen_orient, _get_unique_id, _get_system_dir, _get_gles_version_code, _play_video, _is_video_playing, _pause_video, _stop_video, _set_keep_screen_on, _alert, _set_clipboard, _get_clipboard, p_use_apk_expansion); os_android->set_need_reload_hooks(p_need_reload_hook); char wd[500]; @@ -870,7 +884,7 @@ static void _initialize_java_modules() { ERR_CONTINUE(!initialize); } jobject obj = env->CallStaticObjectMethod(singletonClass, initialize, _godot_instance); - jobject gob = env->NewGlobalRef(obj); + env->NewGlobalRef(obj); } } } @@ -914,13 +928,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, j if (os_android) os_android->set_display_size(Size2(width, height)); - - /*input_mutex->lock(); - resized=true; - if (reload) - resized_reload=true; - new_size=Size2(width,height); - input_mutex->unlock();*/ } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jobject obj, bool p_32_bits) { @@ -969,7 +976,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, job os_android->process_gyroscope(gyroscope); - if (os_android->main_loop_iterate() == true) { + if (os_android->main_loop_iterate()) { jclass cls = env->FindClass("org/godotengine/godot/Godot"); jmethodID _finish = env->GetMethodID(cls, "forceQuit", "()V"); diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index c8bdf98923..96ff226402 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -39,7 +39,7 @@ #include "file_access_android.h" #include "main/main.h" #include "servers/visual/visual_server_raster.h" -//#include "servers/visual/visual_server_wrap_mt.h" +#include "servers/visual/visual_server_wrap_mt.h" #ifdef ANDROID_NATIVE_ACTIVITY #include "dir_access_android.h" @@ -62,12 +62,19 @@ public: int OS_Android::get_video_driver_count() const { - return 1; + return 2; } const char *OS_Android::get_video_driver_name(int p_driver) const { - return "GLES2"; + switch (p_driver) { + case VIDEO_DRIVER_GLES3: + return "GLES3"; + case VIDEO_DRIVER_GLES2: + return "GLES2"; + } + ERR_EXPLAIN("Invalid video driver index " + itos(p_driver)); + ERR_FAIL_V(NULL); } int OS_Android::get_audio_driver_count() const { @@ -132,28 +139,55 @@ Error OS_Android::initialize(const VideoMode &p_desired, int p_video_driver, int bool use_gl3 = get_gl_version_code_func() >= 0x00030000; use_gl3 = use_gl3 && (GLOBAL_GET("rendering/quality/driver/driver_name") == "GLES3"); - use_gl2 = !use_gl3; - - if (gfx_init_func) - gfx_init_func(gfx_init_ud, use_gl2); + bool gl_initialization_error = false; + + while (true) { + if (use_gl3) { + if (RasterizerGLES3::is_viable() == OK) { + if (gfx_init_func) + gfx_init_func(gfx_init_ud, false); + RasterizerGLES3::register_config(); + RasterizerGLES3::make_current(); + break; + } else { + if (GLOBAL_GET("rendering/quality/driver/driver_fallback") == "Best") { + p_video_driver = VIDEO_DRIVER_GLES2; + use_gl3 = false; + continue; + } else { + gl_initialization_error = true; + break; + } + } + } else { + if (RasterizerGLES2::is_viable() == OK) { + if (gfx_init_func) + gfx_init_func(gfx_init_ud, true); + RasterizerGLES2::register_config(); + RasterizerGLES2::make_current(); + break; + } else { + gl_initialization_error = true; + break; + } + } + } - if (use_gl2) { - RasterizerGLES2::register_config(); - RasterizerGLES2::make_current(); - video_driver_index = VIDEO_DRIVER_GLES2; - } else { - RasterizerGLES3::register_config(); - RasterizerGLES3::make_current(); - video_driver_index = VIDEO_DRIVER_GLES3; + if (gl_initialization_error) { + OS::get_singleton()->alert("Your device does not support any of the supported OpenGL versions.\n" + "Please try updating your Android version.", + "Unable to initialize Video driver"); + return ERR_UNAVAILABLE; } - visual_server = memnew(VisualServerRaster); - /* if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) { + video_driver_index = p_video_driver; + visual_server = memnew(VisualServerRaster); + if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) { visual_server = memnew(VisualServerWrapMT(visual_server, false)); - };*/ + } + visual_server->init(); - // visual_server->cursor_set_visible(false, 0); AudioDriverManager::initialize(p_audio_driver); @@ -221,13 +255,10 @@ int OS_Android::get_mouse_button_state() const { return 0; } + void OS_Android::set_window_title(const String &p_title) { } -//interesting byt not yet -//void set_clipboard(const String& p_text); -//String get_clipboard() const; - void OS_Android::set_video_mode(const VideoMode &p_video_mode, int p_screen) { } @@ -553,7 +584,7 @@ Error OS_Android::shell_open(String p_uri) { String OS_Android::get_resource_dir() const { - return "/"; //android has it's own filesystem for resources inside the APK + return "/"; //android has its own filesystem for resources inside the APK } String OS_Android::get_locale() const { @@ -563,6 +594,23 @@ String OS_Android::get_locale() const { return OS_Unix::get_locale(); } +void OS_Android::set_clipboard(const String &p_text) { + + if (set_clipboard_func) { + set_clipboard_func(p_text); + } else { + OS_Unix::set_clipboard(p_text); + } +} + +String OS_Android::get_clipboard() const { + if (get_clipboard_func) { + return get_clipboard_func(); + } + + return OS_Unix::get_clipboard(); +} + String OS_Android::get_model_name() const { if (get_model_func) @@ -627,13 +675,14 @@ String OS_Android::get_unique_id() const { return OS::get_unique_id(); } -Error OS_Android::native_video_play(String p_path, float p_volume) { +Error OS_Android::native_video_play(String p_path, float p_volume, String p_audio_track, String p_subtitle_track) { + // FIXME: Add support for volume, audio and subtitle tracks if (video_play_func) video_play_func(p_path); return OK; } -bool OS_Android::native_video_is_playing() { +bool OS_Android::native_video_is_playing() const { if (video_is_playing_func) return video_is_playing_func(); return false; @@ -696,7 +745,7 @@ bool OS_Android::_check_internal_feature_support(const String &p_feature) { return false; } -OS_Android::OS_Android(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetUserDataDirFunc p_get_user_data_dir_func, GetLocaleFunc p_get_locale_func, GetModelFunc p_get_model_func, GetScreenDPIFunc p_get_screen_dpi_func, ShowVirtualKeyboardFunc p_show_vk, HideVirtualKeyboardFunc p_hide_vk, VirtualKeyboardHeightFunc p_vk_height_func, SetScreenOrientationFunc p_screen_orient, GetUniqueIDFunc p_get_unique_id, GetSystemDirFunc p_get_sdir_func, GetGLVersionCodeFunc p_get_gl_version_func, VideoPlayFunc p_video_play_func, VideoIsPlayingFunc p_video_is_playing_func, VideoPauseFunc p_video_pause_func, VideoStopFunc p_video_stop_func, SetKeepScreenOnFunc p_set_keep_screen_on_func, AlertFunc p_alert_func, bool p_use_apk_expansion) { +OS_Android::OS_Android(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetUserDataDirFunc p_get_user_data_dir_func, GetLocaleFunc p_get_locale_func, GetModelFunc p_get_model_func, GetScreenDPIFunc p_get_screen_dpi_func, ShowVirtualKeyboardFunc p_show_vk, HideVirtualKeyboardFunc p_hide_vk, VirtualKeyboardHeightFunc p_vk_height_func, SetScreenOrientationFunc p_screen_orient, GetUniqueIDFunc p_get_unique_id, GetSystemDirFunc p_get_sdir_func, GetGLVersionCodeFunc p_get_gl_version_func, VideoPlayFunc p_video_play_func, VideoIsPlayingFunc p_video_is_playing_func, VideoPauseFunc p_video_pause_func, VideoStopFunc p_video_stop_func, SetKeepScreenOnFunc p_set_keep_screen_on_func, AlertFunc p_alert_func, SetClipboardFunc p_set_clipboard_func, GetClipboardFunc p_get_clipboard_func, bool p_use_apk_expansion) { use_apk_expansion = p_use_apk_expansion; default_videomode.width = 800; @@ -729,6 +778,9 @@ OS_Android::OS_Android(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, OpenURI hide_virtual_keyboard_func = p_hide_vk; get_virtual_keyboard_height_func = p_vk_height_func; + set_clipboard_func = p_set_clipboard_func; + get_clipboard_func = p_get_clipboard_func; + set_screen_orientation_func = p_screen_orient; set_keep_screen_on_func = p_set_keep_screen_on_func; alert_func = p_alert_func; diff --git a/platform/android/os_android.h b/platform/android/os_android.h index c4220906a3..e89a380e4c 100644 --- a/platform/android/os_android.h +++ b/platform/android/os_android.h @@ -33,10 +33,10 @@ #include "audio_driver_jandroid.h" #include "audio_driver_opensl.h" +#include "core/os/input.h" +#include "core/os/main_loop.h" #include "drivers/unix/os_unix.h" #include "main/input_default.h" -#include "os/input.h" -#include "os/main_loop.h" //#include "power_android.h" #include "servers/audio_server.h" #include "servers/visual/rasterizer.h" @@ -51,6 +51,8 @@ typedef void (*GFXInitFunc)(void *ud, bool gl2); typedef int (*OpenURIFunc)(const String &); typedef String (*GetUserDataDirFunc)(); typedef String (*GetLocaleFunc)(); +typedef void (*SetClipboardFunc)(const String &); +typedef String (*GetClipboardFunc)(); typedef String (*GetModelFunc)(); typedef int (*GetScreenDPIFunc)(); typedef String (*GetUniqueIDFunc)(); @@ -119,6 +121,8 @@ private: OpenURIFunc open_uri_func; GetUserDataDirFunc get_user_data_dir_func; GetLocaleFunc get_locale_func; + SetClipboardFunc set_clipboard_func; + GetClipboardFunc get_clipboard_func; GetModelFunc get_model_func; GetScreenDPIFunc get_screen_dpi_func; ShowVirtualKeyboardFunc show_virtual_keyboard_func; @@ -140,7 +144,7 @@ private: int video_driver_index; public: - // functions used by main to initialize/deintialize the OS + // functions used by main to initialize/deinitialize the OS virtual int get_video_driver_count() const; virtual const char *get_video_driver_name(int p_driver) const; @@ -172,9 +176,6 @@ public: virtual int get_mouse_button_state() const; virtual void set_window_title(const String &p_title); - //virtual void set_clipboard(const String& p_text); - //virtual String get_clipboard() const; - virtual void set_video_mode(const VideoMode &p_video_mode, int p_screen = 0); virtual VideoMode get_video_mode(int p_screen = 0) const; virtual void get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen = 0) const; @@ -218,6 +219,8 @@ public: virtual String get_user_data_dir() const; virtual String get_resource_dir() const; virtual String get_locale() const; + virtual void set_clipboard(const String &p_text); + virtual String get_clipboard() const; virtual String get_model_name() const; virtual int get_screen_dpi(int p_screen = 0) const; @@ -234,8 +237,8 @@ public: void process_event(Ref<InputEvent> p_event); void init_video_mode(int p_video_width, int p_video_height); - virtual Error native_video_play(String p_path, float p_volume); - virtual bool native_video_is_playing(); + virtual Error native_video_play(String p_path, float p_volume, String p_audio_track, String p_subtitle_track); + virtual bool native_video_is_playing() const; virtual void native_video_pause(); virtual void native_video_stop(); @@ -244,7 +247,7 @@ public: void joy_connection_changed(int p_device, bool p_connected, String p_name); virtual bool _check_internal_feature_support(const String &p_feature); - OS_Android(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetUserDataDirFunc p_get_user_data_dir_func, GetLocaleFunc p_get_locale_func, GetModelFunc p_get_model_func, GetScreenDPIFunc p_get_screen_dpi_func, ShowVirtualKeyboardFunc p_show_vk, HideVirtualKeyboardFunc p_hide_vk, VirtualKeyboardHeightFunc p_vk_height_func, SetScreenOrientationFunc p_screen_orient, GetUniqueIDFunc p_get_unique_id, GetSystemDirFunc p_get_sdir_func, GetGLVersionCodeFunc p_get_gl_version_func, VideoPlayFunc p_video_play_func, VideoIsPlayingFunc p_video_is_playing_func, VideoPauseFunc p_video_pause_func, VideoStopFunc p_video_stop_func, SetKeepScreenOnFunc p_set_keep_screen_on_func, AlertFunc p_alert_func, bool p_use_apk_expansion); + OS_Android(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetUserDataDirFunc p_get_user_data_dir_func, GetLocaleFunc p_get_locale_func, GetModelFunc p_get_model_func, GetScreenDPIFunc p_get_screen_dpi_func, ShowVirtualKeyboardFunc p_show_vk, HideVirtualKeyboardFunc p_hide_vk, VirtualKeyboardHeightFunc p_vk_height_func, SetScreenOrientationFunc p_screen_orient, GetUniqueIDFunc p_get_unique_id, GetSystemDirFunc p_get_sdir_func, GetGLVersionCodeFunc p_get_gl_version_func, VideoPlayFunc p_video_play_func, VideoIsPlayingFunc p_video_is_playing_func, VideoPauseFunc p_video_pause_func, VideoStopFunc p_video_stop_func, SetKeepScreenOnFunc p_set_keep_screen_on_func, AlertFunc p_alert_func, SetClipboardFunc p_set_clipboard, GetClipboardFunc p_get_clipboard, bool p_use_apk_expansion); ~OS_Android(); }; diff --git a/platform/android/power_android.h b/platform/android/power_android.h index f0d1bee1e2..c39764222e 100644 --- a/platform/android/power_android.h +++ b/platform/android/power_android.h @@ -31,7 +31,7 @@ #ifndef PLATFORM_ANDROID_POWER_ANDROID_H_ #define PLATFORM_ANDROID_POWER_ANDROID_H_ -#include "os/os.h" +#include "core/os/os.h" #include <android/native_window_jni.h> class power_android { diff --git a/platform/android/thread_jandroid.cpp b/platform/android/thread_jandroid.cpp index b13baf69c2..6795315e63 100644 --- a/platform/android/thread_jandroid.cpp +++ b/platform/android/thread_jandroid.cpp @@ -30,9 +30,9 @@ #include "thread_jandroid.h" +#include "core/os/memory.h" #include "core/safe_refcount.h" -#include "os/memory.h" -#include "script_language.h" +#include "core/script_language.h" static pthread_key_t _create_thread_id_key() { pthread_key_t key; diff --git a/platform/android/thread_jandroid.h b/platform/android/thread_jandroid.h index 2bb64f3db2..a57bc47e6d 100644 --- a/platform/android/thread_jandroid.h +++ b/platform/android/thread_jandroid.h @@ -35,7 +35,7 @@ @author Juan Linietsky <reduzio@gmail.com> */ -#include "os/thread.h" +#include "core/os/thread.h" #include <jni.h> #include <pthread.h> #include <sys/types.h> diff --git a/platform/haiku/audio_driver_media_kit.cpp b/platform/haiku/audio_driver_media_kit.cpp index aeaf698015..7e68c01fad 100644 --- a/platform/haiku/audio_driver_media_kit.cpp +++ b/platform/haiku/audio_driver_media_kit.cpp @@ -32,7 +32,7 @@ #ifdef MEDIA_KIT_ENABLED -#include "project_settings.h" +#include "core/project_settings.h" int32_t *AudioDriverMediaKit::samples_in = NULL; diff --git a/platform/haiku/context_gl_haiku.h b/platform/haiku/context_gl_haiku.h index 74f09984f2..2c20570a8d 100644 --- a/platform/haiku/context_gl_haiku.h +++ b/platform/haiku/context_gl_haiku.h @@ -46,9 +46,6 @@ private: bool use_vsync; public: - ContextGL_Haiku(HaikuDirectWindow *p_window); - ~ContextGL_Haiku(); - virtual Error initialize(); virtual void release_current(); virtual void make_current(); @@ -58,6 +55,9 @@ public: virtual void set_use_vsync(bool p_use); virtual bool is_using_vsync() const; + + ContextGL_Haiku(HaikuDirectWindow *p_window); + virtual ~ContextGL_Haiku(); }; #endif diff --git a/platform/haiku/detect.py b/platform/haiku/detect.py index 7ecdd2bb11..8d704ac657 100644 --- a/platform/haiku/detect.py +++ b/platform/haiku/detect.py @@ -148,7 +148,7 @@ def configure(env): ## Flags env.Append(CPPPATH=['#platform/haiku']) - env.Append(CPPFLAGS=['-DUNIX_ENABLED', '-DOPENGL_ENABLED', '-DGLES_ENABLED', '-DGLES_OVER_GL']) + env.Append(CPPFLAGS=['-DUNIX_ENABLED', '-DOPENGL_ENABLED', '-DGLES_ENABLED']) env.Append(CPPFLAGS=['-DMEDIA_KIT_ENABLED']) # env.Append(CCFLAGS=['-DFREETYPE_ENABLED']) env.Append(CPPFLAGS=['-DPTHREAD_NO_RENAME']) # TODO: enable when we have pthread_setname_np diff --git a/platform/haiku/haiku_direct_window.cpp b/platform/haiku/haiku_direct_window.cpp index 7eeb226167..6b64082250 100644 --- a/platform/haiku/haiku_direct_window.cpp +++ b/platform/haiku/haiku_direct_window.cpp @@ -30,10 +30,10 @@ #include <UnicodeChar.h> +#include "core/os/keyboard.h" #include "haiku_direct_window.h" #include "key_mapping_haiku.h" #include "main/main.h" -#include "os/keyboard.h" HaikuDirectWindow::HaikuDirectWindow(BRect p_frame) : BDirectWindow(p_frame, "Godot", B_TITLED_WINDOW, B_QUIT_ON_WINDOW_CLOSE) { @@ -86,7 +86,7 @@ void HaikuDirectWindow::DirectConnected(direct_buffer_info *info) { void HaikuDirectWindow::MessageReceived(BMessage *message) { switch (message->what) { case REDRAW_MSG: - if (Main::iteration() == true) { + if (Main::iteration()) { view->EnableDirectMode(false); Quit(); } diff --git a/platform/haiku/key_mapping_haiku.cpp b/platform/haiku/key_mapping_haiku.cpp index 28a282e25c..ebe8117d5d 100644 --- a/platform/haiku/key_mapping_haiku.cpp +++ b/platform/haiku/key_mapping_haiku.cpp @@ -30,8 +30,8 @@ #include <InterfaceDefs.h> +#include "core/os/keyboard.h" #include "key_mapping_haiku.h" -#include "os/keyboard.h" struct _HaikuTranslatePair { unsigned int keysym; diff --git a/platform/haiku/os_haiku.cpp b/platform/haiku/os_haiku.cpp index c80365f1f3..f9f12af817 100644 --- a/platform/haiku/os_haiku.cpp +++ b/platform/haiku/os_haiku.cpp @@ -117,16 +117,13 @@ Error OS_Haiku::initialize(const VideoMode &p_desired, int p_video_driver, int p #endif - visual_server = memnew(VisualServerRaster()); - - ERR_FAIL_COND_V(!visual_server, ERR_UNAVAILABLE); - - // TODO: enable multithreaded VS - /* + visual_server = memnew(VisualServerRaster); + // FIXME: Reimplement threaded rendering if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) { - visual_server = memnew(VisualServerWrapMT(visual_server, get_render_thread_mode() == RENDER_SEPARATE_THREAD)); + visual_server = memnew(VisualServerWrapMT(visual_server, false)); } - */ + + ERR_FAIL_COND_V(!visual_server, ERR_UNAVAILABLE); video_driver_index = p_video_driver; diff --git a/platform/haiku/platform_config.h b/platform/haiku/platform_config.h index 72c8ee2535..b00510f5a1 100644 --- a/platform/haiku/platform_config.h +++ b/platform/haiku/platform_config.h @@ -33,5 +33,5 @@ // for ifaddrs.h needed in drivers/unix/ip_unix.cpp #define _BSD_SOURCE 1 -#define GLES3_INCLUDE_H "glad/glad.h" -#define GLES2_INCLUDE_H "glad/glad.h" +#define GLES3_INCLUDE_H "thirdparty/glad/glad/glad.h" +#define GLES2_INCLUDE_H "thirdparty/glad/glad/glad.h" diff --git a/platform/iphone/SCsub b/platform/iphone/SCsub index b96bec16b4..debf051eda 100644 --- a/platform/iphone/SCsub +++ b/platform/iphone/SCsub @@ -1,8 +1,9 @@ #!/usr/bin/env python -import os Import('env') +import os + iphone_lib = [ 'godot_iphone.cpp', 'os_iphone.cpp', diff --git a/platform/iphone/detect.py b/platform/iphone/detect.py index b13a1e9643..417571f6f3 100644 --- a/platform/iphone/detect.py +++ b/platform/iphone/detect.py @@ -87,7 +87,7 @@ def configure(env): s_compiler_path = '$IPHONEPATH/Developer/usr/bin/' ccache_path = os.environ.get("CCACHE") - if ccache_path == None: + if ccache_path is None: env['CC'] = compiler_path + 'clang' env['CXX'] = compiler_path + 'clang++' env['S_compiler'] = s_compiler_path + 'gcc' diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp index 008e213e5e..1fc497456c 100644 --- a/platform/iphone/export/export.cpp +++ b/platform/iphone/export/export.cpp @@ -29,18 +29,18 @@ /*************************************************************************/ #include "export.h" +#include "core/io/marshalls.h" +#include "core/io/resource_saver.h" +#include "core/io/zip_io.h" +#include "core/os/file_access.h" +#include "core/os/os.h" +#include "core/project_settings.h" +#include "core/version.h" #include "editor/editor_export.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" -#include "io/marshalls.h" -#include "io/resource_saver.h" -#include "io/zip_io.h" -#include "os/file_access.h" -#include "os/os.h" #include "platform/iphone/logo.gen.h" -#include "project_settings.h" #include "string.h" -#include "version.h" #include <sys/stat.h> @@ -108,7 +108,11 @@ public: virtual String get_os_name() const { return "iOS"; } virtual Ref<Texture> get_logo() const { return logo; } - virtual String get_binary_extension(const Ref<EditorExportPreset> &p_preset) const { return "ipa"; } + virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const { + List<String> list; + list.push_back("ipa"); + return list; + } virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0); virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const; @@ -203,7 +207,7 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options) r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/spotlight_40x40", PROPERTY_HINT_FILE, "*.png"), "")); // Spotlight r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/spotlight_80x80", PROPERTY_HINT_FILE, "*.png"), "")); // Spotlight on devices with retina display - for (int i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) { + for (unsigned int i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) { r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, loading_screen_infos[i].preset_key, PROPERTY_HINT_FILE, "*.png"), "")); } @@ -353,7 +357,7 @@ Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_pr DirAccess *da = DirAccess::open(p_iconset_dir); ERR_FAIL_COND_V(!da, ERR_CANT_OPEN); - for (int i = 0; i < (sizeof(icon_infos) / sizeof(icon_infos[0])); ++i) { + for (unsigned int i = 0; i < (sizeof(icon_infos) / sizeof(icon_infos[0])); ++i) { IconInfo info = icon_infos[i]; String icon_path = p_preset->get(info.preset_key); if (icon_path.length() == 0) { @@ -403,7 +407,7 @@ Error EditorExportPlatformIOS::_export_loading_screens(const Ref<EditorExportPre DirAccess *da = DirAccess::open(p_dest_dir); ERR_FAIL_COND_V(!da, ERR_CANT_OPEN); - for (int i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) { + for (unsigned int i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) { LoadingScreenInfo info = loading_screen_infos[i]; String loading_screen_file = p_preset->get(info.preset_key); if (loading_screen_file.size() > 0) { @@ -490,7 +494,7 @@ private: static String _hex_pad(uint32_t num) { Vector<char> ret; ret.resize(sizeof(num) * 2); - for (int i = 0; i < sizeof(num) * 2; ++i) { + for (unsigned int i = 0; i < sizeof(num) * 2; ++i) { uint8_t four_bits = (num >> (sizeof(num) * 8 - (i + 1) * 4)) & 0xF; ret.write[i] = _hex_char(four_bits); } diff --git a/platform/iphone/gl_view.mm b/platform/iphone/gl_view.mm index 478a3125af..2925b46007 100644 --- a/platform/iphone/gl_view.mm +++ b/platform/iphone/gl_view.mm @@ -53,7 +53,6 @@ static GLView *_instance = NULL; static bool video_found_error = false; static bool video_playing = false; -static float video_previous_volume = 0.0f; static CMTime video_current_time; void _show_keyboard(String); @@ -248,16 +247,6 @@ static int remove_touch(UITouch *p_touch) { return remaining; }; -static int get_first_id(UITouch *p_touch) { - - for (int i = 0; i < max_touches; i++) { - - if (touches[i] != NULL) - return i; - }; - return -1; -}; - static void clear_touches() { for (int i = 0; i < max_touches; i++) { @@ -751,7 +740,6 @@ static void clear_touches() { [_instance.moviePlayerController stop]; [_instance.moviePlayerController.view removeFromSuperview]; - //[[MPMusicPlayerController applicationMusicPlayer] setVolume: video_previous_volume]; video_playing = false; } */ diff --git a/platform/iphone/globals/global_defaults.cpp b/platform/iphone/globals/global_defaults.cpp index ccc90665c5..423f50995e 100644 --- a/platform/iphone/globals/global_defaults.cpp +++ b/platform/iphone/globals/global_defaults.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "global_defaults.h" -#include "project_settings.h" +#include "core/project_settings.h" void register_iphone_global_defaults() { } diff --git a/platform/iphone/godot_iphone.cpp b/platform/iphone/godot_iphone.cpp index dacbf42087..f9b9654a8c 100644 --- a/platform/iphone/godot_iphone.cpp +++ b/platform/iphone/godot_iphone.cpp @@ -28,9 +28,9 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#include "core/ustring.h" #include "main/main.h" #include "os_iphone.h" -#include "ustring.h" #include <stdio.h> #include <string.h> diff --git a/platform/iphone/os_iphone.cpp b/platform/iphone/os_iphone.cpp index a4538a6673..e996a5905b 100644 --- a/platform/iphone/os_iphone.cpp +++ b/platform/iphone/os_iphone.cpp @@ -34,7 +34,7 @@ #include "drivers/gles3/rasterizer_gles3.h" #include "servers/visual/visual_server_raster.h" -//#include "servers/visual/visual_server_wrap_mt.h" +#include "servers/visual/visual_server_wrap_mt.h" #include "main/main.h" @@ -99,18 +99,19 @@ int OSIPhone::get_current_video_driver() const { Error OSIPhone::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { - video_driver_index = p_video_driver; //this may be misleading + video_driver_index = VIDEO_DRIVER_GLES3; + if (RasterizerGLES3::is_viable() != OK) { + return ERR_UNAVAILABLE; + } RasterizerGLES3::register_config(); RasterizerGLES3::make_current(); - visual_server = memnew(VisualServerRaster()); - /* - FIXME: Reimplement threaded rendering? Or remove? + visual_server = memnew(VisualServerRaster); + // FIXME: Reimplement threaded rendering if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) { visual_server = memnew(VisualServerWrapMT(visual_server, false)); - }; - */ + } visual_server->init(); //visual_server->cursor_set_visible(false, 0); diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h index db2912ad93..64a3c6355a 100644 --- a/platform/iphone/os_iphone.h +++ b/platform/iphone/os_iphone.h @@ -33,9 +33,9 @@ #ifndef OS_IPHONE_H #define OS_IPHONE_H +#include "core/os/input.h" #include "drivers/coreaudio/audio_driver_coreaudio.h" #include "drivers/unix/os_unix.h" -#include "os/input.h" #include "game_center.h" #include "icloud.h" diff --git a/platform/iphone/platform_refcount.h b/platform/iphone/platform_refcount.h index 94e4e5fa3b..34338d92e7 100644 --- a/platform/iphone/platform_refcount.h +++ b/platform/iphone/platform_refcount.h @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "safe_refcount.h" +#include "core/safe_refcount.h" #ifdef IPHONE_ENABLED diff --git a/platform/iphone/sem_iphone.cpp b/platform/iphone/sem_iphone.cpp index ec1337d63f..ebab9db8fa 100644 --- a/platform/iphone/sem_iphone.cpp +++ b/platform/iphone/sem_iphone.cpp @@ -70,7 +70,7 @@ void cgsem_destroy(cgsem_t *cgsem) { close(cgsem->pipefd[0]); } -#include "os/memory.h" +#include "core/os/memory.h" #include <errno.h> Error SemaphoreIphone::wait() { diff --git a/platform/iphone/sem_iphone.h b/platform/iphone/sem_iphone.h index ebd4e4ee43..3edc4492eb 100644 --- a/platform/iphone/sem_iphone.h +++ b/platform/iphone/sem_iphone.h @@ -37,7 +37,7 @@ struct cgsem { typedef struct cgsem cgsem_t; -#include "os/semaphore.h" +#include "core/os/semaphore.h" class SemaphoreIphone : public Semaphore { diff --git a/platform/javascript/api/api.cpp b/platform/javascript/api/api.cpp index b377ca4e52..c7a6d53561 100644 --- a/platform/javascript/api/api.cpp +++ b/platform/javascript/api/api.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "api.h" -#include "engine.h" +#include "core/engine.h" #include "javascript_eval.h" static JavaScript *javascript_eval; diff --git a/platform/javascript/api/javascript_eval.h b/platform/javascript/api/javascript_eval.h index 05f7c9f38a..49d5309737 100644 --- a/platform/javascript/api/javascript_eval.h +++ b/platform/javascript/api/javascript_eval.h @@ -31,7 +31,7 @@ #ifndef JAVASCRIPT_EVAL_H #define JAVASCRIPT_EVAL_H -#include "object.h" +#include "core/object.h" class JavaScript : public Object { private: diff --git a/platform/javascript/detect.py b/platform/javascript/detect.py index fc909f6619..cf85c3df7f 100644 --- a/platform/javascript/detect.py +++ b/platform/javascript/detect.py @@ -25,7 +25,6 @@ def get_opts(): def get_flags(): return [ ('tools', False), - ('module_theora_enabled', False), # Disabling the mbedtls module reduces file size. # The module has little use due to the limited networking functionality # in this platform. For the available networking methods, the browser @@ -129,6 +128,10 @@ def configure(env): # us since we don't know requirements at compile-time. env.Append(LINKFLAGS=['-s', 'ALLOW_MEMORY_GROWTH=1']) + # Since we use both memory growth and MEMFS preloading, + # this avoids unecessary copying on start-up. + env.Append(LINKFLAGS=['--no-heap-copy']) + # This setting just makes WebGL 2 APIs available, it does NOT disable WebGL 1. env.Append(LINKFLAGS=['-s', 'USE_WEBGL2=1']) diff --git a/platform/javascript/dom_keys.inc b/platform/javascript/dom_keys.inc index dc8d67d52b..a30818decc 100644 --- a/platform/javascript/dom_keys.inc +++ b/platform/javascript/dom_keys.inc @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "os/keyboard.h" +#include "core/os/keyboard.h" // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode#Constants_for_keyCode_value #define DOM_VK_CANCEL 0x03 diff --git a/platform/javascript/engine.js b/platform/javascript/engine.js index c3ef5bbbb5..91458eb4c3 100644 --- a/platform/javascript/engine.js +++ b/platform/javascript/engine.js @@ -1,3 +1,6 @@ + // The following is concatenated with generated code, and acts as the end + // of a wrapper for said code. See pre.js for the other part of the + // wrapper. exposedLibs['PATH'] = PATH; exposedLibs['FS'] = FS; return Module; diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp index a7f0084562..7a325e81dd 100644 --- a/platform/javascript/export/export.cpp +++ b/platform/javascript/export/export.cpp @@ -28,9 +28,9 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#include "core/io/zip_io.h" #include "editor/editor_node.h" #include "editor_export.h" -#include "io/zip_io.h" #include "main/splash.gen.h" #include "platform/javascript/logo.gen.h" #include "platform/javascript/run_icon.gen.h" @@ -58,7 +58,7 @@ public: virtual Ref<Texture> get_logo() const; virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const; - virtual String get_binary_extension(const Ref<EditorExportPreset> &p_preset) const; + virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const; virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0); virtual bool poll_devices(); @@ -174,9 +174,11 @@ bool EditorExportPlatformJavaScript::can_export(const Ref<EditorExportPreset> &p return valid; } -String EditorExportPlatformJavaScript::get_binary_extension(const Ref<EditorExportPreset> &p_preset) const { +List<String> EditorExportPlatformJavaScript::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const { - return "html"; + List<String> list; + list.push_back("html"); + return list; } Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { diff --git a/platform/javascript/http_client_javascript.cpp b/platform/javascript/http_client_javascript.cpp index 8d90e01ae1..ccf4f8a11b 100644 --- a/platform/javascript/http_client_javascript.cpp +++ b/platform/javascript/http_client_javascript.cpp @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#include "core/io/http_client.h" #include "http_request.h" -#include "io/http_client.h" Error HTTPClient::connect_to_host(const String &p_host, int p_port, bool p_ssl, bool p_verify_host) { @@ -237,7 +237,7 @@ Error HTTPClient::poll() { case STATUS_CONNECTION_ERROR: return ERR_CONNECTION_ERROR; - case STATUS_REQUESTING: + case STATUS_REQUESTING: { #ifdef DEBUG_ENABLED if (!has_polled) { @@ -281,6 +281,10 @@ Error HTTPClient::poll() { godot_xhr_get_response(xhr_id, write.ptr(), polled_response.size()); write = PoolByteArray::Write(); break; + } + + default: + ERR_FAIL_V(ERR_BUG); } return OK; } diff --git a/platform/javascript/javascript_eval.cpp b/platform/javascript/javascript_eval.cpp index 07b4c192e6..9b8174cc71 100644 --- a/platform/javascript/javascript_eval.cpp +++ b/platform/javascript/javascript_eval.cpp @@ -140,8 +140,9 @@ Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) { case Variant::POOL_BYTE_ARRAY: arr_write = PoolByteArray::Write(); return arr; + default: + return Variant(); } - return Variant(); } #endif // JAVASCRIPT_EVAL_ENABLED diff --git a/platform/javascript/javascript_main.cpp b/platform/javascript/javascript_main.cpp index 3829e8d406..ec60571402 100644 --- a/platform/javascript/javascript_main.cpp +++ b/platform/javascript/javascript_main.cpp @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "io/resource_loader.h" +#include "core/io/resource_loader.h" #include "main/main.h" #include "os_javascript.h" diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp index 5a8a05d4df..9250ca4903 100644 --- a/platform/javascript/os_javascript.cpp +++ b/platform/javascript/os_javascript.cpp @@ -30,15 +30,16 @@ #include "os_javascript.h" -#include "gles2/rasterizer_gles2.h" -#include "gles3/rasterizer_gles3.h" -#include "io/file_access_buffered_fa.h" +#include "core/io/file_access_buffered_fa.h" +#include "drivers/gles2/rasterizer_gles2.h" +#include "drivers/gles3/rasterizer_gles3.h" +#include "drivers/unix/dir_access_unix.h" +#include "drivers/unix/file_access_unix.h" #include "main/main.h" #include "servers/visual/visual_server_raster.h" -#include "unix/dir_access_unix.h" -#include "unix/file_access_unix.h" #include <emscripten.h> +#include <png.h> #include <stdlib.h> #include "dom_keys.inc" @@ -120,14 +121,14 @@ void OS_JavaScript::set_window_size(const Size2 p_size) { emscripten_exit_soft_fullscreen(); window_maximized = false; } - emscripten_set_canvas_size(p_size.x, p_size.y); + emscripten_set_canvas_element_size(NULL, p_size.x, p_size.y); } } Size2 OS_JavaScript::get_window_size() const { - int canvas[3]; - emscripten_get_canvas_size(canvas, canvas + 1, canvas + 2); + int canvas[2]; + emscripten_get_canvas_element_size(NULL, canvas, canvas + 1); return Size2(canvas[0], canvas[1]); } @@ -377,15 +378,13 @@ static void set_css_cursor(const char *p_cursor) { /* clang-format on */ } -static const char *get_css_cursor() { +static bool is_css_cursor_hidden() { - char cursor[16]; /* clang-format off */ - EM_ASM_INT({ - stringToUTF8(Module.canvas.style.cursor ? Module.canvas.style.cursor : 'auto', $0, 16); - }, cursor); + return EM_ASM_INT({ + return Module.canvas.style.cursor === 'none'; + }); /* clang-format on */ - return cursor; } void OS_JavaScript::set_cursor_shape(CursorShape p_shape) { @@ -429,7 +428,7 @@ void OS_JavaScript::set_mouse_mode(OS::MouseMode p_mode) { OS::MouseMode OS_JavaScript::get_mouse_mode() const { - if (String::utf8(get_css_cursor()) == "none") + if (is_css_cursor_hidden()) return MOUSE_MODE_HIDDEN; EmscriptenPointerlockChangeEvent ev; @@ -565,8 +564,11 @@ void OS_JavaScript::process_joypads() { int joypad_count = emscripten_get_num_gamepads(); for (int joypad = 0; joypad < joypad_count; joypad++) { EmscriptenGamepadEvent state; - emscripten_get_gamepad_status(joypad, &state); - if (state.connected) { + EMSCRIPTEN_RESULT query_result = emscripten_get_gamepad_status(joypad, &state); + // Chromium reserves gamepads slots, so NO_DATA is an expected result. + ERR_CONTINUE(query_result != EMSCRIPTEN_RESULT_SUCCESS && + query_result != EMSCRIPTEN_RESULT_NO_DATA); + if (query_result == EMSCRIPTEN_RESULT_SUCCESS && state.connected) { int button_count = MIN(state.numButtons, 18); int axis_count = MIN(state.numAxes, 8); @@ -652,26 +654,60 @@ Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, attributes.alpha = false; attributes.antialias = false; ERR_FAIL_INDEX_V(p_video_driver, VIDEO_DRIVER_MAX, ERR_INVALID_PARAMETER); - switch (p_video_driver) { - case VIDEO_DRIVER_GLES3: - attributes.majorVersion = 2; - RasterizerGLES3::register_config(); - RasterizerGLES3::make_current(); - break; - case VIDEO_DRIVER_GLES2: - attributes.majorVersion = 1; - RasterizerGLES2::register_config(); - RasterizerGLES2::make_current(); - break; + + bool gles3 = true; + if (p_video_driver == VIDEO_DRIVER_GLES2) { + gles3 = false; + } + + bool gl_initialization_error = false; + + while (true) { + if (gles3) { + if (RasterizerGLES3::is_viable() == OK) { + attributes.majorVersion = 2; + RasterizerGLES3::register_config(); + RasterizerGLES3::make_current(); + break; + } else { + if (GLOBAL_GET("rendering/quality/driver/driver_fallback") == "Best") { + p_video_driver = VIDEO_DRIVER_GLES2; + gles3 = false; + continue; + } else { + gl_initialization_error = true; + break; + } + } + } else { + if (RasterizerGLES2::is_viable() == OK) { + attributes.majorVersion = 1; + RasterizerGLES2::register_config(); + RasterizerGLES2::make_current(); + break; + } else { + gl_initialization_error = true; + break; + } + } } - video_driver_index = p_video_driver; EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(NULL, &attributes); - ERR_EXPLAIN("WebGL " + itos(attributes.majorVersion) + ".0 not available"); - ERR_FAIL_COND_V(emscripten_webgl_make_context_current(ctx) != EMSCRIPTEN_RESULT_SUCCESS, ERR_UNAVAILABLE); + if (emscripten_webgl_make_context_current(ctx) != EMSCRIPTEN_RESULT_SUCCESS) { + gl_initialization_error = true; + } + + if (gl_initialization_error) { + OS::get_singleton()->alert("Your browser does not support any of the supported WebGL versions.\n" + "Please update your browser version.", + "Unable to initialize Video driver"); + return ERR_UNAVAILABLE; + } + + video_driver_index = p_video_driver; video_mode = p_desired; - // Can't fulfil fullscreen request during start-up due to browser security. + // Can't fulfill fullscreen request during start-up due to browser security. video_mode.fullscreen = false; /* clang-format off */ if (EM_ASM_INT_V({ return Module.resizeCanvasOnStart })) { @@ -797,13 +833,13 @@ bool OS_JavaScript::main_loop_iterate() { strategy.canvasResizedCallback = NULL; emscripten_enter_soft_fullscreen(NULL, &strategy); } else { - emscripten_set_canvas_size(windowed_size.width, windowed_size.height); + emscripten_set_canvas_element_size(NULL, windowed_size.width, windowed_size.height); } just_exited_fullscreen = false; } - int canvas[3]; - emscripten_get_canvas_size(canvas, canvas + 1, canvas + 2); + int canvas[2]; + emscripten_get_canvas_element_size(NULL, canvas, canvas + 1); video_mode.width = canvas[0]; video_mode.height = canvas[1]; if (!window_maximized && !video_mode.fullscreen && !just_exited_fullscreen && !entering_fullscreen) { @@ -826,6 +862,24 @@ void OS_JavaScript::finalize() { // Miscellaneous +Error OS_JavaScript::execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode, bool read_stderr) { + + ERR_EXPLAIN("OS::execute() is not available on the HTML5 platform"); + ERR_FAIL_V(ERR_UNAVAILABLE); +} + +Error OS_JavaScript::kill(const ProcessID &p_pid) { + + ERR_EXPLAIN("OS::kill() is not available on the HTML5 platform"); + ERR_FAIL_V(ERR_UNAVAILABLE); +} + +int OS_JavaScript::get_process_id() const { + + ERR_EXPLAIN("OS::get_process_id() is not available on the HTML5 platform"); + ERR_FAIL_V(0); +} + extern "C" EMSCRIPTEN_KEEPALIVE void send_notification(int p_notification) { if (p_notification == MainLoop::NOTIFICATION_WM_MOUSE_ENTER || p_notification == MainLoop::NOTIFICATION_WM_MOUSE_EXIT) { @@ -875,6 +929,57 @@ void OS_JavaScript::set_window_title(const String &p_title) { /* clang-format on */ } +void OS_JavaScript::set_icon(const Ref<Image> &p_icon) { + + ERR_FAIL_COND(p_icon.is_null()); + Ref<Image> icon = p_icon; + if (icon->is_compressed()) { + icon = icon->duplicate(); + ERR_FAIL_COND(icon->decompress() != OK) + } + if (icon->get_format() != Image::FORMAT_RGBA8) { + if (icon == p_icon) + icon = icon->duplicate(); + icon->convert(Image::FORMAT_RGBA8); + } + + png_image png_meta; + memset(&png_meta, 0, sizeof png_meta); + png_meta.version = PNG_IMAGE_VERSION; + png_meta.width = icon->get_width(); + png_meta.height = icon->get_height(); + png_meta.format = PNG_FORMAT_RGBA; + + PoolByteArray png; + size_t len; + PoolByteArray::Read r = icon->get_data().read(); + ERR_FAIL_COND(!png_image_write_get_memory_size(png_meta, len, 0, r.ptr(), 0, NULL)); + + png.resize(len); + PoolByteArray::Write w = png.write(); + ERR_FAIL_COND(!png_image_write_to_memory(&png_meta, w.ptr(), &len, 0, r.ptr(), 0, NULL)); + w = PoolByteArray::Write(); + + r = png.read(); + /* clang-format off */ + EM_ASM_ARGS({ + var PNG_PTR = $0; + var PNG_LEN = $1; + + var png = new Blob([HEAPU8.slice(PNG_PTR, PNG_PTR + PNG_LEN)], { type: "image/png" }); + var url = URL.createObjectURL(png); + var link = document.getElementById('-gd-engine-icon'); + if (link === null) { + link = document.createElement('link'); + link.rel = 'icon'; + link.id = '-gd-engine-icon'; + document.head.appendChild(link); + } + link.href = url; + }, r.ptr(), len); + /* clang-format on */ +} + String OS_JavaScript::get_executable_path() const { return OS::get_executable_path(); diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h index f40fb8fc7e..79dac5940f 100644 --- a/platform/javascript/os_javascript.h +++ b/platform/javascript/os_javascript.h @@ -32,10 +32,10 @@ #define OS_JAVASCRIPT_H #include "audio_driver_javascript.h" +#include "drivers/unix/os_unix.h" #include "main/input_default.h" #include "servers/audio_server.h" #include "servers/visual/rasterizer.h" -#include "unix/os_unix.h" #include <emscripten/html5.h> @@ -133,8 +133,13 @@ public: void run_async(); bool main_loop_iterate(); + virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false); + virtual Error kill(const ProcessID &p_pid); + virtual int get_process_id() const; + virtual void alert(const String &p_alert, const String &p_title = "ALERT!"); virtual void set_window_title(const String &p_title); + virtual void set_icon(const Ref<Image> &p_icon); String get_executable_path() const; virtual Error shell_open(String p_uri); virtual String get_name(); diff --git a/platform/javascript/pre.js b/platform/javascript/pre.js index 02194bc75e..a870e676ea 100644 --- a/platform/javascript/pre.js +++ b/platform/javascript/pre.js @@ -1,2 +1,5 @@ var Engine = { RuntimeEnvironment: function(Module, exposedLibs) { + // The above is concatenated with generated code, and acts as the start of + // a wrapper for said code. See engine.js for the other part of the + // wrapper. diff --git a/platform/osx/SCsub b/platform/osx/SCsub index 5c973c30c2..dc407eee9e 100644 --- a/platform/osx/SCsub +++ b/platform/osx/SCsub @@ -1,8 +1,8 @@ #!/usr/bin/env python -import os Import('env') +import os from platform_methods import run_in_subprocess import platform_osx_builders diff --git a/platform/osx/crash_handler_osx.mm b/platform/osx/crash_handler_osx.mm index 1664c5ce8e..490155bb24 100644 --- a/platform/osx/crash_handler_osx.mm +++ b/platform/osx/crash_handler_osx.mm @@ -28,9 +28,9 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#include "core/project_settings.h" #include "main/main.h" #include "os_osx.h" -#include "project_settings.h" #include <string.h> #include <unistd.h> @@ -68,8 +68,9 @@ static uint64_t load_address() { } static void handle_crash(int sig) { - if (OS::get_singleton() == NULL) - return; + if (OS::get_singleton() == NULL) { + abort(); + } void *bt_buffer[256]; size_t size = backtrace(bt_buffer, 256); @@ -151,6 +152,7 @@ CrashHandler::CrashHandler() { } CrashHandler::~CrashHandler() { + disable(); } void CrashHandler::disable() { diff --git a/platform/osx/detect.py b/platform/osx/detect.py index 8a0883eca3..c5bd64b15c 100644 --- a/platform/osx/detect.py +++ b/platform/osx/detect.py @@ -89,7 +89,7 @@ def configure(env): basecmd = root + "/target/bin/x86_64-apple-" + env["osxcross_sdk"] + "-" ccache_path = os.environ.get("CCACHE") - if ccache_path == None: + if ccache_path is None: env['CC'] = basecmd + "cc" env['CXX'] = basecmd + "c++" else: diff --git a/platform/osx/dir_access_osx.h b/platform/osx/dir_access_osx.h index e01ff2fe4d..a9d6d63a8e 100644 --- a/platform/osx/dir_access_osx.h +++ b/platform/osx/dir_access_osx.h @@ -38,8 +38,8 @@ #include <sys/types.h> #include <unistd.h> +#include "core/os/dir_access.h" #include "drivers/unix/dir_access_unix.h" -#include "os/dir_access.h" /** @author Juan Linietsky <reduzio@gmail.com> diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp index 880705b507..f27c042637 100644 --- a/platform/osx/export/export.cpp +++ b/platform/osx/export/export.cpp @@ -29,18 +29,18 @@ /*************************************************************************/ #include "export.h" +#include "core/io/marshalls.h" +#include "core/io/resource_saver.h" +#include "core/io/zip_io.h" +#include "core/os/file_access.h" +#include "core/os/os.h" +#include "core/project_settings.h" +#include "core/version.h" #include "editor/editor_export.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" -#include "io/marshalls.h" -#include "io/resource_saver.h" -#include "io/zip_io.h" -#include "os/file_access.h" -#include "os/os.h" #include "platform/osx/logo.gen.h" -#include "project_settings.h" #include "string.h" -#include "version.h" #include <sys/stat.h> class EditorExportPlatformOSX : public EditorExportPlatform { @@ -74,7 +74,14 @@ public: virtual String get_os_name() const { return "OSX"; } virtual Ref<Texture> get_logo() const { return logo; } - virtual String get_binary_extension(const Ref<EditorExportPreset> &p_preset) const { return use_dmg() ? "dmg" : "zip"; } + virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const { + List<String> list; + if (use_dmg()) { + list.push_back("dmg"); + } + list.push_back("zip"); + return list; + } virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0); virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const; @@ -334,7 +341,8 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p io2.opaque = &dst_f; zipFile dst_pkg_zip = NULL; - if (use_dmg()) { + String export_format = use_dmg() && p_path.ends_with("dmg") ? "dmg" : "zip"; + if (export_format == "dmg") { // We're on OSX so we can export to DMG, but first we create our application bundle tmp_app_path_name = EditorSettings::get_singleton()->get_cache_dir().plus_file(pkg_name + ".app"); print_line("Exporting to " + tmp_app_path_name); @@ -429,7 +437,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p print_line("ADDING: " + file + " size: " + itos(data.size())); total_size += data.size(); - if (use_dmg()) { + if (export_format == "dmg") { // write it into our application bundle file = tmp_app_path_name + "/" + file; @@ -461,7 +469,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p fi.internal_fa = info.internal_fa; fi.external_fa = info.external_fa; - int zerr = zipOpenNewFileInZip(dst_pkg_zip, + zipOpenNewFileInZip(dst_pkg_zip, file.utf8().get_data(), &fi, NULL, @@ -472,7 +480,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p Z_DEFLATED, Z_DEFAULT_COMPRESSION); - zerr = zipWriteInFileInZip(dst_pkg_zip, data.ptr(), data.size()); + zipWriteInFileInZip(dst_pkg_zip, data.ptr(), data.size()); zipCloseFileInZip(dst_pkg_zip); } } @@ -491,7 +499,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p if (err == OK) { ep.step("Making PKG", 1); - if (use_dmg()) { + if (export_format == "dmg") { String pack_path = tmp_app_path_name + "/Contents/Resources/" + pkg_name + ".pck"; Vector<SharedObject> shared_objects; err = save_pack(p_preset, pack_path, &shared_objects); diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h index 686e3f8c90..546c88e74a 100644 --- a/platform/osx/os_osx.h +++ b/platform/osx/os_osx.h @@ -31,13 +31,13 @@ #ifndef OS_OSX_H #define OS_OSX_H +#include "core/os/input.h" #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" -#include "os/input.h" #include "power_osx.h" #include "servers/audio_server.h" #include "servers/visual/rasterizer.h" @@ -74,8 +74,12 @@ public: IP_Unix *ip_unix; +#ifdef COREAUDIO_ENABLED AudioDriverCoreAudio audio_driver; +#endif +#ifdef COREMIDI_ENABLED MIDIDriverCoreMidi midi_driver; +#endif InputDefault *input; JoypadOSX *joypad_osx; diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index 41cfada723..985811e166 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -30,15 +30,15 @@ #include "os_osx.h" +#include "core/os/keyboard.h" +#include "core/print_string.h" +#include "core/version_generated.gen.h" #include "dir_access_osx.h" #include "drivers/gles2/rasterizer_gles2.h" #include "drivers/gles3/rasterizer_gles3.h" #include "main/main.h" -#include "os/keyboard.h" -#include "print_string.h" #include "sem_osx.h" #include "servers/visual/visual_server_raster.h" -#include "version_generated.gen.h" #include <mach-o/dyld.h> @@ -76,11 +76,6 @@ #define NSWindowStyleMaskBorderless NSBorderlessWindowMask #endif -static NSRect convertRectToBacking(NSRect contentRect) { - - return [OS_OSX::singleton->window_view convertRectToBacking:contentRect]; -} - static void get_key_modifier_state(unsigned int p_osx_state, Ref<InputEventWithModifiers> state) { state->set_shift((p_osx_state & NSEventModifierFlagShift)); @@ -105,12 +100,13 @@ static int prev_mouse_y = 0; static int button_mask = 0; static bool mouse_down_control = false; -static Vector2 get_mouse_pos(NSEvent *event) { +static Vector2 get_mouse_pos(NSPoint locationInWindow, CGFloat backingScaleFactor) { const NSRect contentRect = [OS_OSX::singleton->window_view frame]; - const NSPoint p = [event locationInWindow]; - mouse_x = p.x * OS_OSX::singleton->_mouse_scale([[event window] backingScaleFactor]); - mouse_y = (contentRect.size.height - p.y) * OS_OSX::singleton->_mouse_scale([[event window] backingScaleFactor]); + const NSPoint p = locationInWindow; + const float s = OS_OSX::singleton->_mouse_scale(backingScaleFactor); + mouse_x = p.x * s; + mouse_y = (contentRect.size.height - p.y) * s; return Vector2(mouse_x, mouse_y); } @@ -271,7 +267,7 @@ static Vector2 get_mouse_pos(NSEvent *event) { float newDisplayScale = OS_OSX::singleton->is_hidpi_allowed() ? newBackingScaleFactor : 1.0; const NSRect contentRect = [OS_OSX::singleton->window_view frame]; - const NSRect fbRect = contentRect; //convertRectToBacking(contentRect); + const NSRect fbRect = contentRect; OS_OSX::singleton->window_size.width = fbRect.size.width * newDisplayScale; OS_OSX::singleton->window_size.height = fbRect.size.height * newDisplayScale; @@ -292,7 +288,7 @@ static Vector2 get_mouse_pos(NSEvent *event) { [OS_OSX::singleton->context update]; const NSRect contentRect = [OS_OSX::singleton->window_view frame]; - const NSRect fbRect = contentRect; //convertRectToBacking(contentRect); + const NSRect fbRect = contentRect; float displayScale = OS_OSX::singleton->_display_scale(); OS_OSX::singleton->window_size.width = fbRect.size.width * displayScale; @@ -330,8 +326,16 @@ static Vector2 get_mouse_pos(NSEvent *event) { - (void)windowDidBecomeKey:(NSNotification *)notification { //_GodotInputWindowFocus(window, GL_TRUE); //_GodotPlatformSetCursorMode(window, window->cursorMode); - if (OS_OSX::singleton->get_main_loop()) + [OS_OSX::singleton->context update]; + + if (OS_OSX::singleton->get_main_loop()) { + get_mouse_pos( + [OS_OSX::singleton->window_object mouseLocationOutsideOfEventStream], + [OS_OSX::singleton->window_view backingScaleFactor]); + OS_OSX::singleton->input->set_mouse_position(Point2(mouse_x, mouse_y)); + OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN); + } } - (void)windowDidResignKey:(NSNotification *)notification { @@ -361,6 +365,8 @@ static Vector2 get_mouse_pos(NSEvent *event) { bool imeMode; } - (void)cancelComposition; +- (BOOL)wantsUpdateLayer; +- (void)updateLayer; @end @implementation GodotContentView @@ -371,6 +377,14 @@ static Vector2 get_mouse_pos(NSEvent *event) { } } +- (BOOL)wantsUpdateLayer { + return YES; +} + +- (void)updateLayer { + [OS_OSX::singleton->context update]; +} + - (id)init { self = [super init]; trackingArea = nil; @@ -598,12 +612,13 @@ static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) { mm->set_button_mask(button_mask); prev_mouse_x = mouse_x; prev_mouse_y = mouse_y; - const Vector2 pos = get_mouse_pos(event); + const CGFloat backingScaleFactor = [[event window] backingScaleFactor]; + const Vector2 pos = get_mouse_pos([event locationInWindow], backingScaleFactor); 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(backingScaleFactor); + relativeMotion.y = [event deltaY] * OS_OSX::singleton -> _mouse_scale(backingScaleFactor); mm->set_relative(relativeMotion); get_key_modifier_state([event modifierFlags], mm); @@ -686,7 +701,7 @@ static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) { Ref<InputEventMagnifyGesture> ev; ev.instance(); get_key_modifier_state([event modifierFlags], ev); - ev->set_position(get_mouse_pos(event)); + ev->set_position(get_mouse_pos([event locationInWindow], [[event window] backingScaleFactor])); ev->set_factor([event magnification] + 1.0); OS_OSX::singleton->push_input(ev); } @@ -929,7 +944,7 @@ static int remapKey(unsigned int key) { CFDataRef layoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData); if (!layoutData) - return 0; + return translateKey(key); const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData); @@ -1078,6 +1093,8 @@ inline void sendPanEvent(double dx, double dy, int modifierFlags) { - (void)scrollWheel:(NSEvent *)event { double deltaX, deltaY; + get_mouse_pos([event locationInWindow], [[event window] backingScaleFactor]); + deltaX = [event scrollingDeltaX]; deltaY = [event scrollingDeltaY]; @@ -1276,8 +1293,6 @@ Error OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_a ADD_ATTR2(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core); } - video_driver_index = p_video_driver; - ADD_ATTR2(NSOpenGLPFAColorSize, colorBits); /* @@ -1333,22 +1348,57 @@ Error OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_a /*** END OSX INITIALIZATION ***/ - // only opengl support here... + bool gles3 = true; if (p_video_driver == VIDEO_DRIVER_GLES2) { - RasterizerGLES2::register_config(); - RasterizerGLES2::make_current(); - } else { - RasterizerGLES3::register_config(); - RasterizerGLES3::make_current(); + gles3 = false; } + bool editor = Engine::get_singleton()->is_editor_hint(); + bool gl_initialization_error = false; + + while (true) { + if (gles3) { + if (RasterizerGLES3::is_viable() == OK) { + RasterizerGLES3::register_config(); + RasterizerGLES3::make_current(); + break; + } else { + if (GLOBAL_GET("rendering/quality/driver/driver_fallback") == "Best" || editor) { + p_video_driver = VIDEO_DRIVER_GLES2; + gles3 = false; + continue; + } else { + gl_initialization_error = true; + break; + } + } + } else { + if (RasterizerGLES2::is_viable() == OK) { + RasterizerGLES2::register_config(); + RasterizerGLES2::make_current(); + break; + } else { + gl_initialization_error = true; + break; + } + } + } + + if (gl_initialization_error) { + OS::get_singleton()->alert("Your video card driver does not support any of the supported OpenGL versions.\n" + "Please update your drivers or if you have a very old or integrated GPU upgrade it.", + "Unable to initialize Video driver"); + return ERR_UNAVAILABLE; + } + + video_driver_index = p_video_driver; + visual_server = memnew(VisualServerRaster); if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) { - visual_server = memnew(VisualServerWrapMT(visual_server, get_render_thread_mode() == RENDER_SEPARATE_THREAD)); } - visual_server->init(); + visual_server->init(); AudioDriverManager::initialize(p_audio_driver); input = memnew(InputDefault); @@ -1368,7 +1418,9 @@ Error OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_a void OS_OSX::finalize() { +#ifdef COREMIDI_ENABLED midi_driver.close(); +#endif CFNotificationCenterRemoveObserver(CFNotificationCenterGetDistributedCenter(), NULL, kTISNotifySelectedKeyboardInputSourceChanged, NULL); CGDisplayRemoveReconfigurationCallback(displays_arrangement_changed, NULL); @@ -1570,7 +1622,9 @@ void OS_OSX::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, c } ERR_FAIL_COND(!texture.is_valid()); + ERR_FAIL_COND(p_hotspot.x < 0 || p_hotspot.y < 0); ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256); + ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height); image = texture->get_data(); @@ -1626,8 +1680,10 @@ void OS_OSX::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, c [cursors[p_shape] release]; cursors[p_shape] = cursor; - if (p_shape == CURSOR_ARROW) { - [cursor set]; + if (p_shape == cursor_shape) { + if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) { + [cursor set]; + } } [imgrep release]; @@ -1635,8 +1691,10 @@ void OS_OSX::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, c } else { // Reset to default system cursor cursors[p_shape] = NULL; + + CursorShape c = cursor_shape; cursor_shape = CURSOR_MAX; - set_cursor_shape(p_shape); + set_cursor_shape(c); } } @@ -1824,28 +1882,30 @@ bool OS_OSX::can_draw() const { void OS_OSX::set_clipboard(const String &p_text) { - NSArray *types = [NSArray arrayWithObjects:NSStringPboardType, nil]; + NSString *copiedString = [NSString stringWithUTF8String:p_text.utf8().get_data()]; + NSArray *copiedStringArray = [NSArray arrayWithObject:copiedString]; NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; - [pasteboard declareTypes:types owner:nil]; - [pasteboard setString:[NSString stringWithUTF8String:p_text.utf8().get_data()] - forType:NSStringPboardType]; + [pasteboard clearContents]; + [pasteboard writeObjects:copiedStringArray]; } String OS_OSX::get_clipboard() const { NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; + NSArray *classArray = [NSArray arrayWithObject:[NSString class]]; + NSDictionary *options = [NSDictionary dictionary]; - if (![[pasteboard types] containsObject:NSStringPboardType]) { - return ""; - } + BOOL ok = [pasteboard canReadObjectForClasses:classArray options:options]; - NSString *object = [pasteboard stringForType:NSStringPboardType]; - if (!object) { + if (!ok) { return ""; } - char *utfs = strdup([object UTF8String]); + NSArray *objectsToPaste = [pasteboard readObjectsForClasses:classArray options:options]; + NSString *string = [objectsToPaste objectAtIndex:0]; + + char *utfs = strdup([string UTF8String]); String ret; ret.parse_utf8(utfs); free(utfs); @@ -2677,7 +2737,9 @@ OS_OSX::OS_OSX() { [NSApp sendEvent:event]; } +#ifdef COREAUDIO_ENABLED AudioDriverManager::add_driver(&audio_driver); +#endif } bool OS_OSX::_check_internal_feature_support(const String &p_feature) { diff --git a/platform/osx/platform_config.h b/platform/osx/platform_config.h index 3f72831d77..1d32d5b5b9 100644 --- a/platform/osx/platform_config.h +++ b/platform/osx/platform_config.h @@ -30,6 +30,6 @@ #include <alloca.h> -#define GLES3_INCLUDE_H "glad/glad.h" -#define GLES2_INCLUDE_H "glad/glad.h" +#define GLES3_INCLUDE_H "thirdparty/glad/glad/glad.h" +#define GLES2_INCLUDE_H "thirdparty/glad/glad/glad.h" #define PTHREAD_RENAME_SELF diff --git a/platform/osx/power_osx.h b/platform/osx/power_osx.h index 7123e34a03..9ad51e505b 100644 --- a/platform/osx/power_osx.h +++ b/platform/osx/power_osx.h @@ -31,9 +31,9 @@ #ifndef PLATFORM_OSX_POWER_OSX_H_ #define PLATFORM_OSX_POWER_OSX_H_ +#include "core/os/file_access.h" +#include "core/os/os.h" #include "dir_access_osx.h" -#include "os/file_access.h" -#include "os/os.h" #include <CoreFoundation/CoreFoundation.h> class power_osx { diff --git a/platform/osx/sem_osx.cpp b/platform/osx/sem_osx.cpp index 92f749322e..9b42abdb8d 100644 --- a/platform/osx/sem_osx.cpp +++ b/platform/osx/sem_osx.cpp @@ -65,7 +65,7 @@ void cgsem_destroy(cgsem_t *cgsem) { close(cgsem->pipefd[0]); } -#include "os/memory.h" +#include "core/os/memory.h" #include <errno.h> Error SemaphoreOSX::wait() { diff --git a/platform/osx/sem_osx.h b/platform/osx/sem_osx.h index ce31e966b7..0ab82873c6 100644 --- a/platform/osx/sem_osx.h +++ b/platform/osx/sem_osx.h @@ -37,7 +37,7 @@ struct cgsem { typedef struct cgsem cgsem_t; -#include "os/semaphore.h" +#include "core/os/semaphore.h" class SemaphoreOSX : public Semaphore { diff --git a/platform/server/SCsub b/platform/server/SCsub index 0788ad75ae..51fd05a87e 100644 --- a/platform/server/SCsub +++ b/platform/server/SCsub @@ -1,12 +1,21 @@ #!/usr/bin/env python -Import('env') +import os +import platform +import sys +Import('env') common_server = [\ "os_server.cpp",\ - "#platform/x11/crash_handler_x11.cpp", - "#platform/x11/power_x11.cpp", ] +if sys.platform == "darwin": + common_server.append("#platform/osx/crash_handler_osx.mm") + common_server.append("#platform/osx/power_osx.cpp") + common_server.append("#platform/osx/sem_osx.cpp") +else: + common_server.append("#platform/x11/crash_handler_x11.cpp") + common_server.append("#platform/x11/power_x11.cpp") + prog = env.add_program('#bin/godot_server', ['godot_server.cpp'] + common_server) diff --git a/platform/server/detect.py b/platform/server/detect.py index 266b0c5cc9..0b23e9c649 100644 --- a/platform/server/detect.py +++ b/platform/server/detect.py @@ -11,9 +11,15 @@ def get_name(): return "Server" +def get_program_suffix(): + if (sys.platform == "darwin"): + return "osx" + return "x11" + + def can_build(): - if (os.name != "posix" or sys.platform == "darwin"): + if (os.name != "posix"): return False return True @@ -29,9 +35,7 @@ def get_opts(): def get_flags(): - return [ - ("module_mobile_vr_enabled", False), - ] + return [] def configure(env): @@ -56,7 +60,7 @@ def configure(env): ## Compiler configuration if env['use_llvm']: - if ('clang++' not in env['CXX']): + if ('clang++' not in os.path.basename(env['CXX'])): env["CC"] = "clang" env["CXX"] = "clang++" env["LINK"] = "clang++" @@ -149,6 +153,10 @@ def configure(env): env.Append(CPPPATH=['#platform/server']) env.Append(CPPFLAGS=['-DSERVER_ENABLED', '-DUNIX_ENABLED']) + + if (platform.system() == "Darwin"): + env.Append(LINKFLAGS=['-framework', 'Cocoa', '-framework', 'Carbon', '-lz', '-framework', 'IOKit']) + env.Append(LIBS=['pthread']) if (platform.system() == "Linux"): diff --git a/platform/server/os_server.cpp b/platform/server/os_server.cpp index 1c17780ad7..60f20d6009 100644 --- a/platform/server/os_server.cpp +++ b/platform/server/os_server.cpp @@ -29,10 +29,10 @@ /*************************************************************************/ #include "os_server.h" +#include "core/print_string.h" #include "drivers/dummy/audio_driver_dummy.h" #include "drivers/dummy/rasterizer_dummy.h" #include "drivers/dummy/texture_loader_dummy.h" -#include "print_string.h" #include "servers/visual/visual_server_raster.h" #include "main/main.h" @@ -68,6 +68,10 @@ void OS_Server::initialize_core() { crash_handler.initialize(); OS_Unix::initialize_core(); + +#ifdef __APPLE__ + SemaphoreOSX::make_default(); +#endif } Error OS_Server::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { @@ -87,7 +91,11 @@ Error OS_Server::initialize(const VideoMode &p_desired, int p_video_driver, int input = memnew(InputDefault); +#ifdef __APPLE__ + power_manager = memnew(power_osx); +#else power_manager = memnew(PowerX11); +#endif _ensure_user_data_dir(); @@ -221,7 +229,7 @@ void OS_Server::run() { while (!force_quit) { - if (Main::iteration() == true) + if (Main::iteration()) break; }; diff --git a/platform/server/os_server.h b/platform/server/os_server.h index 07d70e5236..0367ec3db9 100644 --- a/platform/server/os_server.h +++ b/platform/server/os_server.h @@ -34,8 +34,14 @@ #include "drivers/rtaudio/audio_driver_rtaudio.h" #include "drivers/unix/os_unix.h" #include "main/input_default.h" +#ifdef __APPLE__ +#include "platform/osx/crash_handler_osx.h" +#include "platform/osx/power_osx.h" +#include "platform/osx/sem_osx.h" +#else #include "platform/x11/crash_handler_x11.h" #include "platform/x11/power_x11.h" +#endif #include "servers/audio_server.h" #include "servers/visual/rasterizer.h" #include "servers/visual_server.h" @@ -61,7 +67,11 @@ class OS_Server : public OS_Unix { InputDefault *input; +#ifdef __APPLE__ + power_osx *power_manager; +#else PowerX11 *power_manager; +#endif CrashHandler crash_handler; diff --git a/platform/server/platform_config.h b/platform/server/platform_config.h index 2fa8eda337..26ba8f26c6 100644 --- a/platform/server/platform_config.h +++ b/platform/server/platform_config.h @@ -28,10 +28,13 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef __linux__ +#if defined(__linux__) || defined(__APPLE__) #include <alloca.h> #endif #if defined(__FreeBSD__) || defined(__OpenBSD__) #include <stdlib.h> #define PTHREAD_BSD_SET_NAME #endif +#ifdef __APPLE__ +#define PTHREAD_RENAME_SELF +#endif diff --git a/platform/uwp/detect.py b/platform/uwp/detect.py index 559f23ca5b..f25b9ba9cd 100644 --- a/platform/uwp/detect.py +++ b/platform/uwp/detect.py @@ -17,7 +17,7 @@ def can_build(): # building natively on windows! if (os.getenv("VSINSTALLDIR")): - if (os.getenv("ANGLE_SRC_PATH") == None): + if (os.getenv("ANGLE_SRC_PATH") is None): return False return True diff --git a/platform/uwp/export/export.cpp b/platform/uwp/export/export.cpp index 1ad5293b44..41e59a5352 100644 --- a/platform/uwp/export/export.cpp +++ b/platform/uwp/export/export.cpp @@ -29,16 +29,16 @@ /*************************************************************************/ #include "export.h" -#include "bind/core_bind.h" +#include "core/bind/core_bind.h" +#include "core/io/marshalls.h" +#include "core/io/zip_io.h" +#include "core/object.h" +#include "core/os/file_access.h" +#include "core/project_settings.h" +#include "core/version.h" #include "editor/editor_export.h" #include "editor/editor_node.h" -#include "io/marshalls.h" -#include "io/zip_io.h" -#include "object.h" -#include "os/file_access.h" #include "platform/uwp/logo.gen.h" -#include "project_settings.h" -#include "version.h" #include "thirdparty/minizip/unzip.h" #include "thirdparty/minizip/zip.h" @@ -1021,8 +1021,10 @@ public: return "UWP"; } - virtual String get_binary_extension(const Ref<EditorExportPreset> &p_preset) const { - return "appx"; + virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const { + List<String> list; + list.push_back("appx"); + return list; } virtual Ref<Texture> get_logo() const { diff --git a/platform/uwp/gl_context_egl.h b/platform/uwp/gl_context_egl.h index df0108c124..3c7115cc34 100644 --- a/platform/uwp/gl_context_egl.h +++ b/platform/uwp/gl_context_egl.h @@ -34,9 +34,9 @@ #include <wrl.h> #include "EGL/egl.h" +#include "core/error_list.h" +#include "core/os/os.h" #include "drivers/gl_context/context_gl.h" -#include "error_list.h" -#include "os/os.h" using namespace Windows::UI::Core; @@ -71,8 +71,8 @@ public: virtual int get_window_height(); virtual void swap_buffers(); - void set_use_vsync(bool use) { vsync = use; } - bool is_using_vsync() const { return vsync; } + virtual void set_use_vsync(bool use) { vsync = use; } + virtual bool is_using_vsync() const { return vsync; } virtual Error initialize(); void reset(); @@ -80,7 +80,7 @@ public: void cleanup(); ContextEGL(CoreWindow ^ p_window, Driver p_driver); - ~ContextEGL(); + virtual ~ContextEGL(); }; #endif diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp index 8549a44ce5..1f81d476ea 100644 --- a/platform/uwp/os_uwp.cpp +++ b/platform/uwp/os_uwp.cpp @@ -28,25 +28,26 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +// Must include Winsock before windows.h (included by os_uwp.h) +#include "drivers/unix/net_socket_posix.h" + #include "os_uwp.h" +#include "core/io/marshalls.h" +#include "core/project_settings.h" #include "drivers/gles2/rasterizer_gles2.h" #include "drivers/gles3/rasterizer_gles3.h" #include "drivers/unix/ip_unix.h" #include "drivers/windows/dir_access_windows.h" #include "drivers/windows/file_access_windows.h" #include "drivers/windows/mutex_windows.h" -#include "drivers/windows/packet_peer_udp_winsock.h" #include "drivers/windows/rw_lock_windows.h" #include "drivers/windows/semaphore_windows.h" -#include "drivers/windows/stream_peer_tcp_winsock.h" -#include "drivers/windows/tcp_server_winsock.h" -#include "io/marshalls.h" #include "main/main.h" #include "platform/windows/windows_terminal_logger.h" -#include "project_settings.h" #include "servers/audio_server.h" #include "servers/visual/visual_server_raster.h" +#include "servers/visual/visual_server_wrap_mt.h" #include "thread_uwp.h" #include <ppltasks.h> @@ -151,9 +152,7 @@ void OSUWP::initialize_core() { DirAccess::make_default<DirAccessWindows>(DirAccess::ACCESS_USERDATA); DirAccess::make_default<DirAccessWindows>(DirAccess::ACCESS_FILESYSTEM); - TCPServerWinsock::make_default(); - StreamPeerTCPWinsock::make_default(); - PacketPeerUDPWinsock::make_default(); + NetSocketPosix::make_default(); // We need to know how often the clock is updated if (!QueryPerformanceFrequency((LARGE_INTEGER *)&ticks_per_second)) @@ -187,12 +186,78 @@ Error OSUWP::initialize(const VideoMode &p_desired, int p_video_driver, int p_au main_loop = NULL; outside = true; + ContextEGL::Driver opengl_api_type = ContextEGL::GLES_2_0; + if (p_video_driver == VIDEO_DRIVER_GLES2) { - gl_context = memnew(ContextEGL(window, ContextEGL::GLES_2_0)); - } else { - gl_context = memnew(ContextEGL(window, ContextEGL::GLES_3_0)); + opengl_api_type = ContextEGL::GLES_2_0; + } + + bool gl_initialization_error = false; + + gl_context = NULL; + while (!gl_context) { + gl_context = memnew(ContextEGL(window, opengl_api_type)); + + if (gl_context->initialize() != OK) { + memdelete(gl_context); + gl_context = NULL; + + if (GLOBAL_GET("rendering/quality/driver/driver_fallback") == "Best") { + if (p_video_driver == VIDEO_DRIVER_GLES2) { + gl_initialization_error = true; + break; + } + + p_video_driver = VIDEO_DRIVER_GLES2; + opengl_api_type = ContextEGL::GLES_2_0; + } else { + gl_initialization_error = true; + break; + } + } + } + + while (true) { + if (opengl_api_type == ContextEGL::GLES_3_0) { + if (RasterizerGLES3::is_viable() == OK) { + RasterizerGLES3::register_config(); + RasterizerGLES3::make_current(); + break; + } else { + if (GLOBAL_GET("rendering/quality/driver/driver_fallback") == "Best") { + p_video_driver = VIDEO_DRIVER_GLES2; + opengl_api_type = ContextEGL::GLES_2_0; + continue; + } else { + gl_initialization_error = true; + break; + } + } + } + + if (opengl_api_type == ContextEGL::GLES_2_0) { + if (RasterizerGLES2::is_viable() == OK) { + RasterizerGLES2::register_config(); + RasterizerGLES2::make_current(); + break; + } else { + gl_initialization_error = true; + break; + } + } + } + + if (gl_initialization_error) { + OS::get_singleton()->alert("Your video card driver does not support any of the supported OpenGL versions.\n" + "Please update your drivers or if you have a very old or integrated GPU upgrade it.", + "Unable to initialize Video driver"); + return ERR_UNAVAILABLE; } - gl_context->initialize(); + + video_driver_index = p_video_driver; + gl_context->make_current(); + gl_context->set_use_vsync(video_mode.use_vsync); + VideoMode vm; vm.width = gl_context->get_window_width(); vm.height = gl_context->get_window_height(); @@ -230,30 +295,13 @@ Error OSUWP::initialize(const VideoMode &p_desired, int p_video_driver, int p_au set_video_mode(vm); - gl_context->make_current(); - - if (p_video_driver == VIDEO_DRIVER_GLES2) { - RasterizerGLES2::register_config(); - RasterizerGLES2::make_current(); - } else { - RasterizerGLES3::register_config(); - RasterizerGLES3::make_current(); - } - gl_context->set_use_vsync(vm.use_vsync); - - video_driver_index = p_video_driver; - visual_server = memnew(VisualServerRaster); - // FIXME: Reimplement threaded rendering? Or remove? - /* + // FIXME: Reimplement threaded rendering if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) { - - visual_server = memnew(VisualServerWrapMT(visual_server, get_render_thread_mode() == RENDER_SEPARATE_THREAD)); + visual_server = memnew(VisualServerWrapMT(visual_server, false)); } - */ visual_server->init(); - input = memnew(InputDefault); joypad = ref new JoypadUWP(input); @@ -356,6 +404,8 @@ void OSUWP::finalize() { } void OSUWP::finalize_core() { + + NetSocketPosix::cleanup(); } void OSUWP::alert(const String &p_alert, const String &p_title) { @@ -812,7 +862,7 @@ void OSUWP::run() { CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent); if (managed_object->alert_close_handle) continue; process_events(); // get rid of pending events - if (Main::iteration() == true) + if (Main::iteration()) break; }; diff --git a/platform/uwp/os_uwp.h b/platform/uwp/os_uwp.h index 89f71f0013..9641b9cde9 100644 --- a/platform/uwp/os_uwp.h +++ b/platform/uwp/os_uwp.h @@ -32,13 +32,13 @@ #define OSUWP_H #include "core/math/transform_2d.h" +#include "core/os/input.h" +#include "core/os/os.h" #include "core/ustring.h" #include "drivers/xaudio2/audio_driver_xaudio2.h" #include "gl_context_egl.h" #include "joypad_uwp.h" #include "main/input_default.h" -#include "os/input.h" -#include "os/os.h" #include "power_uwp.h" #include "servers/audio_server.h" #include "servers/visual/rasterizer.h" @@ -152,7 +152,7 @@ private: Windows::Devices::Sensors::Magnetometer ^ magnetometer; Windows::Devices::Sensors::Gyrometer ^ gyrometer; - // functions used by main to initialize/deintialize the OS + // functions used by main to initialize/deinitialize the OS protected: virtual int get_video_driver_count() const; virtual int get_current_video_driver() const; diff --git a/platform/uwp/power_uwp.h b/platform/uwp/power_uwp.h index 09572a15f4..da1cffe8f0 100644 --- a/platform/uwp/power_uwp.h +++ b/platform/uwp/power_uwp.h @@ -31,9 +31,9 @@ #ifndef PLATFORM_UWP_POWER_UWP_H_ #define PLATFORM_UWP_POWER_UWP_H_ -#include "os/dir_access.h" -#include "os/file_access.h" -#include "os/os.h" +#include "core/os/dir_access.h" +#include "core/os/file_access.h" +#include "core/os/os.h" class PowerUWP { diff --git a/platform/uwp/thread_uwp.cpp b/platform/uwp/thread_uwp.cpp index 25cd29190f..c755204ec4 100644 --- a/platform/uwp/thread_uwp.cpp +++ b/platform/uwp/thread_uwp.cpp @@ -30,7 +30,7 @@ #include "thread_uwp.h" -#include "os/memory.h" +#include "core/os/memory.h" Thread *ThreadUWP::create_func_uwp(ThreadCreateCallback p_callback, void *p_user, const Settings &) { diff --git a/platform/uwp/thread_uwp.h b/platform/uwp/thread_uwp.h index 89081f3b2b..16e7efb60b 100644 --- a/platform/uwp/thread_uwp.h +++ b/platform/uwp/thread_uwp.h @@ -33,7 +33,7 @@ #ifdef UWP_ENABLED -#include "os/thread.h" +#include "core/os/thread.h" #include <thread> diff --git a/platform/windows/SCsub b/platform/windows/SCsub index 53ed3bf887..e07d373c4b 100644 --- a/platform/windows/SCsub +++ b/platform/windows/SCsub @@ -1,12 +1,13 @@ #!/usr/bin/env python -import os Import('env') +import os from platform_methods import run_in_subprocess import platform_windows_builders common_win = [ + "godot_win.cpp", "context_gl_win.cpp", "crash_handler_win.cpp", "os_windows.cpp", @@ -17,17 +18,16 @@ common_win = [ "windows_terminal_logger.cpp" ] -restarget = "godot_res" + env["OBJSUFFIX"] - -obj = env.RES(restarget, 'godot_res.rc') - -common_win.append(obj) +res_file = 'godot_res.rc' +res_target = "godot_res" + env["OBJSUFFIX"] +res_obj = env.RES(res_target, res_file) -prog = env.add_program('#bin/godot', ['godot_win.cpp'] + common_win, PROGSUFFIX=env["PROGSUFFIX"]) +prog = env.add_program('#bin/godot', common_win + res_obj, PROGSUFFIX=env["PROGSUFFIX"]) # Microsoft Visual Studio Project Generation if env['vsproj']: - env.vs_srcs = env.vs_srcs + ["platform/windows/godot_win.cpp"] + env.vs_srcs = env.vs_srcs + ["platform/windows/" + res_file] + env.vs_srcs = env.vs_srcs + ["platform/windows/godot.natvis"] for x in common_win: env.vs_srcs = env.vs_srcs + ["platform/windows/" + str(x)] diff --git a/platform/windows/context_gl_win.cpp b/platform/windows/context_gl_win.cpp index 59435b04ea..2d70b00dda 100644 --- a/platform/windows/context_gl_win.cpp +++ b/platform/windows/context_gl_win.cpp @@ -91,45 +91,41 @@ Error ContextGL_Win::initialize() { PFD_DRAW_TO_WINDOW | // Format Must Support Window PFD_SUPPORT_OPENGL | // Format Must Support OpenGL PFD_DOUBLEBUFFER, - PFD_TYPE_RGBA, - OS::get_singleton()->is_layered_allowed() ? 32 : 24, - 0, 0, 0, 0, 0, 0, // Color Bits Ignored - 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 - 24, // 24Bit Z-Buffer (Depth Buffer) - 0, // No Stencil Buffer - 0, // No Auxiliary Buffer - PFD_MAIN_PLANE, // Main Drawing Layer - 0, // Reserved + (BYTE)PFD_TYPE_RGBA, + (BYTE)(OS::get_singleton()->is_layered_allowed() ? 32 : 24), + (BYTE)0, (BYTE)0, (BYTE)0, (BYTE)0, (BYTE)0, (BYTE)0, // Color Bits Ignored + (BYTE)(OS::get_singleton()->is_layered_allowed() ? 8 : 0), // Alpha Buffer + (BYTE)0, // Shift Bit Ignored + (BYTE)0, // No Accumulation Buffer + (BYTE)0, (BYTE)0, (BYTE)0, (BYTE)0, // Accumulation Bits Ignored + (BYTE)24, // 24Bit Z-Buffer (Depth Buffer) + (BYTE)0, // No Stencil Buffer + (BYTE)0, // No Auxiliary Buffer + (BYTE)PFD_MAIN_PLANE, // Main Drawing Layer + (BYTE)0, // Reserved 0, 0, 0 // Layer Masks Ignored }; hDC = GetDC(hWnd); if (!hDC) { - MessageBox(NULL, "Can't Create A GL Device Context.", "ERROR", MB_OK | MB_ICONEXCLAMATION); return ERR_CANT_CREATE; // Return FALSE } pixel_format = ChoosePixelFormat(hDC, &pfd); if (!pixel_format) // Did Windows Find A Matching Pixel Format? { - MessageBox(NULL, "Can't Find A Suitable pixel_format.", "ERROR", MB_OK | MB_ICONEXCLAMATION); return ERR_CANT_CREATE; // Return FALSE } BOOL ret = SetPixelFormat(hDC, pixel_format, &pfd); if (!ret) // Are We Able To Set The Pixel Format? { - MessageBox(NULL, "Can't Set The pixel_format.", "ERROR", MB_OK | MB_ICONEXCLAMATION); return ERR_CANT_CREATE; // Return FALSE } hRC = wglCreateContext(hDC); if (!hRC) // Are We Able To Get A Rendering Context? { - MessageBox(NULL, "Can't Create A Temporary GL Rendering Context.", "ERROR", MB_OK | MB_ICONEXCLAMATION); return ERR_CANT_CREATE; // Return FALSE } @@ -151,7 +147,6 @@ Error ContextGL_Win::initialize() { if (wglCreateContextAttribsARB == NULL) //OpenGL 3.0 is not supported { - MessageBox(NULL, "Cannot get Proc Address for CreateContextAttribs", "ERROR", MB_OK | MB_ICONEXCLAMATION); wglDeleteContext(hRC); return ERR_CANT_CREATE; } @@ -159,7 +154,6 @@ Error ContextGL_Win::initialize() { HGLRC new_hRC = wglCreateContextAttribsARB(hDC, 0, attribs); if (!new_hRC) { wglDeleteContext(hRC); - MessageBox(NULL, "Can't Create An OpenGL 3.3 Rendering Context.", "ERROR", MB_OK | MB_ICONEXCLAMATION); return ERR_CANT_CREATE; // Return false } wglMakeCurrent(hDC, NULL); @@ -168,7 +162,6 @@ Error ContextGL_Win::initialize() { if (!wglMakeCurrent(hDC, hRC)) // Try To Activate The Rendering Context { - MessageBox(NULL, "Can't Activate The GL 3.3 Rendering Context.", "ERROR", MB_OK | MB_ICONEXCLAMATION); return ERR_CANT_CREATE; // Return FALSE } } diff --git a/platform/windows/context_gl_win.h b/platform/windows/context_gl_win.h index e7578a1aeb..5bcdb433b3 100644 --- a/platform/windows/context_gl_win.h +++ b/platform/windows/context_gl_win.h @@ -35,9 +35,9 @@ #ifndef CONTEXT_GL_WIN_H #define CONTEXT_GL_WIN_H +#include "core/error_list.h" +#include "core/os/os.h" #include "drivers/gl_context/context_gl.h" -#include "error_list.h" -#include "os/os.h" #include <windows.h> @@ -69,7 +69,7 @@ public: virtual bool is_using_vsync() const; ContextGL_Win(HWND hwnd, bool p_opengl_3_context); - ~ContextGL_Win(); + virtual ~ContextGL_Win(); }; #endif diff --git a/platform/windows/crash_handler_win.cpp b/platform/windows/crash_handler_win.cpp index 76a227c608..2760e87b8b 100644 --- a/platform/windows/crash_handler_win.cpp +++ b/platform/windows/crash_handler_win.cpp @@ -28,9 +28,9 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#include "core/project_settings.h" #include "main/main.h" #include "os_windows.h" -#include "project_settings.h" #ifdef CRASH_HANDLER_EXCEPTION diff --git a/platform/windows/detect.py b/platform/windows/detect.py index dcd3597e88..5d5af17086 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -208,8 +208,9 @@ def configure_msvc(env, manual_msvc_config): 'RTAUDIO_ENABLED', 'WASAPI_ENABLED', 'WINMIDI_ENABLED', 'TYPED_METHOD_BIND', 'WIN32', 'MSVC', - {'WINVER' : '$target_win_version', - '_WIN32_WINNT': '$target_win_version'}]) + 'WINVER=$target_win_version', + '_WIN32_WINNT=$target_win_version']) + env.AppendUnique(CPPDEFINES=['NOMINMAX']) # disable bogus min/max WinDef.h macros if env["bits"] == "64": env.AppendUnique(CPPDEFINES=['_WIN64']) diff --git a/platform/windows/export/export.cpp b/platform/windows/export/export.cpp index 38fd6366c7..dcaae40b10 100644 --- a/platform/windows/export/export.cpp +++ b/platform/windows/export/export.cpp @@ -28,10 +28,10 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#include "core/os/file_access.h" +#include "core/os/os.h" #include "editor/editor_export.h" #include "editor/editor_settings.h" -#include "os/file_access.h" -#include "os/os.h" #include "platform/windows/logo.gen.h" class EditorExportPlatformWindows : public EditorExportPlatformPC { diff --git a/platform/windows/godot.natvis b/platform/windows/godot.natvis new file mode 100644 index 0000000000..01963035a1 --- /dev/null +++ b/platform/windows/godot.natvis @@ -0,0 +1,129 @@ +<?xml version="1.0" encoding="utf-8"?> +<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010"> + <Type Name="Vector<*>"> + <Expand> + <Item Name="size">(_cowdata && _cowdata->_ptr) ? (((const unsigned int *)(_cowdata->_ptr))[-1]) : 0</Item> + <ArrayItems> + <Size>(_cowdata && _cowdata->_ptr) ? (((const unsigned int *)(_cowdata->_ptr))[-1]) : 0</Size> + <ValuePointer>(_cowdata) ? (_cowdata->_ptr) : 0</ValuePointer> + </ArrayItems> + </Expand> + </Type> + + <Type Name="PoolVector<*>"> + <Expand> + <Item Name="size">alloc ? (alloc->size / sizeof($T1)) : 0</Item> + <ArrayItems> + <Size>alloc ? (alloc->size / sizeof($T1)) : 0</Size> + <ValuePointer>alloc ? (($T1 *)alloc->mem) : 0</ValuePointer> + </ArrayItems> + </Expand> + </Type> + + <Type Name="Variant"> + <DisplayString Condition="this->type == Variant::NIL">nil</DisplayString> + <DisplayString Condition="this->type == Variant::BOOL">{_data._bool}</DisplayString> + <DisplayString Condition="this->type == Variant::INT">{_data._int}</DisplayString> + <DisplayString Condition="this->type == Variant::REAL">{_data._real}</DisplayString> + <DisplayString Condition="this->type == Variant::TRANSFORM2D">{_data._transform2d}</DisplayString> + <DisplayString Condition="this->type == Variant::AABB">{_data._aabb}</DisplayString> + <DisplayString Condition="this->type == Variant::BASIS">{_data._basis}</DisplayString> + <DisplayString Condition="this->type == Variant::TRANSFORM">{_data._transform}</DisplayString> + <DisplayString Condition="this->type == Variant::ARRAY">{*(Array *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::STRING && ((String *)(&_data._mem[0]))->_cowdata._ptr == 0">""</DisplayString> + <DisplayString Condition="this->type == Variant::STRING && ((String *)(&_data._mem[0]))->_cowdata._ptr != 0">{((String *)(&_data._mem[0]))->_cowdata._ptr,su}</DisplayString> + <DisplayString Condition="this->type == Variant::VECTOR2">{*(Vector2 *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::RECT2">{*(Rect2 *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::VECTOR3">{*(Vector3 *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::PLANE">{*(Plane *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::QUAT">{*(Quat *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::COLOR">{*(Color *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::NODE_PATH">{*(NodePath *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::_RID">{*(RID *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::OBJECT">{*(Object *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::DICTIONARY">{*(Dictionary *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::ARRAY">{*(Array *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::POOL_BYTE_ARRAY">{*(PoolByteArray *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::POOL_INT_ARRAY">{*(PoolIntArray *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::POOL_REAL_ARRAY">{*(PoolRealArray *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::POOL_STRING_ARRAY">{*(PoolStringArray *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::POOL_VECTOR2_ARRAY">{*(PoolVector2Array *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::POOL_VECTOR3_ARRAY">{*(PoolVector3Array *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::POOL_COLOR_ARRAY">{*(PoolColorArray *)_data._mem}</DisplayString> + + <StringView Condition="this->type == Variant::STRING && ((String *)(&_data._mem[0]))->_cowdata._ptr != 0">((String *)(&_data._mem[0]))->_cowdata._ptr,su</StringView> + + <Expand> + <Item Name="value" Condition="this->type == Variant::BOOL">_data._bool</Item> + <Item Name="value" Condition="this->type == Variant::INT">_data._int</Item> + <Item Name="value" Condition="this->type == Variant::REAL">_data._real</Item> + <Item Name="value" Condition="this->type == Variant::TRANSFORM2D">_data._transform2d</Item> + <Item Name="value" Condition="this->type == Variant::AABB">_data._aabb</Item> + <Item Name="value" Condition="this->type == Variant::BASIS">_data._basis</Item> + <Item Name="value" Condition="this->type == Variant::TRANSFORM">_data._transform</Item> + <Item Name="value" Condition="this->type == Variant::ARRAY">*(Array *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::STRING">*(String *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::VECTOR2">*(Vector2 *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::RECT2">*(Rect2 *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::VECTOR3">*(Vector3 *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::PLANE">*(Plane *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::QUAT">*(Quat *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::COLOR">*(Color *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::NODE_PATH">*(NodePath *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::_RID">*(RID *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::OBJECT">*(Object *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::DICTIONARY">*(Dictionary *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::ARRAY">*(Array *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::POOL_BYTE_ARRAY">*(PoolByteArray *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::POOL_INT_ARRAY">*(PoolIntArray *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::POOL_REAL_ARRAY">*(PoolRealArray *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::POOL_STRING_ARRAY">*(PoolStringArray *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::POOL_VECTOR2_ARRAY">*(PoolVector2Array *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::POOL_VECTOR3_ARRAY">*(PoolVector3Array *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::POOL_COLOR_ARRAY">*(PoolColorArray *)_data._mem</Item> + </Expand> + </Type> + + <Type Name="String"> + <DisplayString Condition="this->_cowdata._ptr == 0">empty</DisplayString> + <DisplayString Condition="this->_cowdata._ptr != 0">{this->_cowdata._ptr,su}</DisplayString> + <StringView Condition="this->_cowdata._ptr != 0">this->_cowdata._ptr,su</StringView> + </Type> + + <Type Name="Vector2"> + <DisplayString>{{{x},{y}}}</DisplayString> + <Expand> + <Item Name="x">x</Item> + <Item Name="y">y</Item> + </Expand> + </Type> + + <Type Name="Vector3"> + <DisplayString>{{{x},{y},{z}}}</DisplayString> + <Expand> + <Item Name="x">x</Item> + <Item Name="y">y</Item> + <Item Name="z">z</Item> + </Expand> + </Type> + + <Type Name="Quat"> + <DisplayString>Quat {{{x},{y},{z},{w}}}</DisplayString> + <Expand> + <Item Name="x">x</Item> + <Item Name="y">y</Item> + <Item Name="z">z</Item> + <Item Name="w">w</Item> + </Expand> + </Type> + + <Type Name="Color"> + <DisplayString>Color {{{r},{g},{b},{a}}}</DisplayString> + <Expand> + <Item Name="red">r</Item> + <Item Name="green">g</Item> + <Item Name="blue">b</Item> + <Item Name="alpha">a</Item> + </Expand> + </Type> +</AutoVisualizer> diff --git a/platform/windows/joypad.cpp b/platform/windows/joypad.cpp index b56fb6509e..7201714fb8 100644 --- a/platform/windows/joypad.cpp +++ b/platform/windows/joypad.cpp @@ -163,7 +163,7 @@ bool JoypadWindows::setup_dinput_joypad(const DIDEVICEINSTANCE *instance) { const GUID &guid = instance->guidProduct; char uid[128]; - sprintf(uid, "%08lx%04hx%04hx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx", + sprintf_s(uid, "%08lx%04hx%04hx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx", __builtin_bswap32(guid.Data1), guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]); @@ -172,7 +172,7 @@ bool JoypadWindows::setup_dinput_joypad(const DIDEVICEINSTANCE *instance) { joy->di_joy->SetDataFormat(&c_dfDIJoystick2); joy->di_joy->SetCooperativeLevel(*hWnd, DISCL_FOREGROUND); - joy->di_joy->EnumObjects(objectsCallback, this, NULL); + joy->di_joy->EnumObjects(objectsCallback, this, 0); joy->joy_axis.sort(); joy->guid = instance->guidInstance; diff --git a/platform/windows/key_mapping_win.cpp b/platform/windows/key_mapping_win.cpp index 69dd385354..80580a63b3 100644 --- a/platform/windows/key_mapping_win.cpp +++ b/platform/windows/key_mapping_win.cpp @@ -212,7 +212,7 @@ static _WinTranslatePair _vk_to_keycode[] = { { KEY_SEMICOLON, VK_OEM_1 }, // (0xBA) { KEY_EQUAL, VK_OEM_PLUS }, // (0xBB) // Windows 2000/XP: For any country/region, the '+' key - { KEY_COLON, VK_OEM_COMMA }, // (0xBC) // Windows 2000/XP: For any country/region, the ',' key + { KEY_COMMA, VK_OEM_COMMA }, // (0xBC) // Windows 2000/XP: For any country/region, the ',' key { KEY_MINUS, VK_OEM_MINUS }, // (0xBD) // Windows 2000/XP: For any country/region, the '-' key { KEY_PERIOD, VK_OEM_PERIOD }, // (0xBE) // Windows 2000/XP: For any country/region, the '.' key { KEY_SLASH, VK_OEM_2 }, // (0xBF) //Windows 2000/XP: For the US standard keyboard, the '/?' key diff --git a/platform/windows/key_mapping_win.h b/platform/windows/key_mapping_win.h index 8d6461f27d..340f916e1c 100644 --- a/platform/windows/key_mapping_win.h +++ b/platform/windows/key_mapping_win.h @@ -31,7 +31,7 @@ #ifndef KEY_MAPPING_WINDOWS_H #define KEY_MAPPING_WINDOWS_H -#include "os/keyboard.h" +#include "core/os/keyboard.h" #include <windows.h> diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index fa8717a4b8..5e9f9bc59b 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -28,27 +28,27 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +// Must include Winsock before windows.h (included by os_windows.h) +#include "drivers/unix/net_socket_posix.h" + #include "os_windows.h" +#include "core/io/marshalls.h" +#include "core/version_generated.gen.h" #include "drivers/gles2/rasterizer_gles2.h" #include "drivers/gles3/rasterizer_gles3.h" #include "drivers/windows/dir_access_windows.h" #include "drivers/windows/file_access_windows.h" #include "drivers/windows/mutex_windows.h" -#include "drivers/windows/packet_peer_udp_winsock.h" #include "drivers/windows/rw_lock_windows.h" #include "drivers/windows/semaphore_windows.h" -#include "drivers/windows/stream_peer_tcp_winsock.h" -#include "drivers/windows/tcp_server_winsock.h" #include "drivers/windows/thread_windows.h" -#include "io/marshalls.h" #include "joypad.h" #include "lang_table.h" #include "main/main.h" #include "servers/audio_server.h" #include "servers/visual/visual_server_raster.h" #include "servers/visual/visual_server_wrap_mt.h" -#include "version_generated.gen.h" #include "windows_terminal_logger.h" #include <process.h> @@ -58,11 +58,8 @@ static const WORD MAX_CONSOLE_LINES = 1500; extern "C" { -#ifdef _MSC_VER -_declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; -#else -__attribute__((visibility("default"))) DWORD NvOptimusEnablement = 0x00000001; -#endif +__declspec(dllexport) DWORD NvOptimusEnablement = 1; +__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; } // Workaround mingw-w64 < 4.0 bug @@ -190,28 +187,6 @@ BOOL WINAPI HandlerRoutine(_In_ DWORD dwCtrlType) { } } -BOOL CALLBACK _CloseWindowsEnum(HWND hWnd, LPARAM lParam) { - DWORD dwID; - - GetWindowThreadProcessId(hWnd, &dwID); - - if (dwID == (DWORD)lParam) { - PostMessage(hWnd, WM_CLOSE, 0, 0); - } - - return TRUE; -} - -bool _close_gracefully(const PROCESS_INFORMATION &pi, const DWORD dwStopWaitMsec) { - if (!EnumWindows(_CloseWindowsEnum, pi.dwProcessId)) - return false; - - if (WaitForSingleObject(pi.hProcess, dwStopWaitMsec) != WAIT_OBJECT_0) - return false; - - return true; -} - void OS_Windows::initialize_debugging() { SetConsoleCtrlHandler(HandlerRoutine, TRUE); @@ -241,9 +216,7 @@ void OS_Windows::initialize_core() { DirAccess::make_default<DirAccessWindows>(DirAccess::ACCESS_USERDATA); DirAccess::make_default<DirAccessWindows>(DirAccess::ACCESS_FILESYSTEM); - TCPServerWinsock::make_default(); - StreamPeerTCPWinsock::make_default(); - PacketPeerUDPWinsock::make_default(); + NetSocketPosix::make_default(); // We need to know how often the clock is updated if (!QueryPerformanceFrequency((LARGE_INTEGER *)&ticks_per_second)) @@ -271,7 +244,11 @@ bool OS_Windows::can_draw() const { #define MI_WP_SIGNATURE 0xFF515700 #define SIGNATURE_MASK 0xFFFFFF00 +// Keeping the name suggested by Microsoft, but this macro really answers: +// Is this mouse event emulated from touch or pen input? #define IsPenEvent(dw) (((dw)&SIGNATURE_MASK) == MI_WP_SIGNATURE) +// This one tells whether the event comes from touchscreen (and not from pen) +#define IsTouchEvent(dw) (IsPenEvent(dw) && ((dw)&0x80)) void OS_Windows::_touch_event(bool p_pressed, float p_x, float p_y, int idx) { @@ -478,7 +455,7 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) */ } - if (window_has_focus && main_loop) + if (window_has_focus && main_loop && mm->get_relative() != Vector2()) input->parse_input_event(mm); } delete[] lpb; @@ -491,7 +468,7 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) if (input->is_emulating_mouse_from_touch()) { // Universal translation enabled; ignore OS translation LPARAM extra = GetMessageExtraInfo(); - if (IsPenEvent(extra)) { + if (IsTouchEvent(extra)) { break; } } @@ -582,7 +559,7 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) if (input->is_emulating_mouse_from_touch()) { // Universal translation enabled; ignore OS translations for left button LPARAM extra = GetMessageExtraInfo(); - if (IsPenEvent(extra)) { + if (IsTouchEvent(extra)) { break; } } @@ -717,7 +694,7 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) last_button_state = mb->get_button_mask(); mb->set_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); - if (mouse_mode == MOUSE_MODE_CAPTURED) { + if (mouse_mode == MOUSE_MODE_CAPTURED && !use_raw_input) { mb->set_position(Vector2(old_x, old_y)); } @@ -783,7 +760,7 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) RECT r; GetWindowRect(hWnd, &r); - dib_size = Size2(r.right - r.left, r.bottom - r.top); + dib_size = Size2i(r.right - r.left, r.bottom - r.top); BITMAPINFO bmi; ZeroMemory(&bmi, sizeof(BITMAPINFO)); @@ -793,7 +770,7 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 32; bmi.bmiHeader.biCompression = BI_RGB; - bmi.bmiHeader.biSizeImage = dib_size.x, dib_size.y * 4; + bmi.bmiHeader.biSizeImage = dib_size.x * dib_size.y * 4; hBitmap = CreateDIBSection(hDC_dib, &bmi, DIB_RGB_COLORS, (void **)&dib_data, NULL, 0x0); SelectObject(hDC_dib, hBitmap); @@ -1070,7 +1047,6 @@ static int QueryDpiForMonitor(HMONITOR hmon, _MonitorDpiType dpiType = MDT_Defau UINT x = 0, y = 0; HRESULT hr = E_FAIL; - bool bSet = false; if (hmon && (Shcore != (HMODULE)INVALID_HANDLE_VALUE)) { hr = getDPIForMonitor(hmon, dpiType /*MDT_Effective_DPI*/, &x, &y); if (SUCCEEDED(hr) && (x > 0) && (y > 0)) { @@ -1224,7 +1200,14 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle); - char *windowid = getenv("GODOT_WINDOWID"); + char *windowid; +#ifdef MINGW_ENABLED + windowid = getenv("GODOT_WINDOWID"); +#else + size_t len; + _dupenv_s(&windowid, &len, "GODOT_WINDOWID"); +#endif + if (windowid) { // strtoull on mingw @@ -1233,6 +1216,7 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int #else hWnd = (HWND)_strtoui64(windowid, NULL, 0); #endif + free(windowid); SetLastError(0); user_proc = (WNDPROC)GetWindowLongPtr(hWnd, GWLP_WNDPROC); SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)(WNDPROC)::WndProc); @@ -1241,7 +1225,7 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int printf("Error setting WNDPROC: %li\n", le); }; - LONG_PTR proc = GetWindowLongPtr(hWnd, GWLP_WNDPROC); + GetWindowLongPtr(hWnd, GWLP_WNDPROC); RECT rect; if (!GetClientRect(hWnd, &rect)) { @@ -1273,45 +1257,83 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int } #if defined(OPENGL_ENABLED) + + bool gles3_context = true; if (p_video_driver == VIDEO_DRIVER_GLES2) { - gl_context = memnew(ContextGL_Win(hWnd, false)); - gl_context->initialize(); + gles3_context = false; + } - RasterizerGLES2::register_config(); - RasterizerGLES2::make_current(); - } else { - gl_context = memnew(ContextGL_Win(hWnd, true)); - gl_context->initialize(); + bool editor = Engine::get_singleton()->is_editor_hint(); + bool gl_initialization_error = false; + + gl_context = NULL; + while (!gl_context) { + gl_context = memnew(ContextGL_Win(hWnd, gles3_context)); + + if (gl_context->initialize() != OK) { + memdelete(gl_context); + gl_context = NULL; + + if (GLOBAL_GET("rendering/quality/driver/driver_fallback") == "Best" || editor) { + if (p_video_driver == VIDEO_DRIVER_GLES2) { + gl_initialization_error = true; + break; + } + + p_video_driver = VIDEO_DRIVER_GLES2; + gles3_context = false; + } else { + gl_initialization_error = true; + break; + } + } + } + + while (true) { + if (gles3_context) { + if (RasterizerGLES3::is_viable() == OK) { + RasterizerGLES3::register_config(); + RasterizerGLES3::make_current(); + break; + } else { + if (GLOBAL_GET("rendering/quality/driver/driver_fallback") == "Best" || editor) { + p_video_driver = VIDEO_DRIVER_GLES2; + gles3_context = false; + continue; + } else { + gl_initialization_error = true; + break; + } + } + } else { + if (RasterizerGLES2::is_viable() == OK) { + RasterizerGLES2::register_config(); + RasterizerGLES2::make_current(); + break; + } else { + gl_initialization_error = true; + break; + } + } + } - RasterizerGLES3::register_config(); - RasterizerGLES3::make_current(); + if (gl_initialization_error) { + OS::get_singleton()->alert("Your video card driver does not support any of the supported OpenGL versions.\n" + "Please update your drivers or if you have a very old or integrated GPU upgrade it.", + "Unable to initialize Video driver"); + return ERR_UNAVAILABLE; } - video_driver_index = p_video_driver; // FIXME TODO - FIX IF DRIVER DETECTION HAPPENS AND GLES2 MUST BE USED + video_driver_index = p_video_driver; gl_context->set_use_vsync(video_mode.use_vsync); #endif visual_server = memnew(VisualServerRaster); if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) { - visual_server = memnew(VisualServerWrapMT(visual_server, get_render_thread_mode() == RENDER_SEPARATE_THREAD)); } - /* - DEVMODE dmScreenSettings; // Device Mode - memset(&dmScreenSettings,0,sizeof(dmScreenSettings)); // Makes Sure Memory's Cleared - dmScreenSettings.dmSize=sizeof(dmScreenSettings); // Size Of The Devmode Structure - dmScreenSettings.dmPelsWidth = width; // Selected Screen Width - dmScreenSettings.dmPelsHeight = height; // Selected Screen Height - dmScreenSettings.dmBitsPerPel = bits; // Selected Bits Per Pixel - dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT; - if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL) - - - - - */ visual_server->init(); input = memnew(InputDefault); @@ -1479,9 +1501,7 @@ void OS_Windows::finalize_core() { timeEndPeriod(1); memdelete(process_map); - - TCPServerWinsock::cleanup(); - StreamPeerTCPWinsock::cleanup(); + NetSocketPosix::cleanup(); } void OS_Windows::alert(const String &p_alert, const String &p_title) { @@ -1676,18 +1696,31 @@ void OS_Windows::set_window_position(const Point2 &p_position) { RECT r; GetWindowRect(hWnd, &r); MoveWindow(hWnd, p_position.x, p_position.y, r.right - r.left, r.bottom - r.top, TRUE); + + // Don't let the mouse leave the window when moved + if (mouse_mode == MOUSE_MODE_CONFINED) { + RECT rect; + GetClientRect(hWnd, &rect); + ClientToScreen(hWnd, (POINT *)&rect.left); + ClientToScreen(hWnd, (POINT *)&rect.right); + ClipCursor(&rect); + } } Size2 OS_Windows::get_window_size() const { RECT r; - GetClientRect(hWnd, &r); - return Vector2(r.right - r.left, r.bottom - r.top); + if (GetClientRect(hWnd, &r)) { // Only area inside of window border + return Size2(r.right - r.left, r.bottom - r.top); + } + return Size2(); } Size2 OS_Windows::get_real_window_size() const { RECT r; - GetWindowRect(hWnd, &r); - return Vector2(r.right - r.left, r.bottom - r.top); + if (GetWindowRect(hWnd, &r)) { // Includes area of the window border + return Size2(r.right - r.left, r.bottom - r.top); + } + return Size2(); } void OS_Windows::set_window_size(const Size2 p_size) { @@ -1704,7 +1737,7 @@ void OS_Windows::set_window_size(const Size2 p_size) { RECT rect; GetWindowRect(hWnd, &rect); - if (video_mode.borderless_window == false) { + if (!video_mode.borderless_window) { RECT crect; GetClientRect(hWnd, &crect); @@ -2217,14 +2250,15 @@ void OS_Windows::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shap } ERR_FAIL_COND(!texture.is_valid()); + ERR_FAIL_COND(p_hotspot.x < 0 || p_hotspot.y < 0); ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256); + ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height); image = texture->get_data(); ERR_FAIL_COND(!image.is_valid()); UINT image_size = texture_size.width * texture_size.height; - UINT size = sizeof(UINT) * image_size; // Create the BITMAP with alpha channel COLORREF *buffer = (COLORREF *)memalloc(sizeof(COLORREF) * image_size); @@ -2269,8 +2303,10 @@ void OS_Windows::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shap cursors[p_shape] = CreateIconIndirect(&iconinfo); - if (p_shape == CURSOR_ARROW) { - SetCursor(cursors[p_shape]); + if (p_shape == cursor_shape) { + if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) { + SetCursor(cursors[p_shape]); + } } if (hAndMask != NULL) { @@ -2286,8 +2322,10 @@ void OS_Windows::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shap } else { // Reset to default system cursor cursors[p_shape] = NULL; + + CursorShape c = cursor_shape; cursor_shape = CURSOR_MAX; - set_cursor_shape(p_shape); + set_cursor_shape(c); } } @@ -2411,26 +2449,20 @@ Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments, return OK; }; -Error OS_Windows::kill(const ProcessID &p_pid, const int p_max_wait_msec) { +Error OS_Windows::kill(const ProcessID &p_pid) { + ERR_FAIL_COND_V(!process_map->has(p_pid), FAILED); const PROCESS_INFORMATION pi = (*process_map)[p_pid].pi; process_map->erase(p_pid); - Error result; - - if (p_max_wait_msec != -1 && _close_gracefully(pi, p_max_wait_msec)) { - result = OK; - } else { - const int ret = TerminateProcess(pi.hProcess, 0); - result = ret != 0 ? OK : FAILED; - } + const int ret = TerminateProcess(pi.hProcess, 0); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); - return result; -} + return ret != 0 ? OK : FAILED; +}; int OS_Windows::get_process_id() const { return _getpid(); @@ -2506,7 +2538,16 @@ void OS_Windows::set_icon(const Ref<Image> &p_icon) { bool OS_Windows::has_environment(const String &p_var) const { +#ifdef MINGW_ENABLED return _wgetenv(p_var.c_str()) != NULL; +#else + wchar_t *env; + size_t len; + _wdupenv_s(&env, &len, p_var.c_str()); + const bool has_env = env != NULL; + free(env); + return has_env; +#endif }; String OS_Windows::get_environment(const String &p_var) const { @@ -2689,15 +2730,10 @@ void OS_Windows::run() { main_loop->init(); - uint64_t last_ticks = get_ticks_usec(); - - int frames = 0; - uint64_t frame = 0; - while (!force_quit) { process_events(); // get rid of pending events - if (Main::iteration() == true) + if (Main::iteration()) break; }; @@ -2886,7 +2922,7 @@ bool OS_Windows::is_disable_crash_handler() const { Error OS_Windows::move_to_trash(const String &p_path) { SHFILEOPSTRUCTW sf; WCHAR *from = new WCHAR[p_path.length() + 2]; - wcscpy(from, p_path.c_str()); + wcscpy_s(from, p_path.length() + 1, p_path.c_str()); from[p_path.length() + 1] = 0; sf.hwnd = hWnd; diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index 243d4bb328..01e1c51ca5 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -31,13 +31,13 @@ #ifndef OS_WINDOWS_H #define OS_WINDOWS_H #include "context_gl_win.h" +#include "core/os/input.h" +#include "core/os/os.h" #include "core/project_settings.h" #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" #include "servers/audio_server.h" #include "servers/visual/rasterizer.h" @@ -157,7 +157,7 @@ class OS_Windows : public OS { void _update_window_style(bool repaint = true); - // functions used by main to initialize/deintialize the OS + // functions used by main to initialize/deinitialize the OS protected: virtual int get_current_video_driver() const; @@ -259,7 +259,7 @@ public: virtual uint64_t get_ticks_usec() const; virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false); - virtual Error kill(const ProcessID &p_pid, const int p_stop_max_wait_msec = -1); + virtual Error kill(const ProcessID &p_pid); virtual int get_process_id() const; virtual bool has_environment(const String &p_var) const; diff --git a/platform/windows/platform_config.h b/platform/windows/platform_config.h index d100385e80..aa020ed470 100644 --- a/platform/windows/platform_config.h +++ b/platform/windows/platform_config.h @@ -32,5 +32,5 @@ //#else //#include <alloca.h> //#endif -#define GLES3_INCLUDE_H "glad/glad.h" -#define GLES2_INCLUDE_H "glad/glad.h" +#define GLES3_INCLUDE_H "thirdparty/glad/glad/glad.h" +#define GLES2_INCLUDE_H "thirdparty/glad/glad/glad.h" diff --git a/platform/windows/power_windows.h b/platform/windows/power_windows.h index 1c1a8c0876..4984b473ca 100644 --- a/platform/windows/power_windows.h +++ b/platform/windows/power_windows.h @@ -31,9 +31,9 @@ #ifndef PLATFORM_WINDOWS_POWER_WINDOWS_H_ #define PLATFORM_WINDOWS_POWER_WINDOWS_H_ -#include "os/dir_access.h" -#include "os/file_access.h" -#include "os/os.h" +#include "core/os/dir_access.h" +#include "core/os/file_access.h" +#include "core/os/os.h" #include <windows.h> diff --git a/platform/windows/windows_terminal_logger.h b/platform/windows/windows_terminal_logger.h index 1ad2bcb0fd..1cd1941b8a 100644 --- a/platform/windows/windows_terminal_logger.h +++ b/platform/windows/windows_terminal_logger.h @@ -33,7 +33,7 @@ #ifdef WINDOWS_ENABLED -#include "io/logger.h" +#include "core/io/logger.h" class WindowsTerminalLogger : public StdLogger { public: @@ -44,4 +44,4 @@ public: #endif -#endif
\ No newline at end of file +#endif diff --git a/platform/x11/SCsub b/platform/x11/SCsub index d3901eb798..97d3d1b514 100644 --- a/platform/x11/SCsub +++ b/platform/x11/SCsub @@ -1,8 +1,8 @@ #!/usr/bin/env python -import os Import('env') +import os from platform_methods import run_in_subprocess import platform_x11_builders diff --git a/platform/x11/context_gl_x11.cpp b/platform/x11/context_gl_x11.cpp index 5a239e326b..8c1869a1f1 100644 --- a/platform/x11/context_gl_x11.cpp +++ b/platform/x11/context_gl_x11.cpp @@ -116,9 +116,14 @@ Error ContextGL_X11::initialize() { }; int fbcount; - GLXFBConfig fbconfig; + GLXFBConfig fbconfig = 0; XVisualInfo *vi = NULL; + XSetWindowAttributes swa; + swa.event_mask = StructureNotifyMask; + swa.border_pixel = 0; + unsigned long valuemask = CWBorderPixel | CWColormap | CWEventMask; + if (OS::get_singleton()->is_layered_allowed()) { GLXFBConfig *fbc = glXChooseFBConfig(x11_display, DefaultScreen(x11_display), visual_attribs_layered, &fbcount); ERR_FAIL_COND_V(!fbc, ERR_UNCONFIGURED); @@ -142,16 +147,10 @@ Error ContextGL_X11::initialize() { } ERR_FAIL_COND_V(!fbconfig, ERR_UNCONFIGURED); - XSetWindowAttributes swa; - - swa.colormap = XCreateColormap(x11_display, RootWindow(x11_display, vi->screen), vi->visual, AllocNone); - swa.border_pixel = 0; swa.background_pixmap = None; swa.background_pixel = 0; swa.border_pixmap = None; - swa.event_mask = StructureNotifyMask; - - x11_window = XCreateWindow(x11_display, RootWindow(x11_display, vi->screen), 0, 0, OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height, 0, vi->depth, InputOutput, vi->visual, CWBorderPixel | CWColormap | CWEventMask | CWBackPixel, &swa); + valuemask |= CWBackPixel; } else { GLXFBConfig *fbc = glXChooseFBConfig(x11_display, DefaultScreen(x11_display), visual_attribs, &fbcount); @@ -160,42 +159,21 @@ Error ContextGL_X11::initialize() { vi = glXGetVisualFromFBConfig(x11_display, fbc[0]); fbconfig = fbc[0]; - - XSetWindowAttributes swa; - - swa.colormap = XCreateColormap(x11_display, RootWindow(x11_display, vi->screen), vi->visual, AllocNone); - swa.border_pixel = 0; - swa.event_mask = StructureNotifyMask; - - x11_window = XCreateWindow(x11_display, RootWindow(x11_display, vi->screen), 0, 0, OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height, 0, vi->depth, InputOutput, vi->visual, CWBorderPixel | CWColormap | CWEventMask, &swa); } - ERR_FAIL_COND_V(!x11_window, ERR_UNCONFIGURED); - set_class_hint(x11_display, x11_window); - XMapWindow(x11_display, x11_window); - - int (*oldHandler)(Display *, XErrorEvent *) = - XSetErrorHandler(&ctxErrorHandler); + int (*oldHandler)(Display *, XErrorEvent *) = XSetErrorHandler(&ctxErrorHandler); switch (context_type) { - case GLES_2_0_COMPATIBLE: case OLDSTYLE: { + p->glx_context = glXCreateContext(x11_display, vi, 0, GL_TRUE); + ERR_FAIL_COND_V(!p->glx_context, ERR_UNCONFIGURED); } break; - /* - case ContextType::GLES_2_0_COMPATIBLE: { - - static int context_attribs[] = { - GLX_CONTEXT_MAJOR_VERSION_ARB, 3, - GLX_CONTEXT_MINOR_VERSION_ARB, 0, - None - }; + case GLES_2_0_COMPATIBLE: { - p->glx_context = glXCreateContextAttribsARB(x11_display, fbconfig, NULL, true, context_attribs); - ERR_EXPLAIN("Could not obtain an OpenGL 3.0 context!"); + p->glx_context = glXCreateNewContext(x11_display, fbconfig, GLX_RGBA_TYPE, 0, true); ERR_FAIL_COND_V(!p->glx_context, ERR_UNCONFIGURED); } break; - */ case GLES_3_0_COMPATIBLE: { static int context_attribs[] = { @@ -207,24 +185,22 @@ Error ContextGL_X11::initialize() { }; p->glx_context = glXCreateContextAttribsARB(x11_display, fbconfig, NULL, true, context_attribs); - ERR_EXPLAIN("Could not obtain an OpenGL 3.3 context!"); ERR_FAIL_COND_V(ctxErrorOccurred || !p->glx_context, ERR_UNCONFIGURED); } break; } + swa.colormap = XCreateColormap(x11_display, RootWindow(x11_display, vi->screen), vi->visual, AllocNone); + x11_window = XCreateWindow(x11_display, RootWindow(x11_display, vi->screen), 0, 0, OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height, 0, vi->depth, InputOutput, vi->visual, valuemask, &swa); + + ERR_FAIL_COND_V(!x11_window, ERR_UNCONFIGURED); + set_class_hint(x11_display, x11_window); + XMapWindow(x11_display, x11_window); + XSync(x11_display, False); XSetErrorHandler(oldHandler); glXMakeCurrent(x11_display, x11_window, p->glx_context); - /* - glWrapperInit(wrapper_get_proc_address); - glFlush(); - - glXSwapBuffers(x11_display,x11_window); -*/ - //glXMakeCurrent(x11_display, None, NULL); - XFree(vi); return OK; @@ -297,7 +273,6 @@ ContextGL_X11::ContextGL_X11(::Display *p_x11_display, ::Window &p_x11_window, c ContextGL_X11::~ContextGL_X11() { release_current(); glXDestroyContext(x11_display, p->glx_context); - memdelete(p); } diff --git a/platform/x11/context_gl_x11.h b/platform/x11/context_gl_x11.h index b8f3eb95d4..be3083d957 100644 --- a/platform/x11/context_gl_x11.h +++ b/platform/x11/context_gl_x11.h @@ -38,8 +38,8 @@ #if defined(OPENGL_ENABLED) +#include "core/os/os.h" #include "drivers/gl_context/context_gl.h" -#include "os/os.h" #include <X11/Xlib.h> #include <X11/extensions/Xrender.h> @@ -79,7 +79,7 @@ public: virtual bool is_using_vsync() const; ContextGL_X11(::Display *p_x11_display, ::Window &p_x11_window, const OS::VideoMode &p_default_video_mode, ContextType p_context_type); - ~ContextGL_X11(); + virtual ~ContextGL_X11(); }; #endif diff --git a/platform/x11/crash_handler_x11.cpp b/platform/x11/crash_handler_x11.cpp index 960105271b..79c3d9aece 100644 --- a/platform/x11/crash_handler_x11.cpp +++ b/platform/x11/crash_handler_x11.cpp @@ -33,9 +33,9 @@ #endif #include "crash_handler_x11.h" +#include "core/os/os.h" +#include "core/project_settings.h" #include "main/main.h" -#include "os/os.h" -#include "project_settings.h" #ifdef CRASH_HANDLER_ENABLED #include <cxxabi.h> @@ -45,8 +45,9 @@ #include <stdlib.h> static void handle_crash(int sig) { - if (OS::get_singleton() == NULL) - return; + if (OS::get_singleton() == NULL) { + abort(); + } void *bt_buffer[256]; size_t size = backtrace(bt_buffer, 256); @@ -119,6 +120,7 @@ CrashHandler::CrashHandler() { } CrashHandler::~CrashHandler() { + disable(); } void CrashHandler::disable() { diff --git a/platform/x11/detect.py b/platform/x11/detect.py index 6a7a426804..ee59e9b5a1 100644 --- a/platform/x11/detect.py +++ b/platform/x11/detect.py @@ -86,7 +86,7 @@ def configure(env): env.Prepend(CCFLAGS=['-O3', '-ffast-math']) else: #optimize for size env.Prepend(CCFLAGS=['-Os']) - + if (env["debug_symbols"] == "yes"): env.Prepend(CCFLAGS=['-g1']) if (env["debug_symbols"] == "full"): @@ -115,12 +115,12 @@ def configure(env): ## Compiler configuration - if 'CXX' in env and 'clang' in env['CXX']: + if 'CXX' in env and 'clang' in os.path.basename(env['CXX']): # Convenience check to enforce the use_llvm overrides when CXX is clang(++) env['use_llvm'] = True if env['use_llvm']: - if ('clang++' not in env['CXX']): + if ('clang++' not in os.path.basename(env['CXX'])): env["CC"] = "clang" env["CXX"] = "clang++" env["LINK"] = "clang++" @@ -250,7 +250,8 @@ def configure(env): if (os.system("pkg-config --exists alsa") == 0): # 0 means found print("Enabling ALSA") env.Append(CPPFLAGS=["-DALSA_ENABLED", "-DALSAMIDI_ENABLED"]) - env.ParseConfig('pkg-config alsa --cflags --libs') + # Don't parse --cflags, we don't need to add /usr/include/alsa to include path + env.ParseConfig('pkg-config alsa --libs') else: print("ALSA libraries not found, disabling driver") @@ -278,7 +279,7 @@ def configure(env): env.ParseConfig('pkg-config zlib --cflags --libs') env.Append(CPPPATH=['#platform/x11']) - env.Append(CPPFLAGS=['-DX11_ENABLED', '-DUNIX_ENABLED', '-DOPENGL_ENABLED', '-DGLES_ENABLED', '-DGLES_OVER_GL']) + env.Append(CPPFLAGS=['-DX11_ENABLED', '-DUNIX_ENABLED', '-DOPENGL_ENABLED', '-DGLES_ENABLED']) env.Append(LIBS=['GL', 'pthread']) if (platform.system() == "Linux"): diff --git a/platform/x11/godot_x11.cpp b/platform/x11/godot_x11.cpp index 3241cbcbf9..21148f8e86 100644 --- a/platform/x11/godot_x11.cpp +++ b/platform/x11/godot_x11.cpp @@ -43,7 +43,7 @@ int main(int argc, char *argv[]) { setlocale(LC_CTYPE, ""); char *cwd = (char *)malloc(PATH_MAX); - getcwd(cwd, PATH_MAX); + char *ret = getcwd(cwd, PATH_MAX); Error err = Main::setup(argv[0], argc - 1, &argv[1]); if (err != OK) { @@ -55,7 +55,8 @@ int main(int argc, char *argv[]) { os.run(); // it is actually the OS that decides how to run Main::cleanup(); - chdir(cwd); + if (ret) + chdir(cwd); free(cwd); return os.get_exit_code(); diff --git a/platform/x11/joypad_linux.h b/platform/x11/joypad_linux.h index 1187acac23..34b240abf1 100644 --- a/platform/x11/joypad_linux.h +++ b/platform/x11/joypad_linux.h @@ -33,9 +33,9 @@ #define JOYPAD_LINUX_H #ifdef JOYDEV_ENABLED +#include "core/os/mutex.h" +#include "core/os/thread.h" #include "main/input_default.h" -#include "os/mutex.h" -#include "os/thread.h" struct input_absinfo; diff --git a/platform/x11/key_mapping_x11.h b/platform/x11/key_mapping_x11.h index 62dfcf3a4d..6f05941c19 100644 --- a/platform/x11/key_mapping_x11.h +++ b/platform/x11/key_mapping_x11.h @@ -41,7 +41,7 @@ #define XK_XKB_KEYS #include <X11/keysymdef.h> -#include "os/keyboard.h" +#include "core/os/keyboard.h" class KeyMappingX11 { KeyMappingX11(){}; diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index 56b0c975c4..8ba5833796 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -29,12 +29,12 @@ /*************************************************************************/ #include "os_x11.h" +#include "core/os/dir_access.h" +#include "core/print_string.h" #include "drivers/gles2/rasterizer_gles2.h" #include "drivers/gles3/rasterizer_gles3.h" #include "errno.h" #include "key_mapping_x11.h" -#include "os/dir_access.h" -#include "print_string.h" #include "servers/visual/visual_server_raster.h" #include "servers/visual/visual_server_wrap_mt.h" @@ -274,31 +274,80 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a opengl_api_type = ContextGL_X11::GLES_2_0_COMPATIBLE; } - context_gl = memnew(ContextGL_X11(x11_display, x11_window, current_videomode, opengl_api_type)); - context_gl->initialize(); + bool editor = Engine::get_singleton()->is_editor_hint(); + bool gl_initialization_error = false; - switch (opengl_api_type) { - case ContextGL_X11::GLES_2_0_COMPATIBLE: { - RasterizerGLES2::register_config(); - RasterizerGLES2::make_current(); - } break; - case ContextGL_X11::GLES_3_0_COMPATIBLE: { - RasterizerGLES3::register_config(); - RasterizerGLES3::make_current(); - } break; + context_gl = NULL; + while (!context_gl) { + context_gl = memnew(ContextGL_X11(x11_display, x11_window, current_videomode, opengl_api_type)); + + if (context_gl->initialize() != OK) { + memdelete(context_gl); + context_gl = NULL; + + if (GLOBAL_GET("rendering/quality/driver/driver_fallback") == "Best" || editor) { + if (p_video_driver == VIDEO_DRIVER_GLES2) { + gl_initialization_error = true; + break; + } + + p_video_driver = VIDEO_DRIVER_GLES2; + opengl_api_type = ContextGL_X11::GLES_2_0_COMPATIBLE; + } else { + gl_initialization_error = true; + break; + } + } + } + + while (true) { + if (opengl_api_type == ContextGL_X11::GLES_3_0_COMPATIBLE) { + if (RasterizerGLES3::is_viable() == OK) { + RasterizerGLES3::register_config(); + RasterizerGLES3::make_current(); + break; + } else { + if (GLOBAL_GET("rendering/quality/driver/driver_fallback") == "Best" || editor) { + p_video_driver = VIDEO_DRIVER_GLES2; + opengl_api_type = ContextGL_X11::GLES_2_0_COMPATIBLE; + continue; + } else { + gl_initialization_error = true; + break; + } + } + } + + if (opengl_api_type == ContextGL_X11::GLES_2_0_COMPATIBLE) { + if (RasterizerGLES2::is_viable() == OK) { + RasterizerGLES2::register_config(); + RasterizerGLES2::make_current(); + break; + } else { + gl_initialization_error = true; + break; + } + } + } + + if (gl_initialization_error) { + OS::get_singleton()->alert("Your video card driver does not support any of the supported OpenGL versions.\n" + "Please update your drivers or if you have a very old or integrated GPU upgrade it.", + "Unable to initialize Video driver"); + return ERR_UNAVAILABLE; } - video_driver_index = p_video_driver; // FIXME TODO - FIX IF DRIVER DETECTION HAPPENS AND GLES2 MUST BE USED + video_driver_index = p_video_driver; context_gl->set_use_vsync(current_videomode.use_vsync); #endif - visual_server = memnew(VisualServerRaster); + visual_server = memnew(VisualServerRaster); if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) { - visual_server = memnew(VisualServerWrapMT(visual_server, get_render_thread_mode() == RENDER_SEPARATE_THREAD)); } + if (current_videomode.maximized) { current_videomode.maximized = false; set_window_maximized(true); @@ -339,8 +388,6 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a set_window_always_on_top(true); } - AudioDriverManager::initialize(p_audio_driver); - ERR_FAIL_COND_V(!visual_server, ERR_UNAVAILABLE); ERR_FAIL_COND_V(x11_window == 0, ERR_UNAVAILABLE); @@ -510,6 +557,8 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a visual_server->init(); + AudioDriverManager::initialize(p_audio_driver); + input = memnew(InputDefault); window_has_focus = true; // Set focus to true at init @@ -532,6 +581,8 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a } } + update_real_mouse_position(); + return OK; } @@ -693,12 +744,15 @@ void OS_X11::set_mouse_mode(MouseMode p_mode) { ERR_PRINT("NO GRAB"); } - center.x = current_videomode.width / 2; - center.y = current_videomode.height / 2; - XWarpPointer(x11_display, None, x11_window, - 0, 0, 0, 0, (int)center.x, (int)center.y); + if (mouse_mode == MOUSE_MODE_CAPTURED) { + center.x = current_videomode.width / 2; + center.y = current_videomode.height / 2; + + XWarpPointer(x11_display, None, x11_window, + 0, 0, 0, 0, (int)center.x, (int)center.y); - input->set_mouse_position(center); + input->set_mouse_position(center); + } } else { do_mouse_warp = false; } @@ -1001,6 +1055,7 @@ Point2 OS_X11::get_window_position() const { void OS_X11::set_window_position(const Point2 &p_position) { XMoveWindow(x11_display, x11_window, p_position.x, p_position.y); + update_real_mouse_position(); } Size2 OS_X11::get_window_size() const { @@ -1030,8 +1085,18 @@ Size2 OS_X11::get_real_window_size() const { } void OS_X11::set_window_size(const Size2 p_size) { + + if (current_videomode.width == p_size.width && current_videomode.height == p_size.height) + return; + + XWindowAttributes xwa; + XSync(x11_display, False); + XGetWindowAttributes(x11_display, x11_window, &xwa); + int old_w = xwa.width; + int old_h = xwa.height; + // If window resizable is disabled we need to update the attributes first - if (is_window_resizable() == false) { + if (!is_window_resizable()) { XSizeHints *xsh; xsh = XAllocSizeHints(); xsh->flags = PMinSize | PMaxSize; @@ -1049,6 +1114,16 @@ void OS_X11::set_window_size(const Size2 p_size) { // Update our videomode width and height current_videomode.width = p_size.x; current_videomode.height = p_size.y; + + for (int timeout = 0; timeout < 50; ++timeout) { + XSync(x11_display, False); + XGetWindowAttributes(x11_display, x11_window, &xwa); + + if (old_w != xwa.width || old_h != xwa.height) + break; + + usleep(10000); + } } void OS_X11::set_window_fullscreen(bool p_enabled) { @@ -1613,7 +1688,7 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) { } } else { //ignore - if (last_is_pressed == false) { + if (!last_is_pressed) { return; } } @@ -1920,15 +1995,9 @@ void OS_X11::process_xevents() { } break; case MotionNotify: { - // FUCK YOU X11 API YOU SERIOUSLY GROSS ME OUT - // YOU ARE AS GROSS AS LOOKING AT A PUTRID PILE - // OF POOP STICKING OUT OF A CLOGGED TOILET - // HOW THE FUCK I AM SUPPOSED TO KNOW WHICH ONE - // OF THE MOTION NOTIFY EVENTS IS THE ONE GENERATED - // BY WARPING THE MOUSE POINTER? - // YOU ARE FORCING ME TO FILTER ONE BY ONE TO FIND IT - // PLEASE DO ME A FAVOR AND DIE DROWNED IN A FECAL - // MOUNTAIN BECAUSE THAT'S WHERE YOU BELONG. + // The X11 API requires filtering one-by-one through the motion + // notify events, in order to figure out which event is the one + // generated by warping the mouse pointer. while (true) { if (mouse_mode == MOUSE_MODE_CAPTURED && event.xmotion.x == current_videomode.width / 2 && event.xmotion.y == current_videomode.height / 2) { @@ -1995,6 +2064,10 @@ void OS_X11::process_xevents() { Point2i rel = pos - last_mouse_pos; + if (mouse_mode == MOUSE_MODE_CAPTURED) { + pos = Point2i(current_videomode.width / 2, current_videomode.height / 2); + } + Ref<InputEventMouseMotion> mm; mm.instance(); @@ -2022,7 +2095,7 @@ void OS_X11::process_xevents() { last_timestamp = event.xkey.time; // key event is a little complex, so - // it will be handled in it's own function. + // it will be handled in its own function. handle_key_event((XKeyEvent *)&event); } break; case SelectionRequest: { @@ -2445,13 +2518,16 @@ void OS_X11::set_cursor_shape(CursorShape p_shape) { ERR_FAIL_INDEX(p_shape, CURSOR_MAX); - if (p_shape == current_cursor) + if (p_shape == current_cursor) { return; - if (mouse_mode == MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) { - if (cursors[p_shape] != None) + } + + if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) { + if (cursors[p_shape] != None) { XDefineCursor(x11_display, x11_window, cursors[p_shape]); - else if (cursors[CURSOR_ARROW] != None) + } else if (cursors[CURSOR_ARROW] != None) { XDefineCursor(x11_display, x11_window, cursors[CURSOR_ARROW]); + } } current_cursor = p_shape; @@ -2485,7 +2561,9 @@ void OS_X11::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, c } ERR_FAIL_COND(!texture.is_valid()); + ERR_FAIL_COND(p_hotspot.x < 0 || p_hotspot.y < 0); ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256); + ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height); image = texture->get_data(); @@ -2525,8 +2603,10 @@ void OS_X11::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, c // Save it for a further usage cursors[p_shape] = XcursorImageLoadCursor(x11_display, cursor_image); - if (p_shape == CURSOR_ARROW) { - XDefineCursor(x11_display, x11_window, cursors[p_shape]); + if (p_shape == current_cursor) { + if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) { + XDefineCursor(x11_display, x11_window, cursors[p_shape]); + } } memfree(cursor_image->pixels); @@ -2537,8 +2617,9 @@ void OS_X11::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, c cursors[p_shape] = XcursorImageLoadCursor(x11_display, img[p_shape]); } + CursorShape c = current_cursor; current_cursor = CURSOR_MAX; - set_cursor_shape(p_shape); + set_cursor_shape(c); } } @@ -2564,51 +2645,146 @@ void OS_X11::swap_buffers() { } void OS_X11::alert(const String &p_alert, const String &p_title) { + const char *message_programs[] = { "zenity", "kdialog", "Xdialog", "xmessage" }; + + String path = get_environment("PATH"); + Vector<String> path_elems = path.split(":", false); + String program; + + for (int i = 0; i < path_elems.size(); i++) { + for (unsigned int k = 0; k < sizeof(message_programs) / sizeof(char *); k++) { + String tested_path = path_elems[i] + "/" + message_programs[k]; + + if (FileAccess::exists(tested_path)) { + program = tested_path; + break; + } + } + + if (program.length()) + break; + } List<String> args; - args.push_back("-center"); - args.push_back("-title"); - args.push_back(p_title); - args.push_back(p_alert); - execute("xmessage", args, true); + if (program.ends_with("zenity")) { + args.push_back("--error"); + args.push_back("--width"); + args.push_back("500"); + args.push_back("--title"); + args.push_back(p_title); + args.push_back("--text"); + args.push_back(p_alert); + } + + if (program.ends_with("kdialog")) { + args.push_back("--error"); + args.push_back(p_alert); + args.push_back("--title"); + args.push_back(p_title); + } + + if (program.ends_with("Xdialog")) { + args.push_back("--title"); + args.push_back(p_title); + args.push_back("--msgbox"); + args.push_back(p_alert); + args.push_back("0"); + args.push_back("0"); + } + + if (program.ends_with("xmessage")) { + args.push_back("-center"); + args.push_back("-title"); + args.push_back(p_title); + args.push_back(p_alert); + } + + if (program.length()) { + execute(program, args, true); + } else { + print_line(p_alert); + } + + return; +} + +bool g_set_icon_error = false; +int set_icon_errorhandler(Display *dpy, XErrorEvent *ev) { + g_set_icon_error = true; + return 0; } void OS_X11::set_icon(const Ref<Image> &p_icon) { + int (*oldHandler)(Display *, XErrorEvent *) = XSetErrorHandler(&set_icon_errorhandler); + Atom net_wm_icon = XInternAtom(x11_display, "_NET_WM_ICON", False); if (p_icon.is_valid()) { Ref<Image> img = p_icon->duplicate(); img->convert(Image::FORMAT_RGBA8); - int w = img->get_width(); - int h = img->get_height(); + while (true) { + int w = img->get_width(); + int h = img->get_height(); + + if (g_set_icon_error) { + g_set_icon_error = false; + + WARN_PRINT("Icon too large, attempting to resize icon."); + + int new_width, new_height; + if (w > h) { + new_width = w / 2; + new_height = h * new_width / w; + } else { + new_height = h / 2; + new_width = w * new_height / h; + } - // We're using long to have wordsize (32Bit build -> 32 Bits, 64 Bit build -> 64 Bits - Vector<long> pd; + w = new_width; + h = new_height; + + if (!w || !h) { + WARN_PRINT("Unable to set icon."); + break; + } + + img->resize(w, h, Image::INTERPOLATE_CUBIC); + } - pd.resize(2 + w * h); + // We're using long to have wordsize (32Bit build -> 32 Bits, 64 Bit build -> 64 Bits + Vector<long> pd; - pd.write[0] = w; - pd.write[1] = h; + pd.resize(2 + w * h); - PoolVector<uint8_t>::Read r = img->get_data().read(); + pd.write[0] = w; + pd.write[1] = h; - long *wr = &pd.write[2]; - uint8_t const *pr = r.ptr(); + PoolVector<uint8_t>::Read r = img->get_data().read(); - for (int i = 0; i < w * h; i++) { - long v = 0; - // A R G B - v |= pr[3] << 24 | pr[0] << 16 | pr[1] << 8 | pr[2]; - *wr++ = v; - pr += 4; + long *wr = &pd.write[2]; + uint8_t const *pr = r.ptr(); + + for (int i = 0; i < w * h; i++) { + long v = 0; + // A R G B + v |= pr[3] << 24 | pr[0] << 16 | pr[1] << 8 | pr[2]; + *wr++ = v; + pr += 4; + } + + XChangeProperty(x11_display, x11_window, net_wm_icon, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)pd.ptr(), pd.size()); + + if (!g_set_icon_error) + break; } - XChangeProperty(x11_display, x11_window, net_wm_icon, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)pd.ptr(), pd.size()); } else { XDeleteProperty(x11_display, x11_window, net_wm_icon); } + XFlush(x11_display); + XSetErrorHandler(oldHandler); } void OS_X11::force_process_input() { @@ -2638,7 +2814,7 @@ void OS_X11::run() { #ifdef JOYDEV_ENABLED joypad->process_joypads(); #endif - if (Main::iteration() == true) + if (Main::iteration()) break; }; @@ -2826,6 +3002,25 @@ OS::LatinKeyboardVariant OS_X11::get_latin_keyboard_variant() const { return LATIN_KEYBOARD_QWERTY; } +void OS_X11::update_real_mouse_position() { + Window root_return, child_return; + int root_x, root_y, win_x, win_y; + unsigned int mask_return; + + Bool xquerypointer_result = XQueryPointer(x11_display, x11_window, &root_return, &child_return, &root_x, &root_y, + &win_x, &win_y, &mask_return); + + if (xquerypointer_result) { + if (win_x > 0 && win_y > 0 && win_x <= current_videomode.width && win_y <= current_videomode.height) { + + last_mouse_pos.x = win_x; + last_mouse_pos.y = win_y; + last_mouse_pos_valid = true; + input->set_mouse_position(last_mouse_pos); + } + } +} + OS_X11::OS_X11() { #ifdef PULSEAUDIO_ENABLED diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h index 44455a2d8d..bb8411e213 100644 --- a/platform/x11/os_x11.h +++ b/platform/x11/os_x11.h @@ -32,19 +32,19 @@ #define OS_X11_H #include "context_gl_x11.h" +#include "core/os/input.h" #include "crash_handler_x11.h" -#include "drivers/unix/os_unix.h" -#include "os/input.h" -#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 "drivers/unix/os_unix.h" #include "joypad_linux.h" #include "main/input_default.h" #include "power_x11.h" #include "servers/audio_server.h" #include "servers/visual/rasterizer.h" +#include "servers/visual_server.h" +//#include "servers/visual/visual_server_wrap_mt.h" #include <X11/Xcursor/Xcursor.h> #include <X11/Xlib.h> @@ -145,7 +145,6 @@ class OS_X11 : public OS_Unix { void handle_key_event(XKeyEvent *p_event, bool p_echo = false); void process_xevents(); virtual void delete_main_loop(); - IP_Unix *ip_unix; bool force_quit; bool minimized; @@ -177,8 +176,6 @@ class OS_X11 : public OS_Unix { AudioDriverPulseAudio driver_pulseaudio; #endif - Atom net_wm_icon; - PowerX11 *power_manager; bool layered_window; @@ -186,8 +183,6 @@ class OS_X11 : public OS_Unix { CrashHandler crash_handler; int video_driver_index; - int audio_driver_index; - unsigned int capture_idle; bool maximized; //void set_wm_border(bool p_enabled); void set_wm_fullscreen(bool p_enabled); @@ -313,6 +308,7 @@ public: virtual LatinKeyboardVariant get_latin_keyboard_variant() const; + void update_real_mouse_position(); OS_X11(); }; diff --git a/platform/x11/platform_config.h b/platform/x11/platform_config.h index b757be49c3..f6d7f5a8cd 100644 --- a/platform/x11/platform_config.h +++ b/platform/x11/platform_config.h @@ -36,5 +36,5 @@ #define PTHREAD_BSD_SET_NAME #endif -#define GLES3_INCLUDE_H "glad/glad.h" -#define GLES2_INCLUDE_H "glad/glad.h" +#define GLES3_INCLUDE_H "thirdparty/glad/glad/glad.h" +#define GLES2_INCLUDE_H "thirdparty/glad/glad/glad.h" diff --git a/platform/x11/power_x11.h b/platform/x11/power_x11.h index 4077887998..d0805b6f8a 100644 --- a/platform/x11/power_x11.h +++ b/platform/x11/power_x11.h @@ -31,9 +31,9 @@ #ifndef X11_POWER_H_ #define X11_POWER_H_ -#include "os/dir_access.h" -#include "os/file_access.h" -#include "os/os.h" +#include "core/os/dir_access.h" +#include "core/os/file_access.h" +#include "core/os/os.h" class PowerX11 { diff --git a/scene/2d/SCsub b/scene/2d/SCsub index bf9125be7f..b01e2fd54d 100644 --- a/scene/2d/SCsub +++ b/scene/2d/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.scene_sources, "*.cpp") - -Export('env') diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp index 85e7f8df92..8a5910c10e 100644 --- a/scene/2d/animated_sprite.cpp +++ b/scene/2d/animated_sprite.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "animated_sprite.h" -#include "os/os.h" +#include "core/os/os.h" #include "scene/scene_string_names.h" #define NORMAL_SUFFIX "_normal" @@ -395,15 +395,17 @@ void AnimatedSprite::_notification(int p_what) { int fc = frames->get_frame_count(animation); if (frame >= fc - 1) { if (frames->get_animation_loop(animation)) { + emit_signal(SceneStringNames::get_singleton()->animation_finished); frame = 0; } else { + if (!is_over) { + emit_signal(SceneStringNames::get_singleton()->animation_finished); + is_over = true; + } frame = fc - 1; } } else { frame++; - if (frame == fc - 1) { - emit_signal(SceneStringNames::get_singleton()->animation_finished); - } } update(); @@ -625,6 +627,7 @@ void AnimatedSprite::_reset_timeout() { return; timeout = _get_frame_duration(); + is_over = false; } void AnimatedSprite::set_animation(const StringName &p_animation) { @@ -712,4 +715,5 @@ AnimatedSprite::AnimatedSprite() { playing = false; animation = "default"; timeout = 0; + is_over = false; } diff --git a/scene/2d/animated_sprite.h b/scene/2d/animated_sprite.h index cc49465403..7270ee4d0e 100644 --- a/scene/2d/animated_sprite.h +++ b/scene/2d/animated_sprite.h @@ -135,6 +135,7 @@ class AnimatedSprite : public Node2D { bool centered; Point2 offset; + bool is_over; float timeout; bool hflip; diff --git a/scene/2d/area_2d.h b/scene/2d/area_2d.h index 0fda9b867d..cd60b6c1e1 100644 --- a/scene/2d/area_2d.h +++ b/scene/2d/area_2d.h @@ -31,8 +31,8 @@ #ifndef AREA_2D_H #define AREA_2D_H +#include "core/vset.h" #include "scene/2d/collision_object_2d.h" -#include "vset.h" class Area2D : public CollisionObject2D { diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp index 559e041dbf..9de72a4fcd 100644 --- a/scene/2d/audio_stream_player_2d.cpp +++ b/scene/2d/audio_stream_player_2d.cpp @@ -30,7 +30,7 @@ #include "audio_stream_player_2d.h" -#include "engine.h" +#include "core/engine.h" #include "scene/2d/area_2d.h" #include "scene/main/viewport.h" @@ -179,7 +179,7 @@ void AudioStreamPlayer2D::_notification(int p_what) { Physics2DDirectSpaceState::ShapeResult sr[MAX_INTERSECT_AREAS]; - int areas = space_state->intersect_point(global_pos, sr, MAX_INTERSECT_AREAS, Set<RID>(), area_mask); + int areas = space_state->intersect_point(global_pos, sr, MAX_INTERSECT_AREAS, Set<RID>(), area_mask, false, true); for (int i = 0; i < areas; i++) { diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp index 3b86ca76ea..788a39d05d 100644 --- a/scene/2d/camera_2d.cpp +++ b/scene/2d/camera_2d.cpp @@ -422,14 +422,14 @@ void Camera2D::clear_current() { void Camera2D::set_limit(Margin p_margin, int p_limit) { - ERR_FAIL_INDEX(p_margin, 4); + ERR_FAIL_INDEX((int)p_margin, 4); limit[p_margin] = p_limit; update(); } int Camera2D::get_limit(Margin p_margin) const { - ERR_FAIL_INDEX_V(p_margin, 4, 0); + ERR_FAIL_INDEX_V((int)p_margin, 4, 0); return limit[p_margin]; } @@ -446,14 +446,14 @@ bool Camera2D::is_limit_smoothing_enabled() const { void Camera2D::set_drag_margin(Margin p_margin, float p_drag_margin) { - ERR_FAIL_INDEX(p_margin, 4); + ERR_FAIL_INDEX((int)p_margin, 4); drag_margin[p_margin] = p_drag_margin; update(); } float Camera2D::get_drag_margin(Margin p_margin) const { - ERR_FAIL_INDEX_V(p_margin, 4, 0); + ERR_FAIL_INDEX_V((int)p_margin, 4, 0); return drag_margin[p_margin]; } diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp index f4b5d54cb2..8dc2f57685 100644 --- a/scene/2d/canvas_item.cpp +++ b/scene/2d/canvas_item.cpp @@ -29,9 +29,9 @@ /*************************************************************************/ #include "canvas_item.h" +#include "core/message_queue.h" #include "core/method_bind_ext.gen.inc" -#include "message_queue.h" -#include "os/input.h" +#include "core/os/input.h" #include "scene/main/canvas_layer.h" #include "scene/main/viewport.h" #include "scene/resources/font.h" @@ -44,12 +44,19 @@ Mutex *CanvasItemMaterial::material_mutex = NULL; SelfList<CanvasItemMaterial>::List CanvasItemMaterial::dirty_materials; Map<CanvasItemMaterial::MaterialKey, CanvasItemMaterial::ShaderData> CanvasItemMaterial::shader_map; +CanvasItemMaterial::ShaderNames *CanvasItemMaterial::shader_names = NULL; void CanvasItemMaterial::init_shaders() { #ifndef NO_THREADS material_mutex = Mutex::create(); #endif + + shader_names = memnew(ShaderNames); + + shader_names->particles_anim_h_frames = "particles_anim_h_frames"; + shader_names->particles_anim_v_frames = "particles_anim_v_frames"; + shader_names->particles_anim_loop = "particles_anim_loop"; } void CanvasItemMaterial::finish_shaders() { @@ -102,7 +109,37 @@ void CanvasItemMaterial::_update_shader() { case LIGHT_MODE_UNSHADED: code += ",unshaded"; break; case LIGHT_MODE_LIGHT_ONLY: code += ",light_only"; break; } - code += ";\n"; //that's it. + + code += ";\n"; + + if (particles_animation) { + + code += "uniform int particles_anim_h_frames;\n"; + code += "uniform int particles_anim_v_frames;\n"; + code += "uniform bool particles_anim_loop;\n"; + + code += "void vertex() {\n"; + + code += "\tfloat h_frames = float(particles_anim_h_frames);\n"; + code += "\tfloat v_frames = float(particles_anim_v_frames);\n"; + + code += "\tVERTEX.xy /= TEXTURE_PIXEL_SIZE * vec2(h_frames, v_frames);\n"; + + code += "\tint total_frames = particles_anim_h_frames * particles_anim_v_frames;\n"; + code += "\tint frame = int(float(total_frames) * INSTANCE_CUSTOM.z);\n"; + code += "\tif (particles_anim_loop) {\n"; + code += "\t\tframe = abs(frame) % total_frames;\n"; + code += "\t} else {\n"; + code += "\t\tframe = clamp(frame, 0, total_frames - 1);\n"; + code += "\t}\n"; + + code += "\tfloat frame_w = 1.0 / h_frames;\n"; + code += "\tfloat frame_h = 1.0 / v_frames;\n"; + code += "\tUV.x = UV.x * frame_w + frame_w * float(frame % particles_anim_h_frames);\n"; + code += "\tUV.y = UV.y * frame_h + frame_h * float(frame / particles_anim_v_frames);\n"; + + code += "}\n"; + } ShaderData shader_data; shader_data.shader = VS::get_singleton()->shader_create(); @@ -177,7 +214,52 @@ CanvasItemMaterial::LightMode CanvasItemMaterial::get_light_mode() const { return light_mode; } +void CanvasItemMaterial::set_particles_animation(bool p_particles_anim) { + particles_animation = p_particles_anim; + _queue_shader_change(); + _change_notify(); +} + +bool CanvasItemMaterial::get_particles_animation() const { + return particles_animation; +} + +void CanvasItemMaterial::set_particles_anim_h_frames(int p_frames) { + + particles_anim_h_frames = p_frames; + VS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_h_frames, p_frames); +} + +int CanvasItemMaterial::get_particles_anim_h_frames() const { + + return particles_anim_h_frames; +} +void CanvasItemMaterial::set_particles_anim_v_frames(int p_frames) { + + particles_anim_v_frames = p_frames; + VS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_v_frames, p_frames); +} + +int CanvasItemMaterial::get_particles_anim_v_frames() const { + + return particles_anim_v_frames; +} + +void CanvasItemMaterial::set_particles_anim_loop(bool p_loop) { + + particles_anim_loop = p_loop; + VS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_loop, particles_anim_loop); +} + +bool CanvasItemMaterial::get_particles_anim_loop() const { + + return particles_anim_loop; +} + void CanvasItemMaterial::_validate_property(PropertyInfo &property) const { + if (property.name.begins_with("particles_anim_") && !particles_animation) { + property.usage = 0; + } } RID CanvasItemMaterial::get_shader_rid() const { @@ -199,8 +281,25 @@ void CanvasItemMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_light_mode", "light_mode"), &CanvasItemMaterial::set_light_mode); ClassDB::bind_method(D_METHOD("get_light_mode"), &CanvasItemMaterial::get_light_mode); + ClassDB::bind_method(D_METHOD("set_particles_animation", "particles_anim"), &CanvasItemMaterial::set_particles_animation); + ClassDB::bind_method(D_METHOD("get_particles_animation"), &CanvasItemMaterial::get_particles_animation); + + ClassDB::bind_method(D_METHOD("set_particles_anim_h_frames", "frames"), &CanvasItemMaterial::set_particles_anim_h_frames); + ClassDB::bind_method(D_METHOD("get_particles_anim_h_frames"), &CanvasItemMaterial::get_particles_anim_h_frames); + + ClassDB::bind_method(D_METHOD("set_particles_anim_v_frames", "frames"), &CanvasItemMaterial::set_particles_anim_v_frames); + ClassDB::bind_method(D_METHOD("get_particles_anim_v_frames"), &CanvasItemMaterial::get_particles_anim_v_frames); + + ClassDB::bind_method(D_METHOD("set_particles_anim_loop", "loop"), &CanvasItemMaterial::set_particles_anim_loop); + ClassDB::bind_method(D_METHOD("get_particles_anim_loop"), &CanvasItemMaterial::get_particles_anim_loop); + ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Mix,Add,Sub,Mul,Premult Alpha"), "set_blend_mode", "get_blend_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mode", PROPERTY_HINT_ENUM, "Normal,Unshaded,Light Only"), "set_light_mode", "get_light_mode"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "particles_animation"), "set_particles_animation", "get_particles_animation"); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "particles_anim_h_frames", PROPERTY_HINT_RANGE, "1,128,1"), "set_particles_anim_h_frames", "get_particles_anim_h_frames"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "particles_anim_v_frames", PROPERTY_HINT_RANGE, "1,128,1"), "set_particles_anim_v_frames", "get_particles_anim_v_frames"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "particles_anim_loop"), "set_particles_anim_loop", "get_particles_anim_loop"); BIND_ENUM_CONSTANT(BLEND_MODE_MIX); BIND_ENUM_CONSTANT(BLEND_MODE_ADD); @@ -218,6 +317,11 @@ CanvasItemMaterial::CanvasItemMaterial() : blend_mode = BLEND_MODE_MIX; light_mode = LIGHT_MODE_NORMAL; + particles_animation = false; + + set_particles_anim_h_frames(1); + set_particles_anim_v_frames(1); + set_particles_anim_loop(false); current_key.key = 0; current_key.invalid_key = 1; @@ -976,6 +1080,17 @@ Vector2 CanvasItem::get_local_mouse_position() const { return get_global_transform().affine_inverse().xform(get_global_mouse_position()); } +void CanvasItem::force_update_transform() { + ERR_FAIL_COND(!is_inside_tree()); + if (!xform_change.in_list()) { + return; + } + + get_tree()->xform_change_list.remove(&xform_change); + + notification(NOTIFICATION_TRANSFORM_CHANGED); +} + void CanvasItem::_bind_methods() { ClassDB::bind_method(D_METHOD("_toplevel_raise_self"), &CanvasItem::_toplevel_raise_self); @@ -1070,6 +1185,8 @@ void CanvasItem::_bind_methods() { ClassDB::bind_method(D_METHOD("set_notify_transform", "enable"), &CanvasItem::set_notify_transform); ClassDB::bind_method(D_METHOD("is_transform_notification_enabled"), &CanvasItem::is_transform_notification_enabled); + ClassDB::bind_method(D_METHOD("force_update_transform"), &CanvasItem::force_update_transform); + ClassDB::bind_method(D_METHOD("make_canvas_position_local", "screen_point"), &CanvasItem::make_canvas_position_local); ClassDB::bind_method(D_METHOD("make_input_local", "event"), &CanvasItem::make_input_local); diff --git a/scene/2d/canvas_item.h b/scene/2d/canvas_item.h index 616f4016bf..9fe7cb1e00 100644 --- a/scene/2d/canvas_item.h +++ b/scene/2d/canvas_item.h @@ -70,6 +70,7 @@ private: struct { uint32_t blend_mode : 4; uint32_t light_mode : 4; + uint32_t particles_animation : 1; uint32_t invalid_key : 1; }; @@ -80,6 +81,14 @@ private: } }; + struct ShaderNames { + StringName particles_anim_h_frames; + StringName particles_anim_v_frames; + StringName particles_anim_loop; + }; + + static ShaderNames *shader_names; + struct ShaderData { RID shader; int users; @@ -95,6 +104,7 @@ private: mk.key = 0; mk.blend_mode = blend_mode; mk.light_mode = light_mode; + mk.particles_animation = particles_animation; return mk; } @@ -108,6 +118,11 @@ private: BlendMode blend_mode; LightMode light_mode; + bool particles_animation; + + int particles_anim_h_frames; + int particles_anim_v_frames; + bool particles_anim_loop; protected: static void _bind_methods(); @@ -120,6 +135,17 @@ public: void set_light_mode(LightMode p_light_mode); LightMode get_light_mode() const; + void set_particles_animation(bool p_particles_anim); + bool get_particles_animation() const; + + void set_particles_anim_h_frames(int p_frames); + int get_particles_anim_h_frames() const; + void set_particles_anim_v_frames(int p_frames); + int get_particles_anim_v_frames() const; + + void set_particles_anim_loop(bool p_frames); + bool get_particles_anim_loop() const; + static void init_shaders(); static void finish_shaders(); static void flush_changes(); @@ -347,7 +373,9 @@ public: void set_notify_transform(bool p_enable); bool is_transform_notification_enabled() const; - // Used by control nodes to retreive the parent's anchorable area + void force_update_transform(); + + // Used by control nodes to retrieve the parent's anchorable area virtual Rect2 get_anchorable_rect() const { return Rect2(0, 0, 0, 0); }; int get_canvas_layer() const; diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp index f4213de2ca..738f7ddf59 100644 --- a/scene/2d/collision_object_2d.cpp +++ b/scene/2d/collision_object_2d.cpp @@ -400,7 +400,7 @@ void CollisionObject2D::_bind_methods() { BIND_VMETHOD(MethodInfo("_input_event", PropertyInfo(Variant::OBJECT, "viewport"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), PropertyInfo(Variant::INT, "shape_idx"))); - ADD_SIGNAL(MethodInfo("input_event", PropertyInfo(Variant::OBJECT, "viewport"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), PropertyInfo(Variant::INT, "shape_idx"))); + ADD_SIGNAL(MethodInfo("input_event", PropertyInfo(Variant::OBJECT, "viewport", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), PropertyInfo(Variant::INT, "shape_idx"))); ADD_SIGNAL(MethodInfo("mouse_entered")); ADD_SIGNAL(MethodInfo("mouse_exited")); diff --git a/scene/2d/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp index 9f19f56e75..410b27c691 100644 --- a/scene/2d/collision_polygon_2d.cpp +++ b/scene/2d/collision_polygon_2d.cpp @@ -31,7 +31,7 @@ #include "collision_polygon_2d.h" #include "collision_object_2d.h" -#include "engine.h" +#include "core/engine.h" #include "scene/resources/concave_polygon_shape_2d.h" #include "scene/resources/convex_polygon_shape_2d.h" @@ -247,7 +247,7 @@ Vector<Point2> CollisionPolygon2D::get_polygon() const { void CollisionPolygon2D::set_build_mode(BuildMode p_mode) { - ERR_FAIL_INDEX(p_mode, 2); + ERR_FAIL_INDEX((int)p_mode, 2); build_mode = p_mode; if (parent) { _build_polygon(); diff --git a/scene/2d/collision_shape_2d.cpp b/scene/2d/collision_shape_2d.cpp index ff5f7062c4..cb9c8ecf95 100644 --- a/scene/2d/collision_shape_2d.cpp +++ b/scene/2d/collision_shape_2d.cpp @@ -31,7 +31,7 @@ #include "collision_shape_2d.h" #include "collision_object_2d.h" -#include "engine.h" +#include "core/engine.h" #include "scene/resources/capsule_shape_2d.h" #include "scene/resources/circle_shape_2d.h" #include "scene/resources/concave_polygon_shape_2d.h" diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp new file mode 100644 index 0000000000..b6e2f4f5cd --- /dev/null +++ b/scene/2d/cpu_particles_2d.cpp @@ -0,0 +1,1409 @@ +/*************************************************************************/ +/* cpu_particles_2d.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 "cpu_particles_2d.h" +#include "particles_2d.h" +#include "scene/2d/canvas_item.h" +#include "scene/resources/particles_material.h" +#include "servers/visual_server.h" + +void CPUParticles2D::set_emitting(bool p_emitting) { + + emitting = p_emitting; + if (!is_processing_internal()) { + set_process_internal(true); + if (is_inside_tree()) { +#ifndef NO_THREADS + update_mutex->lock(); +#endif + VS::get_singleton()->connect("frame_pre_draw", this, "_update_render_thread"); + VS::get_singleton()->canvas_item_set_update_when_visible(get_canvas_item(), true); + +#ifndef NO_THREADS + update_mutex->unlock(); +#endif + } + } +} + +void CPUParticles2D::set_amount(int p_amount) { + + ERR_FAIL_COND(p_amount < 1); + + particles.resize(p_amount); + { + PoolVector<Particle>::Write w = particles.write(); + + for (int i = 0; i < p_amount; i++) { + w[i].active = false; + } + } + + particle_data.resize((8 + 4 + 1) * p_amount); + VS::get_singleton()->multimesh_allocate(multimesh, p_amount, VS::MULTIMESH_TRANSFORM_2D, VS::MULTIMESH_COLOR_8BIT, VS::MULTIMESH_CUSTOM_DATA_FLOAT); + + particle_order.resize(p_amount); +} +void CPUParticles2D::set_lifetime(float p_lifetime) { + + ERR_FAIL_COND(p_lifetime <= 0); + lifetime = p_lifetime; +} + +void CPUParticles2D::set_one_shot(bool p_one_shot) { + + one_shot = p_one_shot; +} + +void CPUParticles2D::set_pre_process_time(float p_time) { + + pre_process_time = p_time; +} +void CPUParticles2D::set_explosiveness_ratio(float p_ratio) { + + explosiveness_ratio = p_ratio; +} +void CPUParticles2D::set_randomness_ratio(float p_ratio) { + + randomness_ratio = p_ratio; +} +void CPUParticles2D::set_use_local_coordinates(bool p_enable) { + + local_coords = p_enable; +} +void CPUParticles2D::set_speed_scale(float p_scale) { + + speed_scale = p_scale; +} + +bool CPUParticles2D::is_emitting() const { + + return emitting; +} +int CPUParticles2D::get_amount() const { + + return particles.size(); +} +float CPUParticles2D::get_lifetime() const { + + return lifetime; +} +bool CPUParticles2D::get_one_shot() const { + + return one_shot; +} + +float CPUParticles2D::get_pre_process_time() const { + + return pre_process_time; +} +float CPUParticles2D::get_explosiveness_ratio() const { + + return explosiveness_ratio; +} +float CPUParticles2D::get_randomness_ratio() const { + + return randomness_ratio; +} + +bool CPUParticles2D::get_use_local_coordinates() const { + + return local_coords; +} + +float CPUParticles2D::get_speed_scale() const { + + return speed_scale; +} + +void CPUParticles2D::set_draw_order(DrawOrder p_order) { + + draw_order = p_order; +} + +CPUParticles2D::DrawOrder CPUParticles2D::get_draw_order() const { + + return draw_order; +} + +void CPUParticles2D::_generate_mesh_texture() { + + PoolVector<Vector2> vertices; + vertices.push_back(Vector2(-0.5, -0.5)); + vertices.push_back(Vector2(0.5, -0.5)); + vertices.push_back(Vector2(0.5, 0.5)); + vertices.push_back(Vector2(-0.5, 0.5)); + PoolVector<Vector2> uvs; + uvs.push_back(Vector2(0, 0)); + uvs.push_back(Vector2(1, 0)); + uvs.push_back(Vector2(1, 1)); + uvs.push_back(Vector2(0, 1)); + PoolVector<Color> colors; + colors.push_back(Color(1, 1, 1, 1)); + colors.push_back(Color(1, 1, 1, 1)); + colors.push_back(Color(1, 1, 1, 1)); + colors.push_back(Color(1, 1, 1, 1)); + PoolVector<int> indices; + indices.push_back(0); + indices.push_back(1); + indices.push_back(2); + indices.push_back(2); + indices.push_back(3); + indices.push_back(0); + + Array arr; + arr.resize(VS::ARRAY_MAX); + arr[VS::ARRAY_VERTEX] = vertices; + arr[VS::ARRAY_TEX_UV] = uvs; + arr[VS::ARRAY_COLOR] = colors; + arr[VS::ARRAY_INDEX] = indices; + + VS::get_singleton()->mesh_clear(mesh); + VS::get_singleton()->mesh_add_surface_from_arrays(mesh, VS::PRIMITIVE_TRIANGLES, arr); +} + +void CPUParticles2D::set_texture(const Ref<Texture> &p_texture) { + + texture = p_texture; + update(); +} + +Ref<Texture> CPUParticles2D::get_texture() const { + + return texture; +} + +void CPUParticles2D::set_normalmap(const Ref<Texture> &p_normalmap) { + + normalmap = p_normalmap; + update(); +} + +Ref<Texture> CPUParticles2D::get_normalmap() const { + + return normalmap; +} + +void CPUParticles2D::set_fixed_fps(int p_count) { + fixed_fps = p_count; +} + +int CPUParticles2D::get_fixed_fps() const { + return fixed_fps; +} + +void CPUParticles2D::set_fractional_delta(bool p_enable) { + fractional_delta = p_enable; +} + +bool CPUParticles2D::get_fractional_delta() const { + return fractional_delta; +} + +String CPUParticles2D::get_configuration_warning() const { + + String warnings; + + CanvasItemMaterial *mat = Object::cast_to<CanvasItemMaterial>(get_material().ptr()); + + if (get_material().is_null() || (mat && !mat->get_particles_animation())) { + if (warnings != String()) + warnings += "\n"; + warnings += "- " + TTR("CPUParticles2D animation requires the usage of a CanvasItemMaterial with \"Particles Animation\" enabled."); + } + + return warnings; +} + +void CPUParticles2D::restart() { + + time = 0; + inactive_time = 0; + frame_remainder = 0; + cycle = 0; + + { + int pc = particles.size(); + PoolVector<Particle>::Write w = particles.write(); + + for (int i = 0; i < pc; i++) { + w[i].active = false; + } + } +} + +void CPUParticles2D::set_spread(float p_spread) { + + spread = p_spread; +} + +float CPUParticles2D::get_spread() const { + + return spread; +} + +void CPUParticles2D::set_flatness(float p_flatness) { + + flatness = p_flatness; +} +float CPUParticles2D::get_flatness() const { + + return flatness; +} + +void CPUParticles2D::set_param(Parameter p_param, float p_value) { + + ERR_FAIL_INDEX(p_param, PARAM_MAX); + + parameters[p_param] = p_value; +} +float CPUParticles2D::get_param(Parameter p_param) const { + + ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0); + + return parameters[p_param]; +} + +void CPUParticles2D::set_param_randomness(Parameter p_param, float p_value) { + + ERR_FAIL_INDEX(p_param, PARAM_MAX); + + randomness[p_param] = p_value; +} +float CPUParticles2D::get_param_randomness(Parameter p_param) const { + + ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0); + + return randomness[p_param]; +} + +static void _adjust_curve_range(const Ref<Curve> &p_curve, float p_min, float p_max) { + + Ref<Curve> curve = p_curve; + if (!curve.is_valid()) + return; + + curve->ensure_default_setup(p_min, p_max); +} + +void CPUParticles2D::set_param_curve(Parameter p_param, const Ref<Curve> &p_curve) { + + ERR_FAIL_INDEX(p_param, PARAM_MAX); + + curve_parameters[p_param] = p_curve; + + switch (p_param) { + case PARAM_INITIAL_LINEAR_VELOCITY: { + //do none for this one + } break; + case PARAM_ANGULAR_VELOCITY: { + _adjust_curve_range(p_curve, -360, 360); + } break; + /*case PARAM_ORBIT_VELOCITY: { + _adjust_curve_range(p_curve, -500, 500); + } break;*/ + case PARAM_LINEAR_ACCEL: { + _adjust_curve_range(p_curve, -200, 200); + } break; + case PARAM_RADIAL_ACCEL: { + _adjust_curve_range(p_curve, -200, 200); + } break; + case PARAM_TANGENTIAL_ACCEL: { + _adjust_curve_range(p_curve, -200, 200); + } break; + case PARAM_DAMPING: { + _adjust_curve_range(p_curve, 0, 100); + } break; + case PARAM_ANGLE: { + _adjust_curve_range(p_curve, -360, 360); + } break; + case PARAM_SCALE: { + + } break; + case PARAM_HUE_VARIATION: { + _adjust_curve_range(p_curve, -1, 1); + } break; + case PARAM_ANIM_SPEED: { + _adjust_curve_range(p_curve, 0, 200); + } break; + case PARAM_ANIM_OFFSET: { + } break; + default: {} + } +} +Ref<Curve> CPUParticles2D::get_param_curve(Parameter p_param) const { + + ERR_FAIL_INDEX_V(p_param, PARAM_MAX, Ref<Curve>()); + + return curve_parameters[p_param]; +} + +void CPUParticles2D::set_color(const Color &p_color) { + + color = p_color; +} + +Color CPUParticles2D::get_color() const { + + return color; +} + +void CPUParticles2D::set_color_ramp(const Ref<Gradient> &p_ramp) { + + color_ramp = p_ramp; +} + +Ref<Gradient> CPUParticles2D::get_color_ramp() const { + + return color_ramp; +} + +void CPUParticles2D::set_particle_flag(Flags p_flag, bool p_enable) { + ERR_FAIL_INDEX(p_flag, FLAG_MAX); + flags[p_flag] = p_enable; +} + +bool CPUParticles2D::get_particle_flag(Flags p_flag) const { + ERR_FAIL_INDEX_V(p_flag, FLAG_MAX, false); + return flags[p_flag]; +} + +void CPUParticles2D::set_emission_shape(EmissionShape p_shape) { + + emission_shape = p_shape; + _change_notify(); +} + +void CPUParticles2D::set_emission_sphere_radius(float p_radius) { + + emission_sphere_radius = p_radius; +} + +void CPUParticles2D::set_emission_rect_extents(Vector2 p_extents) { + + emission_rect_extents = p_extents; +} + +void CPUParticles2D::set_emission_points(const PoolVector<Vector2> &p_points) { + + emission_points = p_points; +} + +void CPUParticles2D::set_emission_normals(const PoolVector<Vector2> &p_normals) { + + emission_normals = p_normals; +} + +void CPUParticles2D::set_emission_colors(const PoolVector<Color> &p_colors) { + + emission_colors = p_colors; +} + +float CPUParticles2D::get_emission_sphere_radius() const { + + return emission_sphere_radius; +} +Vector2 CPUParticles2D::get_emission_rect_extents() const { + + return emission_rect_extents; +} +PoolVector<Vector2> CPUParticles2D::get_emission_points() const { + + return emission_points; +} +PoolVector<Vector2> CPUParticles2D::get_emission_normals() const { + + return emission_normals; +} + +PoolVector<Color> CPUParticles2D::get_emission_colors() const { + + return emission_colors; +} + +CPUParticles2D::EmissionShape CPUParticles2D::get_emission_shape() const { + return emission_shape; +} +void CPUParticles2D::set_gravity(const Vector2 &p_gravity) { + + gravity = p_gravity; +} + +Vector2 CPUParticles2D::get_gravity() const { + + return gravity; +} + +void CPUParticles2D::_validate_property(PropertyInfo &property) const { + + if (property.name == "color" && color_ramp.is_valid()) { + property.usage = 0; + } + + if (property.name == "emission_sphere_radius" && emission_shape != EMISSION_SHAPE_CIRCLE) { + property.usage = 0; + } + + if (property.name == "emission_rect_extents" && emission_shape != EMISSION_SHAPE_RECTANGLE) { + property.usage = 0; + } + + if ((property.name == "emission_point_texture" || property.name == "emission_color_texture") && (emission_shape < EMISSION_SHAPE_POINTS)) { + property.usage = 0; + } + + if (property.name == "emission_normals" && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) { + property.usage = 0; + } + + if (property.name == "emission_points" && emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) { + property.usage = 0; + } + + if (property.name == "emission_colors" && emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) { + property.usage = 0; + } + + /* + if (property.name.begins_with("orbit_") && !flags[FLAG_DISABLE_Z]) { + property.usage = 0; + } + */ +} + +static uint32_t idhash(uint32_t x) { + + x = ((x >> uint32_t(16)) ^ x) * uint32_t(0x45d9f3b); + x = ((x >> uint32_t(16)) ^ x) * uint32_t(0x45d9f3b); + x = (x >> uint32_t(16)) ^ x; + return x; +} + +static float rand_from_seed(uint32_t &seed) { + int k; + int s = int(seed); + if (s == 0) + s = 305420679; + k = s / 127773; + s = 16807 * (s - k * 127773) - 2836 * k; + if (s < 0) + s += 2147483647; + seed = uint32_t(s); + return float(seed % uint32_t(65536)) / 65535.0; +} + +void CPUParticles2D::_particles_process(float p_delta) { + + p_delta *= speed_scale; + + int pcount = particles.size(); + PoolVector<Particle>::Write w = particles.write(); + + Particle *parray = w.ptr(); + + float prev_time = time; + time += p_delta; + if (time > lifetime) { + time = Math::fmod(time, lifetime); + cycle++; + if (one_shot && cycle > 0) { + emitting = false; + } + } + + Transform2D emission_xform; + Transform2D velocity_xform; + if (!local_coords) { + emission_xform = get_global_transform(); + velocity_xform = emission_xform; + velocity_xform[2] = Vector2(); + } + + for (int i = 0; i < pcount; i++) { + + Particle &p = parray[i]; + + if (!emitting && !p.active) + continue; + + float restart_time = (float(i) / float(pcount)) * lifetime; + float local_delta = p_delta; + + if (randomness_ratio > 0.0) { + uint32_t seed = cycle; + if (restart_time >= time) { + seed -= uint32_t(1); + } + seed *= uint32_t(pcount); + seed += uint32_t(i); + float random = float(idhash(seed) % uint32_t(65536)) / 65536.0; + restart_time += randomness_ratio * random * 1.0 / float(pcount); + } + + restart_time *= (1.0 - explosiveness_ratio); + bool restart = false; + + if (time > prev_time) { + // restart_time >= prev_time is used so particles emit in the first frame they are processed + + if (restart_time >= prev_time && restart_time < time) { + restart = true; + if (fractional_delta) { + local_delta = (time - restart_time) * lifetime; + } + } + + } else if (local_delta > 0.0) { + if (restart_time >= prev_time) { + restart = true; + if (fractional_delta) { + local_delta = (1.0 - restart_time + time) * lifetime; + } + + } else if (restart_time < time) { + restart = true; + if (fractional_delta) { + local_delta = (time - restart_time) * lifetime; + } + } + } + + if (restart) { + + if (!emitting) { + p.active = false; + continue; + } + p.active = true; + + /*float tex_linear_velocity = 0; + if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { + tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->interpolate(0); + }*/ + + float tex_angle = 0.0; + if (curve_parameters[PARAM_ANGLE].is_valid()) { + tex_angle = curve_parameters[PARAM_ANGLE]->interpolate(0); + } + + float tex_anim_offset = 0.0; + if (curve_parameters[PARAM_ANGLE].is_valid()) { + tex_anim_offset = curve_parameters[PARAM_ANGLE]->interpolate(0); + } + + p.seed = Math::rand(); + + p.angle_rand = Math::randf(); + p.scale_rand = Math::randf(); + p.hue_rot_rand = Math::randf(); + p.anim_offset_rand = Math::randf(); + + float angle1_rad = (Math::randf() * 2.0 - 1.0) * Math_PI * spread / 180.0; + Vector2 rot = Vector2(Math::cos(angle1_rad), Math::sin(angle1_rad)); + p.velocity = rot * parameters[PARAM_INITIAL_LINEAR_VELOCITY] * Math::lerp(1.0f, float(Math::randf()), randomness[PARAM_INITIAL_LINEAR_VELOCITY]); + + float base_angle = (parameters[PARAM_ANGLE] + tex_angle) * Math::lerp(1.0f, p.angle_rand, randomness[PARAM_ANGLE]); + p.rotation = Math::deg2rad(base_angle); + + p.custom[0] = 0.0; // unused + p.custom[1] = 0.0; // phase [0..1] + p.custom[2] = (parameters[PARAM_ANIM_OFFSET] + tex_anim_offset) * Math::lerp(1.0f, p.anim_offset_rand, randomness[PARAM_ANIM_OFFSET]); //animation phase [0..1] + p.custom[3] = 0.0; + p.transform = Transform2D(); + p.time = 0; + p.base_color = Color(1, 1, 1, 1); + + switch (emission_shape) { + case EMISSION_SHAPE_POINT: { + //do none + } break; + case EMISSION_SHAPE_CIRCLE: { + p.transform[2] = Vector2(Math::randf() * 2.0 - 1.0, Math::randf() * 2.0 - 1.0).normalized() * emission_sphere_radius; + } break; + case EMISSION_SHAPE_RECTANGLE: { + p.transform[2] = Vector2(Math::randf() * 2.0 - 1.0, Math::randf() * 2.0 - 1.0) * emission_rect_extents; + } break; + case EMISSION_SHAPE_POINTS: + case EMISSION_SHAPE_DIRECTED_POINTS: { + + int pc = emission_points.size(); + if (pc == 0) + break; + + int random_idx = Math::rand() % pc; + + p.transform[2] = emission_points.get(random_idx); + + if (emission_shape == EMISSION_SHAPE_DIRECTED_POINTS && emission_normals.size() == pc) { + p.velocity = emission_normals.get(random_idx); + } + + if (emission_colors.size() == pc) { + p.base_color = emission_colors.get(random_idx); + } + } break; + } + + if (!local_coords) { + p.velocity = velocity_xform.xform(p.velocity); + p.transform = emission_xform * p.transform; + } + + } else if (!p.active) { + continue; + } else { + + uint32_t alt_seed = p.seed; + + p.time += local_delta; + p.custom[1] = p.time / lifetime; + + float tex_linear_velocity = 0.0; + if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { + tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->interpolate(p.custom[1]); + } + /* + float tex_orbit_velocity = 0.0; + + if (flags[FLAG_DISABLE_Z]) { + + if (curve_parameters[PARAM_INITIAL_ORBIT_VELOCITY].is_valid()) { + tex_orbit_velocity = curve_parameters[PARAM_INITIAL_ORBIT_VELOCITY]->interpolate(p.custom[1]); + } + } +*/ + float tex_angular_velocity = 0.0; + if (curve_parameters[PARAM_ANGULAR_VELOCITY].is_valid()) { + tex_angular_velocity = curve_parameters[PARAM_ANGULAR_VELOCITY]->interpolate(p.custom[1]); + } + + float tex_linear_accel = 0.0; + if (curve_parameters[PARAM_LINEAR_ACCEL].is_valid()) { + tex_linear_accel = curve_parameters[PARAM_LINEAR_ACCEL]->interpolate(p.custom[1]); + } + + float tex_tangential_accel = 0.0; + if (curve_parameters[PARAM_TANGENTIAL_ACCEL].is_valid()) { + tex_tangential_accel = curve_parameters[PARAM_TANGENTIAL_ACCEL]->interpolate(p.custom[1]); + } + + float tex_radial_accel = 0.0; + if (curve_parameters[PARAM_RADIAL_ACCEL].is_valid()) { + tex_radial_accel = curve_parameters[PARAM_RADIAL_ACCEL]->interpolate(p.custom[1]); + } + + float tex_damping = 0.0; + if (curve_parameters[PARAM_DAMPING].is_valid()) { + tex_damping = curve_parameters[PARAM_DAMPING]->interpolate(p.custom[1]); + } + + float tex_angle = 0.0; + if (curve_parameters[PARAM_ANGLE].is_valid()) { + tex_angle = curve_parameters[PARAM_ANGLE]->interpolate(p.custom[1]); + } + float tex_anim_speed = 0.0; + if (curve_parameters[PARAM_ANIM_SPEED].is_valid()) { + tex_anim_speed = curve_parameters[PARAM_ANIM_SPEED]->interpolate(p.custom[1]); + } + + float tex_anim_offset = 0.0; + if (curve_parameters[PARAM_ANIM_OFFSET].is_valid()) { + tex_anim_offset = curve_parameters[PARAM_ANIM_OFFSET]->interpolate(p.custom[1]); + } + + Vector2 force = gravity; + Vector2 pos = p.transform[2]; + + //apply linear acceleration + force += p.velocity.length() > 0.0 ? p.velocity.normalized() * (parameters[PARAM_LINEAR_ACCEL] + tex_linear_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_LINEAR_ACCEL]) : Vector2(); + //apply radial acceleration + Vector2 org = emission_xform[2]; + Vector2 diff = pos - org; + force += diff.length() > 0.0 ? diff.normalized() * (parameters[PARAM_RADIAL_ACCEL] + tex_radial_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_RADIAL_ACCEL]) : Vector2(); + //apply tangential acceleration; + Vector2 yx = Vector2(diff.y, diff.x); + force += yx.length() > 0.0 ? (yx * Vector2(-1.0, 1.0)) * ((parameters[PARAM_TANGENTIAL_ACCEL] + tex_tangential_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_TANGENTIAL_ACCEL])) : Vector2(); + //apply attractor forces + p.velocity += force * local_delta; + //orbit velocity +#if 0 + if (flags[FLAG_DISABLE_Z]) { + + float orbit_amount = (orbit_velocity + tex_orbit_velocity) * mix(1.0, rand_from_seed(alt_seed), orbit_velocity_random); + if (orbit_amount != 0.0) { + float ang = orbit_amount * DELTA * pi * 2.0; + mat2 rot = mat2(vec2(cos(ang), -sin(ang)), vec2(sin(ang), cos(ang))); + TRANSFORM[3].xy -= diff.xy; + TRANSFORM[3].xy += rot * diff.xy; + } + } +#endif + if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { + p.velocity = p.velocity.normalized() * tex_linear_velocity; + } + + if (parameters[PARAM_DAMPING] + tex_damping > 0.0) { + + float v = p.velocity.length(); + float damp = (parameters[PARAM_DAMPING] + tex_damping) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_DAMPING]); + v -= damp * local_delta; + if (v < 0.0) { + p.velocity = Vector2(); + } else { + p.velocity = p.velocity.normalized() * v; + } + } + float base_angle = (parameters[PARAM_ANGLE] + tex_angle) * Math::lerp(1.0f, p.angle_rand, randomness[PARAM_ANGLE]); + base_angle += p.custom[1] * lifetime * (parameters[PARAM_ANGULAR_VELOCITY] + tex_angular_velocity) * Math::lerp(1.0f, rand_from_seed(alt_seed) * 2.0f - 1.0f, randomness[PARAM_ANGULAR_VELOCITY]); + p.rotation = Math::deg2rad(base_angle); //angle + float animation_phase = (parameters[PARAM_ANIM_OFFSET] + tex_anim_offset) * Math::lerp(1.0f, p.anim_offset_rand, randomness[PARAM_ANIM_OFFSET]) + p.custom[1] * (parameters[PARAM_ANIM_SPEED] + tex_anim_speed) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_ANIM_SPEED]); + p.custom[2] = animation_phase; + } + //apply color + //apply hue rotation + + float tex_scale = 1.0; + if (curve_parameters[PARAM_SCALE].is_valid()) { + tex_scale = curve_parameters[PARAM_SCALE]->interpolate(p.custom[1]); + } + + float tex_hue_variation = 0.0; + if (curve_parameters[PARAM_HUE_VARIATION].is_valid()) { + tex_hue_variation = curve_parameters[PARAM_HUE_VARIATION]->interpolate(p.custom[1]); + } + + float hue_rot_angle = (parameters[PARAM_HUE_VARIATION] + tex_hue_variation) * Math_PI * 2.0 * Math::lerp(1.0f, p.hue_rot_rand * 2.0f - 1.0f, randomness[PARAM_HUE_VARIATION]); + float hue_rot_c = Math::cos(hue_rot_angle); + float hue_rot_s = Math::sin(hue_rot_angle); + + Basis hue_rot_mat; + { + Basis mat1(0.299, 0.587, 0.114, 0.299, 0.587, 0.114, 0.299, 0.587, 0.114); + Basis mat2(0.701, -0.587, -0.114, -0.299, 0.413, -0.114, -0.300, -0.588, 0.886); + Basis mat3(0.168, 0.330, -0.497, -0.328, 0.035, 0.292, 1.250, -1.050, -0.203); + + for (int j = 0; j < 3; j++) { + hue_rot_mat[j] = mat1[j] + mat2[j] * hue_rot_c + mat3[j] * hue_rot_s; + } + } + + if (color_ramp.is_valid()) { + p.color = color_ramp->get_color_at_offset(p.custom[1]) * color; + } else { + p.color = color; + } + + Vector3 color_rgb = hue_rot_mat.xform_inv(Vector3(p.color.r, p.color.g, p.color.b)); + p.color.r = color_rgb.x; + p.color.g = color_rgb.y; + p.color.b = color_rgb.z; + + p.color *= p.base_color; + + if (flags[FLAG_ALIGN_Y_TO_VELOCITY]) { + if (p.velocity.length() > 0.0) { + + p.transform.elements[0] = p.velocity.normalized(); + p.transform.elements[0] = p.transform.elements[1].tangent(); + } + + } else { + p.transform.elements[0] = Vector2(Math::cos(p.rotation), -Math::sin(p.rotation)); + p.transform.elements[1] = Vector2(Math::sin(p.rotation), Math::cos(p.rotation)); + } + + //scale by scale + float base_scale = Math::lerp(parameters[PARAM_SCALE] * tex_scale, 1.0f, p.scale_rand * randomness[PARAM_SCALE]); + if (base_scale == 0.0) base_scale = 0.000001; + + p.transform.elements[0] *= base_scale; + p.transform.elements[1] *= base_scale; + + p.transform[2] += p.velocity * local_delta; + } +} + +void CPUParticles2D::_update_particle_data_buffer() { +#ifndef NO_THREADS + update_mutex->lock(); +#endif + + { + + int pc = particles.size(); + + PoolVector<int>::Write ow; + int *order = NULL; + + PoolVector<float>::Write w = particle_data.write(); + PoolVector<Particle>::Read r = particles.read(); + float *ptr = w.ptr(); + + Transform2D un_transform; + if (!local_coords) { + un_transform = get_global_transform().affine_inverse(); + } + + if (draw_order != DRAW_ORDER_INDEX) { + ow = particle_order.write(); + order = ow.ptr(); + + for (int i = 0; i < pc; i++) { + order[i] = i; + } + if (draw_order == DRAW_ORDER_LIFETIME) { + SortArray<int, SortLifetime> sorter; + sorter.compare.particles = r.ptr(); + sorter.sort(order, pc); + } + } + + for (int i = 0; i < pc; i++) { + + int idx = order ? order[i] : i; + + Transform2D t = r[idx].transform; + + if (!local_coords) { + t = un_transform * t; + } + + if (r[idx].active) { + + ptr[0] = t.elements[0][0]; + ptr[1] = t.elements[1][0]; + ptr[2] = 0; + ptr[3] = t.elements[2][0]; + ptr[4] = t.elements[0][1]; + ptr[5] = t.elements[1][1]; + ptr[6] = 0; + ptr[7] = t.elements[2][1]; + + } else { + zeromem(ptr, sizeof(float) * 8); + } + + Color c = r[idx].color; + uint8_t *data8 = (uint8_t *)&ptr[8]; + data8[0] = CLAMP(c.r * 255.0, 0, 255); + data8[1] = CLAMP(c.g * 255.0, 0, 255); + data8[2] = CLAMP(c.b * 255.0, 0, 255); + data8[3] = CLAMP(c.a * 255.0, 0, 255); + + ptr[9] = r[idx].custom[0]; + ptr[10] = r[idx].custom[1]; + ptr[11] = r[idx].custom[2]; + ptr[12] = r[idx].custom[3]; + + ptr += 13; + } + } + +#ifndef NO_THREADS + update_mutex->unlock(); +#endif +} + +void CPUParticles2D::_update_render_thread() { + +#ifndef NO_THREADS + update_mutex->lock(); +#endif + + VS::get_singleton()->multimesh_set_as_bulk_array(multimesh, particle_data); + +#ifndef NO_THREADS + update_mutex->unlock(); +#endif +} + +void CPUParticles2D::_notification(int p_what) { + + if (p_what == NOTIFICATION_ENTER_TREE) { + if (is_processing_internal()) { + +#ifndef NO_THREADS + update_mutex->lock(); +#endif + VS::get_singleton()->connect("frame_pre_draw", this, "_update_render_thread"); + VS::get_singleton()->canvas_item_set_update_when_visible(get_canvas_item(), true); + +#ifndef NO_THREADS + update_mutex->unlock(); +#endif + } + } + + if (p_what == NOTIFICATION_EXIT_TREE) { + if (is_processing_internal()) { + +#ifndef NO_THREADS + update_mutex->lock(); +#endif + VS::get_singleton()->disconnect("frame_pre_draw", this, "_update_render_thread"); + VS::get_singleton()->canvas_item_set_update_when_visible(get_canvas_item(), false); +#ifndef NO_THREADS + update_mutex->unlock(); +#endif + } + } + + if (p_what == NOTIFICATION_PAUSED || p_what == NOTIFICATION_UNPAUSED) { + } + + if (p_what == NOTIFICATION_DRAW) { + + RID texrid; + if (texture.is_valid()) { + texrid = texture->get_rid(); + } + + RID normrid; + if (normalmap.is_valid()) { + normrid = normalmap->get_rid(); + } + + VS::get_singleton()->canvas_item_add_multimesh(get_canvas_item(), multimesh, texrid, normrid); + } + + if (p_what == NOTIFICATION_INTERNAL_PROCESS) { + + if (particles.size() == 0 || !is_visible_in_tree()) + return; + + float delta = get_process_delta_time(); + if (emitting) { + + inactive_time = 0; + } else { + inactive_time += delta; + if (inactive_time > lifetime * 1.2) { + set_process_internal(false); +#ifndef NO_THREADS + update_mutex->lock(); +#endif + VS::get_singleton()->disconnect("frame_pre_draw", this, "_update_render_thread"); + VS::get_singleton()->canvas_item_set_update_when_visible(get_canvas_item(), false); + +#ifndef NO_THREADS + update_mutex->unlock(); +#endif + //reset variables + time = 0; + inactive_time = 0; + frame_remainder = 0; + cycle = 0; + return; + } + } + + if (time == 0 && pre_process_time > 0.0) { + + float frame_time; + if (fixed_fps > 0) + frame_time = 1.0 / fixed_fps; + else + frame_time = 1.0 / 30.0; + + float todo = pre_process_time; + + while (todo >= 0) { + _particles_process(frame_time); + todo -= frame_time; + } + } + + if (fixed_fps > 0) { + float frame_time = 1.0 / fixed_fps; + float decr = frame_time; + + float ldelta = delta; + if (ldelta > 0.1) { //avoid recursive stalls if fps goes below 10 + ldelta = 0.1; + } else if (ldelta <= 0.0) { //unlikely but.. + ldelta = 0.001; + } + float todo = frame_remainder + ldelta; + + while (todo >= frame_time) { + _particles_process(frame_time); + todo -= decr; + } + + frame_remainder = todo; + + } else { + _particles_process(delta); + } + + _update_particle_data_buffer(); + } +} + +void CPUParticles2D::convert_from_particles(Node *p_particles) { + Particles2D *particles = Object::cast_to<Particles2D>(p_particles); + ERR_FAIL_COND(!particles); + + set_emitting(particles->is_emitting()); + set_amount(particles->get_amount()); + set_lifetime(particles->get_lifetime()); + set_one_shot(particles->get_one_shot()); + set_pre_process_time(particles->get_pre_process_time()); + set_explosiveness_ratio(particles->get_explosiveness_ratio()); + set_randomness_ratio(particles->get_randomness_ratio()); + set_use_local_coordinates(particles->get_use_local_coordinates()); + set_fixed_fps(particles->get_fixed_fps()); + set_fractional_delta(particles->get_fractional_delta()); + set_speed_scale(particles->get_speed_scale()); + set_draw_order(DrawOrder(particles->get_draw_order())); + set_texture(particles->get_texture()); + + Ref<Material> mat = particles->get_material(); + if (mat.is_valid()) { + set_material(mat); + } + + Ref<ParticlesMaterial> material = particles->get_process_material(); + if (material.is_null()) + return; + + set_spread(material->get_spread()); + set_flatness(material->get_flatness()); + + set_color(material->get_color()); + + Ref<GradientTexture> gt = material->get_color_ramp(); + if (gt.is_valid()) { + set_color_ramp(gt->get_gradient()); + } + + set_particle_flag(FLAG_ALIGN_Y_TO_VELOCITY, material->get_flag(ParticlesMaterial::FLAG_ALIGN_Y_TO_VELOCITY)); + + set_emission_shape(EmissionShape(material->get_emission_shape())); + set_emission_sphere_radius(material->get_emission_sphere_radius()); + Vector2 rect_extents = Vector2(material->get_emission_box_extents().x, material->get_emission_box_extents().y); + set_emission_rect_extents(rect_extents); + + Vector2 gravity = Vector2(material->get_gravity().x, material->get_gravity().y); + set_gravity(gravity); + +#define CONVERT_PARAM(m_param) \ + set_param(m_param, material->get_param(ParticlesMaterial::m_param)); \ + { \ + Ref<CurveTexture> ctex = material->get_param_texture(ParticlesMaterial::m_param); \ + if (ctex.is_valid()) set_param_curve(m_param, ctex->get_curve()); \ + } \ + set_param_randomness(m_param, material->get_param_randomness(ParticlesMaterial::m_param)); + + CONVERT_PARAM(PARAM_INITIAL_LINEAR_VELOCITY); + CONVERT_PARAM(PARAM_ANGULAR_VELOCITY); + CONVERT_PARAM(PARAM_ORBIT_VELOCITY); + CONVERT_PARAM(PARAM_LINEAR_ACCEL); + CONVERT_PARAM(PARAM_RADIAL_ACCEL); + CONVERT_PARAM(PARAM_TANGENTIAL_ACCEL); + CONVERT_PARAM(PARAM_DAMPING); + CONVERT_PARAM(PARAM_ANGLE); + CONVERT_PARAM(PARAM_SCALE); + CONVERT_PARAM(PARAM_HUE_VARIATION); + CONVERT_PARAM(PARAM_ANIM_SPEED); + CONVERT_PARAM(PARAM_ANIM_OFFSET); + +#undef CONVERT_PARAM +} + +void CPUParticles2D::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_emitting", "emitting"), &CPUParticles2D::set_emitting); + ClassDB::bind_method(D_METHOD("set_amount", "amount"), &CPUParticles2D::set_amount); + ClassDB::bind_method(D_METHOD("set_lifetime", "secs"), &CPUParticles2D::set_lifetime); + ClassDB::bind_method(D_METHOD("set_one_shot", "enable"), &CPUParticles2D::set_one_shot); + ClassDB::bind_method(D_METHOD("set_pre_process_time", "secs"), &CPUParticles2D::set_pre_process_time); + ClassDB::bind_method(D_METHOD("set_explosiveness_ratio", "ratio"), &CPUParticles2D::set_explosiveness_ratio); + ClassDB::bind_method(D_METHOD("set_randomness_ratio", "ratio"), &CPUParticles2D::set_randomness_ratio); + ClassDB::bind_method(D_METHOD("set_use_local_coordinates", "enable"), &CPUParticles2D::set_use_local_coordinates); + ClassDB::bind_method(D_METHOD("set_fixed_fps", "fps"), &CPUParticles2D::set_fixed_fps); + ClassDB::bind_method(D_METHOD("set_fractional_delta", "enable"), &CPUParticles2D::set_fractional_delta); + ClassDB::bind_method(D_METHOD("set_speed_scale", "scale"), &CPUParticles2D::set_speed_scale); + + ClassDB::bind_method(D_METHOD("is_emitting"), &CPUParticles2D::is_emitting); + ClassDB::bind_method(D_METHOD("get_amount"), &CPUParticles2D::get_amount); + ClassDB::bind_method(D_METHOD("get_lifetime"), &CPUParticles2D::get_lifetime); + ClassDB::bind_method(D_METHOD("get_one_shot"), &CPUParticles2D::get_one_shot); + ClassDB::bind_method(D_METHOD("get_pre_process_time"), &CPUParticles2D::get_pre_process_time); + ClassDB::bind_method(D_METHOD("get_explosiveness_ratio"), &CPUParticles2D::get_explosiveness_ratio); + ClassDB::bind_method(D_METHOD("get_randomness_ratio"), &CPUParticles2D::get_randomness_ratio); + ClassDB::bind_method(D_METHOD("get_use_local_coordinates"), &CPUParticles2D::get_use_local_coordinates); + ClassDB::bind_method(D_METHOD("get_fixed_fps"), &CPUParticles2D::get_fixed_fps); + ClassDB::bind_method(D_METHOD("get_fractional_delta"), &CPUParticles2D::get_fractional_delta); + ClassDB::bind_method(D_METHOD("get_speed_scale"), &CPUParticles2D::get_speed_scale); + + ClassDB::bind_method(D_METHOD("set_draw_order", "order"), &CPUParticles2D::set_draw_order); + + ClassDB::bind_method(D_METHOD("get_draw_order"), &CPUParticles2D::get_draw_order); + + ClassDB::bind_method(D_METHOD("set_texture", "texture"), &CPUParticles2D::set_texture); + ClassDB::bind_method(D_METHOD("get_texture"), &CPUParticles2D::get_texture); + + ClassDB::bind_method(D_METHOD("set_normalmap", "normalmap"), &CPUParticles2D::set_normalmap); + ClassDB::bind_method(D_METHOD("get_normalmap"), &CPUParticles2D::get_normalmap); + + ClassDB::bind_method(D_METHOD("restart"), &CPUParticles2D::restart); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_EXP_RANGE, "1,1000000,1"), "set_amount", "get_amount"); + ADD_GROUP("Time", ""); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "lifetime", PROPERTY_HINT_EXP_RANGE, "0.01,600.0,0.01"), "set_lifetime", "get_lifetime"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "preprocess", PROPERTY_HINT_EXP_RANGE, "0.00,600.0,0.01"), "set_pre_process_time", "get_pre_process_time"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "speed_scale", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_speed_scale", "get_speed_scale"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_fps", PROPERTY_HINT_RANGE, "0,1000,1"), "set_fixed_fps", "get_fixed_fps"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fract_delta"), "set_fractional_delta", "get_fractional_delta"); + ADD_GROUP("Drawing", ""); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "local_coords"), "set_use_local_coordinates", "get_use_local_coordinates"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "draw_order", PROPERTY_HINT_ENUM, "Index,Lifetime"), "set_draw_order", "get_draw_order"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normalmap", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_normalmap", "get_normalmap"); + + BIND_ENUM_CONSTANT(DRAW_ORDER_INDEX); + BIND_ENUM_CONSTANT(DRAW_ORDER_LIFETIME); + + //////////////////////////////// + + ClassDB::bind_method(D_METHOD("set_spread", "degrees"), &CPUParticles2D::set_spread); + ClassDB::bind_method(D_METHOD("get_spread"), &CPUParticles2D::get_spread); + + ClassDB::bind_method(D_METHOD("set_flatness", "amount"), &CPUParticles2D::set_flatness); + ClassDB::bind_method(D_METHOD("get_flatness"), &CPUParticles2D::get_flatness); + + ClassDB::bind_method(D_METHOD("set_param", "param", "value"), &CPUParticles2D::set_param); + ClassDB::bind_method(D_METHOD("get_param", "param"), &CPUParticles2D::get_param); + + ClassDB::bind_method(D_METHOD("set_param_randomness", "param", "randomness"), &CPUParticles2D::set_param_randomness); + ClassDB::bind_method(D_METHOD("get_param_randomness", "param"), &CPUParticles2D::get_param_randomness); + + ClassDB::bind_method(D_METHOD("set_param_curve", "param", "curve"), &CPUParticles2D::set_param_curve); + ClassDB::bind_method(D_METHOD("get_param_curve", "param"), &CPUParticles2D::get_param_curve); + + ClassDB::bind_method(D_METHOD("set_color", "color"), &CPUParticles2D::set_color); + ClassDB::bind_method(D_METHOD("get_color"), &CPUParticles2D::get_color); + + ClassDB::bind_method(D_METHOD("set_color_ramp", "ramp"), &CPUParticles2D::set_color_ramp); + ClassDB::bind_method(D_METHOD("get_color_ramp"), &CPUParticles2D::get_color_ramp); + + ClassDB::bind_method(D_METHOD("set_particle_flag", "flag", "enable"), &CPUParticles2D::set_particle_flag); + ClassDB::bind_method(D_METHOD("get_particle_flag", "flag"), &CPUParticles2D::get_particle_flag); + + ClassDB::bind_method(D_METHOD("set_emission_shape", "shape"), &CPUParticles2D::set_emission_shape); + ClassDB::bind_method(D_METHOD("get_emission_shape"), &CPUParticles2D::get_emission_shape); + + ClassDB::bind_method(D_METHOD("set_emission_sphere_radius", "radius"), &CPUParticles2D::set_emission_sphere_radius); + ClassDB::bind_method(D_METHOD("get_emission_sphere_radius"), &CPUParticles2D::get_emission_sphere_radius); + + ClassDB::bind_method(D_METHOD("set_emission_rect_extents", "extents"), &CPUParticles2D::set_emission_rect_extents); + ClassDB::bind_method(D_METHOD("get_emission_rect_extents"), &CPUParticles2D::get_emission_rect_extents); + + ClassDB::bind_method(D_METHOD("set_emission_points", "array"), &CPUParticles2D::set_emission_points); + ClassDB::bind_method(D_METHOD("get_emission_points"), &CPUParticles2D::get_emission_points); + + ClassDB::bind_method(D_METHOD("set_emission_normals", "array"), &CPUParticles2D::set_emission_normals); + ClassDB::bind_method(D_METHOD("get_emission_normals"), &CPUParticles2D::get_emission_normals); + + ClassDB::bind_method(D_METHOD("set_emission_colors", "array"), &CPUParticles2D::set_emission_colors); + ClassDB::bind_method(D_METHOD("get_emission_colors"), &CPUParticles2D::get_emission_colors); + + ClassDB::bind_method(D_METHOD("get_gravity"), &CPUParticles2D::get_gravity); + ClassDB::bind_method(D_METHOD("set_gravity", "accel_vec"), &CPUParticles2D::set_gravity); + + ClassDB::bind_method(D_METHOD("convert_from_particles", "particles"), &CPUParticles2D::convert_from_particles); + + ClassDB::bind_method(D_METHOD("_update_render_thread"), &CPUParticles2D::_update_render_thread); + + ADD_GROUP("Emission Shape", "emission_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Box,Points,Directed Points"), "set_emission_shape", "get_emission_shape"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "emission_sphere_radius", PROPERTY_HINT_RANGE, "0.01,128,0.01"), "set_emission_sphere_radius", "get_emission_sphere_radius"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "emission_rect_extents"), "set_emission_rect_extents", "get_emission_rect_extents"); + ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "emission_points"), "set_emission_points", "get_emission_points"); + ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "emission_normals"), "set_emission_normals", "get_emission_normals"); + ADD_PROPERTY(PropertyInfo(Variant::POOL_COLOR_ARRAY, "emission_colors"), "set_emission_colors", "get_emission_colors"); + ADD_GROUP("Flags", "flag_"); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_align_y"), "set_particle_flag", "get_particle_flag", FLAG_ALIGN_Y_TO_VELOCITY); + ADD_GROUP("Spread", ""); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "spread", PROPERTY_HINT_RANGE, "0,180,0.01"), "set_spread", "get_spread"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "flatness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_flatness", "get_flatness"); + ADD_GROUP("Gravity", ""); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "gravity"), "set_gravity", "get_gravity"); + ADD_GROUP("Initial Velocity", "initial_"); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "initial_velocity", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param", "get_param", PARAM_INITIAL_LINEAR_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "initial_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_INITIAL_LINEAR_VELOCITY); + ADD_GROUP("Angular Velocity", "angular_"); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity", PROPERTY_HINT_RANGE, "-360,360,0.01"), "set_param", "get_param", PARAM_ANGULAR_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGULAR_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angular_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANGULAR_VELOCITY); + /* + ADD_GROUP("Orbit Velocity", "orbit_"); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "orbit_velocity", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_ORBIT_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "orbit_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ORBIT_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "orbit_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ORBIT_VELOCITY); +*/ + ADD_GROUP("Linear Accel", "linear_"); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_LINEAR_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_LINEAR_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "linear_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_LINEAR_ACCEL); + ADD_GROUP("Radial Accel", "radial_"); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "radial_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_RADIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "radial_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_RADIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "radial_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_RADIAL_ACCEL); + ADD_GROUP("Tangential Accel", "tangential_"); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "tangential_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_TANGENTIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "tangential_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_TANGENTIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "tangential_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_TANGENTIAL_ACCEL); + ADD_GROUP("Damping", ""); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "damping", PROPERTY_HINT_RANGE, "0,100,0.01"), "set_param", "get_param", PARAM_DAMPING); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "damping_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_DAMPING); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "damping_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_DAMPING); + ADD_GROUP("Angle", ""); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angle", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater"), "set_param", "get_param", PARAM_ANGLE); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angle_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGLE); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angle_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANGLE); + ADD_GROUP("Scale", ""); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "scale", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param", "get_param", PARAM_SCALE); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "scale_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_SCALE); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "scale_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_SCALE); + ADD_GROUP("Color", ""); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "GradientTexture"), "set_color_ramp", "get_color_ramp"); + + ADD_GROUP("Hue Variation", "hue_"); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "hue_variation", PROPERTY_HINT_RANGE, "-1,1,0.1"), "set_param", "get_param", PARAM_HUE_VARIATION); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "hue_variation_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_HUE_VARIATION); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "hue_variation_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_HUE_VARIATION); + ADD_GROUP("Animation", "anim_"); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_speed", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater"), "set_param", "get_param", PARAM_ANIM_SPEED); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_speed_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_SPEED); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_speed_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANIM_SPEED); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_offset", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_ANIM_OFFSET); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_offset_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_OFFSET); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_offset_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANIM_OFFSET); + + BIND_ENUM_CONSTANT(PARAM_INITIAL_LINEAR_VELOCITY); + BIND_ENUM_CONSTANT(PARAM_ANGULAR_VELOCITY); + BIND_ENUM_CONSTANT(PARAM_ORBIT_VELOCITY); + BIND_ENUM_CONSTANT(PARAM_LINEAR_ACCEL); + BIND_ENUM_CONSTANT(PARAM_RADIAL_ACCEL); + BIND_ENUM_CONSTANT(PARAM_TANGENTIAL_ACCEL); + BIND_ENUM_CONSTANT(PARAM_DAMPING); + BIND_ENUM_CONSTANT(PARAM_ANGLE); + BIND_ENUM_CONSTANT(PARAM_SCALE); + BIND_ENUM_CONSTANT(PARAM_HUE_VARIATION); + BIND_ENUM_CONSTANT(PARAM_ANIM_SPEED); + BIND_ENUM_CONSTANT(PARAM_ANIM_OFFSET); + BIND_ENUM_CONSTANT(PARAM_MAX); + + BIND_ENUM_CONSTANT(FLAG_ALIGN_Y_TO_VELOCITY); + BIND_ENUM_CONSTANT(FLAG_MAX); + + BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINT); + BIND_ENUM_CONSTANT(EMISSION_SHAPE_CIRCLE); + BIND_ENUM_CONSTANT(EMISSION_SHAPE_RECTANGLE); + BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINTS); + BIND_ENUM_CONSTANT(EMISSION_SHAPE_DIRECTED_POINTS); +} + +CPUParticles2D::CPUParticles2D() { + + time = 0; + inactive_time = 0; + frame_remainder = 0; + cycle = 0; + + mesh = VisualServer::get_singleton()->mesh_create(); + multimesh = VisualServer::get_singleton()->multimesh_create(); + VisualServer::get_singleton()->multimesh_set_mesh(multimesh, mesh); + + set_emitting(true); + set_one_shot(false); + set_amount(8); + set_lifetime(1); + set_fixed_fps(0); + set_fractional_delta(true); + set_pre_process_time(0); + set_explosiveness_ratio(0); + set_randomness_ratio(0); + set_use_local_coordinates(true); + + set_draw_order(DRAW_ORDER_INDEX); + set_speed_scale(1); + + set_spread(45); + set_flatness(0); + set_param(PARAM_INITIAL_LINEAR_VELOCITY, 1); + //set_param(PARAM_ORBIT_VELOCITY, 0); + set_param(PARAM_LINEAR_ACCEL, 0); + set_param(PARAM_RADIAL_ACCEL, 0); + set_param(PARAM_TANGENTIAL_ACCEL, 0); + set_param(PARAM_DAMPING, 0); + set_param(PARAM_ANGLE, 0); + set_param(PARAM_SCALE, 1); + set_param(PARAM_HUE_VARIATION, 0); + set_param(PARAM_ANIM_SPEED, 0); + set_param(PARAM_ANIM_OFFSET, 0); + set_emission_shape(EMISSION_SHAPE_POINT); + set_emission_sphere_radius(1); + set_emission_rect_extents(Vector2(1, 1)); + + set_gravity(Vector2(0, 98.8)); + + for (int i = 0; i < PARAM_MAX; i++) { + set_param_randomness(Parameter(i), 0); + } + + for (int i = 0; i < FLAG_MAX; i++) { + flags[i] = false; + } + + set_color(Color(1, 1, 1, 1)); + +#ifndef NO_THREADS + update_mutex = Mutex::create(); +#endif + + _generate_mesh_texture(); +} + +CPUParticles2D::~CPUParticles2D() { + VS::get_singleton()->free(multimesh); + VS::get_singleton()->free(mesh); + +#ifndef NO_THREADS + memdelete(update_mutex); +#endif +} diff --git a/scene/2d/cpu_particles_2d.h b/scene/2d/cpu_particles_2d.h new file mode 100644 index 0000000000..15b7642b5e --- /dev/null +++ b/scene/2d/cpu_particles_2d.h @@ -0,0 +1,297 @@ +/*************************************************************************/ +/* cpu_particles_2d.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 CPU_PARTICLES_2D_H +#define CPU_PARTICLES_2D_H + +#include "core/rid.h" +#include "scene/2d/node_2d.h" +#include "scene/resources/texture.h" + +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ + +class CPUParticles2D : public Node2D { +private: + GDCLASS(CPUParticles2D, Node2D); + +public: + enum DrawOrder { + DRAW_ORDER_INDEX, + DRAW_ORDER_LIFETIME, + }; + + enum Parameter { + + PARAM_INITIAL_LINEAR_VELOCITY, + PARAM_ANGULAR_VELOCITY, + PARAM_ORBIT_VELOCITY, + PARAM_LINEAR_ACCEL, + PARAM_RADIAL_ACCEL, + PARAM_TANGENTIAL_ACCEL, + PARAM_DAMPING, + PARAM_ANGLE, + PARAM_SCALE, + PARAM_HUE_VARIATION, + PARAM_ANIM_SPEED, + PARAM_ANIM_OFFSET, + PARAM_MAX + }; + + enum Flags { + FLAG_ALIGN_Y_TO_VELOCITY, + FLAG_MAX + }; + + enum EmissionShape { + EMISSION_SHAPE_POINT, + EMISSION_SHAPE_CIRCLE, + EMISSION_SHAPE_RECTANGLE, + EMISSION_SHAPE_POINTS, + EMISSION_SHAPE_DIRECTED_POINTS, + }; + +private: + bool emitting; + + struct Particle { + Transform2D transform; + Color color; + float custom[4]; + float rotation; + Vector2 velocity; + bool active; + float angle_rand; + float scale_rand; + float hue_rot_rand; + float anim_offset_rand; + float time; + Color base_color; + + uint32_t seed; + }; + + float time; + float inactive_time; + float frame_remainder; + int cycle; + + RID mesh; + RID multimesh; + + PoolVector<Particle> particles; + PoolVector<float> particle_data; + PoolVector<int> particle_order; + + struct SortLifetime { + const Particle *particles; + + bool operator()(int p_a, int p_b) const { + return particles[p_a].time < particles[p_b].time; + } + }; + + struct SortAxis { + const Particle *particles; + Vector2 axis; + bool operator()(int p_a, int p_b) const { + + return axis.dot(particles[p_a].transform[2]) < axis.dot(particles[p_b].transform[2]); + } + }; + + // + + bool one_shot; + + float lifetime; + float pre_process_time; + float explosiveness_ratio; + float randomness_ratio; + float speed_scale; + bool local_coords; + int fixed_fps; + bool fractional_delta; + + DrawOrder draw_order; + + Ref<Texture> texture; + Ref<Texture> normalmap; + + //////// + + float spread; + float flatness; + + float parameters[PARAM_MAX]; + float randomness[PARAM_MAX]; + + Ref<Curve> curve_parameters[PARAM_MAX]; + Color color; + Ref<Gradient> color_ramp; + + bool flags[FLAG_MAX]; + + EmissionShape emission_shape; + float emission_sphere_radius; + Vector2 emission_rect_extents; + PoolVector<Vector2> emission_points; + PoolVector<Vector2> emission_normals; + PoolVector<Color> emission_colors; + int emission_point_count; + + Vector2 gravity; + + void _particles_process(float p_delta); + void _update_particle_data_buffer(); + + Mutex *update_mutex; + + void _update_render_thread(); + + void _generate_mesh_texture(); + +protected: + static void _bind_methods(); + void _notification(int p_what); + virtual void _validate_property(PropertyInfo &property) const; + +public: + void set_emitting(bool p_emitting); + void set_amount(int p_amount); + void set_lifetime(float p_lifetime); + void set_one_shot(bool p_one_shot); + void set_pre_process_time(float p_time); + void set_explosiveness_ratio(float p_ratio); + void set_randomness_ratio(float p_ratio); + void set_visibility_aabb(const Rect2 &p_aabb); + void set_use_local_coordinates(bool p_enable); + void set_speed_scale(float p_scale); + + bool is_emitting() const; + int get_amount() const; + float get_lifetime() const; + bool get_one_shot() const; + float get_pre_process_time() const; + float get_explosiveness_ratio() const; + float get_randomness_ratio() const; + Rect2 get_visibility_aabb() const; + bool get_use_local_coordinates() const; + float get_speed_scale() const; + + void set_fixed_fps(int p_count); + int get_fixed_fps() const; + + void set_fractional_delta(bool p_enable); + bool get_fractional_delta() const; + + void set_draw_order(DrawOrder p_order); + DrawOrder get_draw_order() const; + + void set_draw_passes(int p_count); + int get_draw_passes() const; + + void set_texture(const Ref<Texture> &p_texture); + Ref<Texture> get_texture() const; + + void set_h_frames(int p_frames); + int get_h_frames(); + + void set_v_frames(int p_frames); + int get_v_frames(); + + void set_loop_animation(bool p_loop); + bool get_loop_animation() const; + + void set_normalmap(const Ref<Texture> &p_normalmap); + Ref<Texture> get_normalmap() const; + + /////////////////// + + void set_spread(float p_spread); + float get_spread() const; + + void set_flatness(float p_flatness); + float get_flatness() const; + + void set_param(Parameter p_param, float p_value); + float get_param(Parameter p_param) const; + + void set_param_randomness(Parameter p_param, float p_value); + float get_param_randomness(Parameter p_param) const; + + void set_param_curve(Parameter p_param, const Ref<Curve> &p_curve); + Ref<Curve> get_param_curve(Parameter p_param) const; + + void set_color(const Color &p_color); + Color get_color() const; + + void set_color_ramp(const Ref<Gradient> &p_texture); + Ref<Gradient> get_color_ramp() const; + + void set_particle_flag(Flags p_flag, bool p_enable); + bool get_particle_flag(Flags p_flag) const; + + void set_emission_shape(EmissionShape p_shape); + void set_emission_sphere_radius(float p_radius); + void set_emission_rect_extents(Vector2 p_extents); + void set_emission_points(const PoolVector<Vector2> &p_points); + void set_emission_normals(const PoolVector<Vector2> &p_normals); + void set_emission_colors(const PoolVector<Color> &p_colors); + void set_emission_point_count(int p_count); + + EmissionShape get_emission_shape() const; + float get_emission_sphere_radius() const; + Vector2 get_emission_rect_extents() const; + PoolVector<Vector2> get_emission_points() const; + PoolVector<Vector2> get_emission_normals() const; + PoolVector<Color> get_emission_colors() const; + int get_emission_point_count() const; + + void set_gravity(const Vector2 &p_gravity); + Vector2 get_gravity() const; + + virtual String get_configuration_warning() const; + + void restart(); + + void convert_from_particles(Node *p_particles); + + CPUParticles2D(); + ~CPUParticles2D(); +}; + +VARIANT_ENUM_CAST(CPUParticles2D::DrawOrder) +VARIANT_ENUM_CAST(CPUParticles2D::Parameter) +VARIANT_ENUM_CAST(CPUParticles2D::Flags) +VARIANT_ENUM_CAST(CPUParticles2D::EmissionShape) + +#endif // CPU_PARTICLES_2D_H diff --git a/scene/2d/joints_2d.cpp b/scene/2d/joints_2d.cpp index 7d5360c0e4..b9a48e1fdc 100644 --- a/scene/2d/joints_2d.cpp +++ b/scene/2d/joints_2d.cpp @@ -30,7 +30,7 @@ #include "joints_2d.h" -#include "engine.h" +#include "core/engine.h" #include "physics_body_2d.h" #include "servers/physics_2d_server.h" diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp index f93c7d1f79..e6a5a0b651 100644 --- a/scene/2d/light_2d.cpp +++ b/scene/2d/light_2d.cpp @@ -30,7 +30,7 @@ #include "light_2d.h" -#include "engine.h" +#include "core/engine.h" #include "servers/visual_server.h" Dictionary Light2D::_edit_get_state() const { @@ -431,7 +431,7 @@ void Light2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_only"), "set_editor_only", "is_editor_only"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture,ImageTexture"), "set_texture", "get_texture"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_texture_offset", "get_texture_offset"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "texture_scale", PROPERTY_HINT_RANGE, "0.01,50,0.01"), "set_texture_scale", "get_texture_scale"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color"); diff --git a/scene/2d/light_occluder_2d.cpp b/scene/2d/light_occluder_2d.cpp index c9e5d0f1bc..ab15b49985 100644 --- a/scene/2d/light_occluder_2d.cpp +++ b/scene/2d/light_occluder_2d.cpp @@ -30,7 +30,7 @@ #include "light_occluder_2d.h" -#include "engine.h" +#include "core/engine.h" void OccluderPolygon2D::set_polygon(const PoolVector<Vector2> &p_polygon) { diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp index ad9775c0b7..e164f0ca75 100644 --- a/scene/2d/line_2d.cpp +++ b/scene/2d/line_2d.cpp @@ -31,7 +31,7 @@ #include "line_2d.h" #include "line_builder.h" -#include "core_string_names.h" +#include "core/core_string_names.h" // Needed so we can bind functions VARIANT_ENUM_CAST(Line2D::LineJointMode) diff --git a/scene/2d/line_builder.cpp b/scene/2d/line_builder.cpp index a3f1b25e05..d5e8a33805 100644 --- a/scene/2d/line_builder.cpp +++ b/scene/2d/line_builder.cpp @@ -294,7 +294,6 @@ void LineBuilder::build() { if (texture_mode == Line2D::LINE_TEXTURE_TILE) { uvx1 = current_distance1 / (width * tile_aspect); } else if (texture_mode == Line2D::LINE_TEXTURE_STRETCH) { - uvx0 = current_distance0 / total_distance; uvx1 = current_distance1 / total_distance; } diff --git a/scene/2d/line_builder.h b/scene/2d/line_builder.h index edfdf97c47..f9d26f12af 100644 --- a/scene/2d/line_builder.h +++ b/scene/2d/line_builder.h @@ -31,10 +31,10 @@ #ifndef LINE_BUILDER_H #define LINE_BUILDER_H -#include "color.h" +#include "core/color.h" +#include "core/math/vector2.h" #include "line_2d.h" #include "scene/resources/color_ramp.h" -#include "vector2.h" class LineBuilder { public: diff --git a/scene/2d/navigation2d.cpp b/scene/2d/navigation2d.cpp index 9eec8e6cc3..1b789bab9d 100644 --- a/scene/2d/navigation2d.cpp +++ b/scene/2d/navigation2d.cpp @@ -388,10 +388,34 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect Polygon *p = E->get(); float cost = p->distance; - cost += p->center.distance_to(end_point); - if (cost < least_cost) { +#ifdef USE_ENTRY_POINT + int es = p->edges.size(); + float shortest_distance = 1e30; + + for (int i = 0; i < es; i++) { + Polygon::Edge &e = p->edges.write[i]; + + if (!e.C) + continue; + + Vector2 edge[2] = { + _get_vertex(p->edges[i].point), + _get_vertex(p->edges[(i + 1) % es].point) + }; + + Vector2 edge_point = Geometry::get_closest_point_to_segment_2d(p->entry, edge); + float dist = p->entry.distance_to(edge_point); + if (dist < shortest_distance) + shortest_distance = dist; + } + + cost += shortest_distance; +#else + cost += p->center.distance_to(end_point); +#endif + if (cost < least_cost) { least_cost_poly = E; least_cost = cost; } diff --git a/scene/2d/navigation_polygon.cpp b/scene/2d/navigation_polygon.cpp index 84b12b0bfe..b36924e521 100644 --- a/scene/2d/navigation_polygon.cpp +++ b/scene/2d/navigation_polygon.cpp @@ -30,8 +30,8 @@ #include "navigation_polygon.h" -#include "core_string_names.h" -#include "engine.h" +#include "core/core_string_names.h" +#include "core/engine.h" #include "navigation2d.h" #include "thirdparty/misc/triangulator.h" diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp index 7de72dc41d..2f94c3c6f5 100644 --- a/scene/2d/node_2d.cpp +++ b/scene/2d/node_2d.cpp @@ -30,7 +30,7 @@ #include "node_2d.h" -#include "message_queue.h" +#include "core/message_queue.h" #include "scene/gui/control.h" #include "scene/main/viewport.h" #include "servers/visual_server.h" @@ -204,12 +204,6 @@ Size2 Node2D::get_scale() const { return _scale; } -void Node2D::_notification(int p_what) { - - switch (p_what) { - } -} - Transform2D Node2D::get_transform() const { return _mat; diff --git a/scene/2d/node_2d.h b/scene/2d/node_2d.h index 725686cdf8..924a84fb88 100644 --- a/scene/2d/node_2d.h +++ b/scene/2d/node_2d.h @@ -52,8 +52,6 @@ class Node2D : public CanvasItem { void _update_xform_values(); protected: - void _notification(int p_what); - static void _bind_methods(); public: diff --git a/scene/2d/parallax_layer.cpp b/scene/2d/parallax_layer.cpp index 2ac6c76032..06de723f27 100644 --- a/scene/2d/parallax_layer.cpp +++ b/scene/2d/parallax_layer.cpp @@ -30,7 +30,7 @@ #include "parallax_layer.h" -#include "engine.h" +#include "core/engine.h" #include "parallax_background.h" void ParallaxLayer::set_motion_scale(const Size2 &p_scale) { diff --git a/scene/2d/particles_2d.cpp b/scene/2d/particles_2d.cpp index e102b86f53..9c01d81c11 100644 --- a/scene/2d/particles_2d.cpp +++ b/scene/2d/particles_2d.cpp @@ -30,10 +30,13 @@ #include "particles_2d.h" -#include "engine.h" -#include "scene/3d/particles.h" +#include "scene/resources/particles_material.h" #include "scene/scene_string_names.h" +#ifdef TOOLS_ENABLED +#include "core/engine.h" +#endif + void Particles2D::set_emitting(bool p_emitting) { VS::get_singleton()->particles_set_emitting(particles, p_emitting); @@ -114,7 +117,7 @@ void Particles2D::set_process_material(const Ref<Material> &p_material) { process_material = p_material; Ref<ParticlesMaterial> pm = p_material; if (pm.is_valid() && !pm->get_flag(ParticlesMaterial::FLAG_DISABLE_Z) && pm->get_gravity() == Vector3(0, -9.8, 0)) { - //likely a new material, modify it! + // Likely a new (3D) material, modify it to match 2D space pm->set_flag(ParticlesMaterial::FLAG_DISABLE_Z, true); pm->set_gravity(Vector3(0, 98, 0)); } @@ -254,30 +257,6 @@ Ref<Texture> Particles2D::get_normal_map() const { void Particles2D::_validate_property(PropertyInfo &property) const { } -void Particles2D::set_v_frames(int p_count) { - - ERR_FAIL_COND(p_count < 1); - v_frames = p_count; - update(); -} - -int Particles2D::get_v_frames() const { - - return v_frames; -} - -void Particles2D::set_h_frames(int p_count) { - - ERR_FAIL_COND(p_count < 1); - h_frames = p_count; - update(); -} - -int Particles2D::get_h_frames() const { - - return h_frames; -} - void Particles2D::restart() { VS::get_singleton()->particles_restart(particles); } @@ -293,7 +272,7 @@ void Particles2D::_notification(int p_what) { if (normal_map.is_valid()) normal_rid = normal_map->get_rid(); - VS::get_singleton()->canvas_item_add_particles(get_canvas_item(), particles, texture_rid, normal_rid, h_frames, v_frames); + VS::get_singleton()->canvas_item_add_particles(get_canvas_item(), particles, texture_rid, normal_rid); #ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint() && (this == get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->is_a_parent_of(this))) { @@ -358,12 +337,6 @@ void Particles2D::_bind_methods() { ClassDB::bind_method(D_METHOD("capture_rect"), &Particles2D::capture_rect); - ClassDB::bind_method(D_METHOD("set_v_frames", "frames"), &Particles2D::set_v_frames); - ClassDB::bind_method(D_METHOD("get_v_frames"), &Particles2D::get_v_frames); - - ClassDB::bind_method(D_METHOD("set_h_frames", "frames"), &Particles2D::set_h_frames); - ClassDB::bind_method(D_METHOD("get_h_frames"), &Particles2D::get_h_frames); - ClassDB::bind_method(D_METHOD("restart"), &Particles2D::restart); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting"); @@ -372,7 +345,7 @@ void Particles2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::REAL, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater"), "set_lifetime", "get_lifetime"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "preprocess", PROPERTY_HINT_RANGE, "0.00,600.0,0.01"), "set_pre_process_time", "get_pre_process_time"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "speed_scale", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_speed_scale", "get_speed_scale"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "speed_scale", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_speed_scale", "get_speed_scale"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio"); ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_fps", PROPERTY_HINT_RANGE, "0,1000,1"), "set_fixed_fps", "get_fixed_fps"); @@ -386,8 +359,6 @@ void Particles2D::_bind_methods() { ADD_GROUP("Textures", ""); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normal_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_normal_map", "get_normal_map"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "h_frames", PROPERTY_HINT_RANGE, "1,1024,1"), "set_h_frames", "get_h_frames"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "v_frames", PROPERTY_HINT_RANGE, "1,1024,1"), "set_v_frames", "get_v_frames"); BIND_ENUM_CONSTANT(DRAW_ORDER_INDEX); BIND_ENUM_CONSTANT(DRAW_ORDER_LIFETIME); @@ -410,8 +381,6 @@ Particles2D::Particles2D() { set_use_local_coordinates(true); set_draw_order(DRAW_ORDER_INDEX); set_speed_scale(1); - h_frames = 1; - v_frames = 1; } Particles2D::~Particles2D() { diff --git a/scene/2d/particles_2d.h b/scene/2d/particles_2d.h index 31a66afb2a..6d52f8b28e 100644 --- a/scene/2d/particles_2d.h +++ b/scene/2d/particles_2d.h @@ -28,11 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef PARTICLES_FRAME_H -#define PARTICLES_FRAME_H +#ifndef PARTICLES_2D_H +#define PARTICLES_2D_H +#include "core/rid.h" #include "scene/2d/node_2d.h" -#include "scene/resources/color_ramp.h" #include "scene/resources/texture.h" class Particles2D : public Node2D { @@ -59,8 +59,6 @@ private: bool local_coords; int fixed_fps; bool fractional_delta; - int v_frames; - int h_frames; Ref<Material> process_material; @@ -118,12 +116,6 @@ public: virtual String get_configuration_warning() const; - void set_v_frames(int p_count); - int get_v_frames() const; - - void set_h_frames(int p_count); - int get_h_frames() const; - void restart(); Rect2 capture_rect() const; Particles2D(); @@ -132,4 +124,4 @@ public: VARIANT_ENUM_CAST(Particles2D::DrawOrder) -#endif // PARTICLES_FRAME_H +#endif // PARTICLES_2D_H diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp index 658b998d17..4276918f53 100644 --- a/scene/2d/path_2d.cpp +++ b/scene/2d/path_2d.cpp @@ -30,7 +30,7 @@ #include "path_2d.h" -#include "engine.h" +#include "core/engine.h" #include "scene/scene_string_names.h" #ifdef TOOLS_ENABLED @@ -63,6 +63,10 @@ bool Path2D::_edit_use_rect() const { bool Path2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + if (curve.is_null()) { + return false; + } + for (int i = 0; i < curve->get_point_count(); i++) { Vector2 s[2]; s[0] = curve->get_point_position(i); diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index 8e31688d90..60074abb29 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -31,22 +31,15 @@ #include "physics_body_2d.h" #include "core/core_string_names.h" +#include "core/engine.h" +#include "core/list.h" +#include "core/math/math_funcs.h" #include "core/method_bind_ext.gen.inc" -#include "engine.h" -#include "math_funcs.h" +#include "core/object.h" +#include "core/rid.h" #include "scene/scene_string_names.h" -void PhysicsBody2D::_notification(int p_what) { - - /* - switch(p_what) { - - case NOTIFICATION_TRANSFORM_CHANGED: { - - Physics2DServer::get_singleton()->body_set_state(get_rid(),Physics2DServer::BODY_STATE_TRANSFORM,get_global_transform()); - } break; - } - */ +void PhysicsBody2D::_notification(int p_what) { } void PhysicsBody2D::_set_layers(uint32_t p_mask) { @@ -75,6 +68,8 @@ void PhysicsBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_layers", "mask"), &PhysicsBody2D::_set_layers); ClassDB::bind_method(D_METHOD("_get_layers"), &PhysicsBody2D::_get_layers); + + ClassDB::bind_method(D_METHOD("get_collision_exceptions"), &PhysicsBody2D::get_collision_exceptions); ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &PhysicsBody2D::add_collision_exception_with); ClassDB::bind_method(D_METHOD("remove_collision_exception_with", "body"), &PhysicsBody2D::remove_collision_exception_with); ADD_PROPERTY(PropertyInfo(Variant::INT, "layers", PROPERTY_HINT_LAYERS_2D_PHYSICS, "", 0), "_set_layers", "_get_layers"); //for backwards compat @@ -144,6 +139,20 @@ PhysicsBody2D::PhysicsBody2D(Physics2DServer::BodyMode p_mode) : set_pickable(false); } +Array PhysicsBody2D::get_collision_exceptions() { + List<RID> exceptions; + Physics2DServer::get_singleton()->body_get_collision_exceptions(get_rid(), &exceptions); + Array ret; + for (List<RID>::Element *E = exceptions.front(); E; E = E->next()) { + RID body = E->get(); + ObjectID instance_id = Physics2DServer::get_singleton()->body_get_object_instance_id(body); + Object *obj = ObjectDB::get_instance(instance_id); + PhysicsBody2D *physics_body = Object::cast_to<PhysicsBody2D>(obj); + ret.append(physics_body); + } + return ret; +} + void PhysicsBody2D::add_collision_exception_with(Node *p_node) { ERR_FAIL_NULL(p_node); @@ -190,7 +199,11 @@ real_t StaticBody2D::get_constant_angular_velocity() const { #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") + if (p_friction == 1.0) { // default value, don't create an override for that + return; + } + + ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physics material instead.") WARN_DEPRECATED ERR_FAIL_COND(p_friction < 0 || p_friction > 1); @@ -204,7 +217,7 @@ void StaticBody2D::set_friction(real_t p_friction) { real_t StaticBody2D::get_friction() const { - ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physical material") + ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physics material instead.") WARN_DEPRECATED if (physics_material_override.is_null()) { @@ -216,7 +229,11 @@ real_t StaticBody2D::get_friction() const { 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") + if (p_bounce == 0.0) { // default value, don't create an override for that + return; + } + + ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physics material instead.") WARN_DEPRECATED ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1); @@ -230,7 +247,7 @@ void StaticBody2D::set_bounce(real_t p_bounce) { real_t StaticBody2D::get_bounce() const { - ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physical material") + ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physics material instead.") WARN_DEPRECATED if (physics_material_override.is_null()) { @@ -282,10 +299,10 @@ void StaticBody2D::_bind_methods() { 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"); + ADD_PROPERTYNO(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01", 0), "set_friction", "get_friction"); + ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01", 0), "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_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override"); } StaticBody2D::StaticBody2D() : @@ -406,13 +423,13 @@ void RigidBody2D::_body_inout(int p_status, ObjectID p_instance, int p_body_shap node->disconnect(SceneStringNames::get_singleton()->tree_entered, this, SceneStringNames::get_singleton()->_body_enter_tree); node->disconnect(SceneStringNames::get_singleton()->tree_exiting, this, SceneStringNames::get_singleton()->_body_exit_tree); if (in_scene) - emit_signal(SceneStringNames::get_singleton()->body_exited, obj); + emit_signal(SceneStringNames::get_singleton()->body_exited, node); } contact_monitor->body_map.erase(E); } if (node && in_scene) { - emit_signal(SceneStringNames::get_singleton()->body_shape_exited, objid, obj, p_body_shape, p_local_shape); + emit_signal(SceneStringNames::get_singleton()->body_shape_exited, objid, node, p_body_shape, p_local_shape); } } } @@ -598,19 +615,24 @@ real_t RigidBody2D::get_inertia() const { void RigidBody2D::set_weight(real_t p_weight) { - set_mass(p_weight / real_t(GLOBAL_DEF("physics/2d/default_gravity", 98)) / 10); + set_mass(p_weight / (real_t(GLOBAL_DEF("physics/2d/default_gravity", 98)) / 10)); } real_t RigidBody2D::get_weight() const { - return mass * real_t(GLOBAL_DEF("physics/2d/default_gravity", 98)) / 10; + 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") + if (p_friction == 1.0) { // default value, don't create an override for that + return; + } + + ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physics material instead.") WARN_DEPRECATED + ERR_FAIL_COND(p_friction < 0 || p_friction > 1); if (physics_material_override.is_null()) { @@ -621,7 +643,7 @@ void RigidBody2D::set_friction(real_t p_friction) { } real_t RigidBody2D::get_friction() const { - ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physical material") + ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physics material instead.") WARN_DEPRECATED if (physics_material_override.is_null()) { @@ -633,7 +655,11 @@ real_t RigidBody2D::get_friction() const { 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") + if (p_bounce == 0.0) { // default value, don't create an override for that + return; + } + + ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physics material instead.") WARN_DEPRECATED ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1); @@ -646,7 +672,7 @@ void RigidBody2D::set_bounce(real_t p_bounce) { } real_t RigidBody2D::get_bounce() const { - ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physical material") + ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physics material instead.") WARN_DEPRECATED if (physics_material_override.is_null()) { @@ -1035,10 +1061,10 @@ void RigidBody2D::_bind_methods() { 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"); + ADD_PROPERTYNO(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01", 0), "set_friction", "get_friction"); + ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01", 0), "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_PROPERTYNZ(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"); @@ -1056,10 +1082,10 @@ void RigidBody2D::_bind_methods() { ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "applied_force"), "set_applied_force", "get_applied_force"); ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "applied_torque"), "set_applied_torque", "get_applied_torque"); - ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape"))); - ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape"))); - ADD_SIGNAL(MethodInfo("body_entered", PropertyInfo(Variant::OBJECT, "body"))); - ADD_SIGNAL(MethodInfo("body_exited", PropertyInfo(Variant::OBJECT, "body"))); + ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape"))); + ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape"))); + ADD_SIGNAL(MethodInfo("body_entered", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); + ADD_SIGNAL(MethodInfo("body_exited", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); ADD_SIGNAL(MethodInfo("sleeping_state_changed")); BIND_ENUM_CONSTANT(MODE_RIGID); diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h index 852963a721..ddd55f7b7f 100644 --- a/scene/2d/physics_body_2d.h +++ b/scene/2d/physics_body_2d.h @@ -31,10 +31,10 @@ #ifndef PHYSICS_BODY_2D_H #define PHYSICS_BODY_2D_H +#include "core/vset.h" #include "scene/2d/collision_object_2d.h" #include "scene/resources/physics_material.h" #include "servers/physics_2d_server.h" -#include "vset.h" class KinematicCollision2D; @@ -67,6 +67,7 @@ public: void set_collision_layer_bit(int p_bit, bool p_value); bool get_collision_layer_bit(int p_bit) const; + Array get_collision_exceptions(); void add_collision_exception_with(Node *p_node); //must be physicsbody void remove_collision_exception_with(Node *p_node); diff --git a/scene/2d/position_2d.cpp b/scene/2d/position_2d.cpp index 64d23719e7..543314eefa 100644 --- a/scene/2d/position_2d.cpp +++ b/scene/2d/position_2d.cpp @@ -30,7 +30,7 @@ #include "position_2d.h" -#include "engine.h" +#include "core/engine.h" #include "scene/resources/texture.h" void Position2D::_draw_cross() { diff --git a/scene/2d/ray_cast_2d.cpp b/scene/2d/ray_cast_2d.cpp index 9582c08110..f7c18a17df 100644 --- a/scene/2d/ray_cast_2d.cpp +++ b/scene/2d/ray_cast_2d.cpp @@ -31,7 +31,7 @@ #include "ray_cast_2d.h" #include "collision_object_2d.h" -#include "engine.h" +#include "core/engine.h" #include "physics_body_2d.h" #include "servers/physics_2d_server.h" @@ -218,6 +218,8 @@ void RayCast2D::_update_raycast_state() { against_shape = rr.shape; } else { collided = false; + against = 0; + against_shape = 0; } } diff --git a/scene/2d/screen_button.cpp b/scene/2d/screen_button.cpp index 45f63fd5bf..44a41328e8 100644 --- a/scene/2d/screen_button.cpp +++ b/scene/2d/screen_button.cpp @@ -29,9 +29,9 @@ /*************************************************************************/ #include "screen_button.h" -#include "input_map.h" -#include "os/input.h" -#include "os/os.h" +#include "core/input_map.h" +#include "core/os/input.h" +#include "core/os/os.h" void TouchScreenButton::set_texture(const Ref<Texture> &p_texture) { diff --git a/scene/2d/sprite.cpp b/scene/2d/sprite.cpp index bb5990fa79..04e199a21b 100644 --- a/scene/2d/sprite.cpp +++ b/scene/2d/sprite.cpp @@ -30,7 +30,7 @@ #include "sprite.h" #include "core/core_string_names.h" -#include "os/os.h" +#include "core/os/os.h" #include "scene/main/viewport.h" #include "scene/scene_string_names.h" diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 80565fa455..a7ac2bb982 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -30,9 +30,9 @@ #include "tile_map.h" -#include "io/marshalls.h" -#include "method_bind_ext.gen.inc" -#include "os/os.h" +#include "core/io/marshalls.h" +#include "core/method_bind_ext.gen.inc" +#include "core/os/os.h" #include "servers/physics_2d_server.h" int TileMap::_get_quadrant_size() const { @@ -257,7 +257,6 @@ void TileMap::update_dirty_quadrants() { VisualServer *vs = VisualServer::get_singleton(); Physics2DServer *ps = Physics2DServer::get_singleton(); Vector2 tofs = get_cell_draw_offset(); - Vector2 tcenter = cell_size / 2; Transform2D nav_rel; if (navigation) nav_rel = get_relative_transform_to_parent(navigation); @@ -305,7 +304,7 @@ void TileMap::update_dirty_quadrants() { } q.occluder_instances.clear(); Ref<ShaderMaterial> prev_material; - int prev_z_index; + int prev_z_index = 0; RID prev_canvas_item; RID prev_debug_canvas_item; @@ -594,7 +593,7 @@ void TileMap::update_dirty_quadrants() { if (quadrant_order_dirty) { - int index = -0x80000000; //always must be drawn below children + int index = -(int64_t)0x80000000; //always must be drawn below children for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { Quadrant &q = E->get(); @@ -726,7 +725,7 @@ void TileMap::set_cellv(const Vector2 &p_pos, int p_tile, bool p_flip_x, bool p_ set_cell(p_pos.x, p_pos.y, p_tile, p_flip_x, p_flip_y, p_transpose); } -void TileMap::set_celld(const Vector2 &p_pos, const Dictionary &p_data) { +void TileMap::_set_celld(const Vector2 &p_pos, const Dictionary &p_data) { set_cell(p_pos.x, p_pos.y, p_data["id"], p_data["flip_h"], p_data["flip_y"], p_data["transpose"], p_data["auto_coord"]); } @@ -1446,6 +1445,11 @@ Vector2 TileMap::world_to_map(const Vector2 &p_pos) const { default: {} } + // Account for precision errors on the border (GH-23250). + // 0.00005 is 5*CMP_EPSILON, results would start being unpredictible if + // cell size is > 15,000, but we can hardly have more precision anyway with + // floating point. + ret += Vector2(0.00005, 0.00005); return ret.floor(); } @@ -1612,7 +1616,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", "position", "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 52aa6e8e2a..499c79b180 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -31,11 +31,11 @@ #ifndef TILE_MAP_H #define TILE_MAP_H +#include "core/self_list.h" +#include "core/vset.h" #include "scene/2d/navigation2d.h" #include "scene/2d/node_2d.h" #include "scene/resources/tile_set.h" -#include "self_list.h" -#include "vset.h" class TileMap : public Node2D { @@ -242,7 +242,7 @@ public: void set_cell_autotile_coord(int p_x, int p_y, const Vector2 &p_coord); Vector2 get_cell_autotile_coord(int p_x, int p_y) const; - void set_celld(const Vector2 &p_pos, const Dictionary &p_data); + void _set_celld(const Vector2 &p_pos, const Dictionary &p_data); void set_cellv(const Vector2 &p_pos, int p_tile, bool p_flip_x = false, bool p_flip_y = false, bool p_transpose = false); int get_cellv(const Vector2 &p_pos) const; diff --git a/scene/2d/visibility_notifier_2d.cpp b/scene/2d/visibility_notifier_2d.cpp index ddca97e60a..7d7c47619a 100644 --- a/scene/2d/visibility_notifier_2d.cpp +++ b/scene/2d/visibility_notifier_2d.cpp @@ -30,7 +30,7 @@ #include "visibility_notifier_2d.h" -#include "engine.h" +#include "core/engine.h" #include "particles_2d.h" #include "scene/2d/animated_sprite.h" #include "scene/2d/physics_body_2d.h" diff --git a/scene/2d/visibility_notifier_2d.h b/scene/2d/visibility_notifier_2d.h index c4e12dfa22..04084b609a 100644 --- a/scene/2d/visibility_notifier_2d.h +++ b/scene/2d/visibility_notifier_2d.h @@ -43,7 +43,7 @@ class VisibilityNotifier2D : public Node2D { Rect2 rect; protected: - friend class SpatialIndexer2D; + friend struct SpatialIndexer2D; void _enter_viewport(Viewport *p_viewport); void _exit_viewport(Viewport *p_viewport); diff --git a/scene/3d/SCsub b/scene/3d/SCsub index 4008f4f196..35cc7479d8 100644 --- a/scene/3d/SCsub +++ b/scene/3d/SCsub @@ -2,9 +2,7 @@ Import('env') - if env['disable_3d']: - env.scene_sources.append("3d/spatial.cpp") env.scene_sources.append("3d/skeleton.cpp") env.scene_sources.append("3d/particles.cpp") @@ -12,5 +10,3 @@ if env['disable_3d']: env.scene_sources.append("3d/scenario_fx.cpp") else: env.add_source_files(env.scene_sources, "*.cpp") - -Export('env') diff --git a/scene/3d/area.cpp b/scene/3d/area.cpp index 6ea980ec97..40a1029201 100644 --- a/scene/3d/area.cpp +++ b/scene/3d/area.cpp @@ -243,7 +243,7 @@ void Area::_clear_monitoring() { emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E->key(), node, E->get().shapes[i].body_shape, E->get().shapes[i].area_shape); } - emit_signal(SceneStringNames::get_singleton()->body_exited, obj); + emit_signal(SceneStringNames::get_singleton()->body_exited, node); node->disconnect(SceneStringNames::get_singleton()->tree_entered, this, SceneStringNames::get_singleton()->_body_enter_tree); node->disconnect(SceneStringNames::get_singleton()->tree_exiting, this, SceneStringNames::get_singleton()->_body_exit_tree); @@ -699,10 +699,10 @@ void Area::_bind_methods() { ClassDB::bind_method(D_METHOD("set_reverb_uniformity", "amount"), &Area::set_reverb_uniformity); ClassDB::bind_method(D_METHOD("get_reverb_uniformity"), &Area::get_reverb_uniformity); - ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "area_shape"))); - ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "area_shape"))); - ADD_SIGNAL(MethodInfo("body_entered", PropertyInfo(Variant::OBJECT, "body"))); - ADD_SIGNAL(MethodInfo("body_exited", PropertyInfo(Variant::OBJECT, "body"))); + ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "area_shape"))); + ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "area_shape"))); + ADD_SIGNAL(MethodInfo("body_entered", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); + ADD_SIGNAL(MethodInfo("body_exited", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); ADD_SIGNAL(MethodInfo("area_shape_entered", PropertyInfo(Variant::INT, "area_id"), PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area"), PropertyInfo(Variant::INT, "area_shape"), PropertyInfo(Variant::INT, "self_shape"))); ADD_SIGNAL(MethodInfo("area_shape_exited", PropertyInfo(Variant::INT, "area_id"), PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area"), PropertyInfo(Variant::INT, "area_shape"), PropertyInfo(Variant::INT, "self_shape"))); diff --git a/scene/3d/area.h b/scene/3d/area.h index e49b7e493b..e1ff1079e3 100644 --- a/scene/3d/area.h +++ b/scene/3d/area.h @@ -31,8 +31,8 @@ #ifndef AREA_H #define AREA_H +#include "core/vset.h" #include "scene/3d/collision_object.h" -#include "vset.h" class Area : public CollisionObject { diff --git a/scene/3d/arvr_nodes.cpp b/scene/3d/arvr_nodes.cpp index 4bff26a200..2dc500f7ab 100644 --- a/scene/3d/arvr_nodes.cpp +++ b/scene/3d/arvr_nodes.cpp @@ -38,14 +38,14 @@ void ARVRCamera::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { - // need to find our ARVROrigin parent and let it know we're it's camera! + // need to find our ARVROrigin parent and let it know we're its camera! ARVROrigin *origin = Object::cast_to<ARVROrigin>(get_parent()); if (origin != NULL) { origin->set_tracked_camera(this); } }; break; case NOTIFICATION_EXIT_TREE: { - // need to find our ARVROrigin parent and let it know we're no longer it's camera! + // need to find our ARVROrigin parent and let it know we're no longer its camera! ARVROrigin *origin = Object::cast_to<ARVROrigin>(get_parent()); if (origin != NULL) { origin->clear_tracked_camera_if(this); diff --git a/scene/3d/arvr_nodes.h b/scene/3d/arvr_nodes.h index 67fb658562..d6690676cc 100644 --- a/scene/3d/arvr_nodes.h +++ b/scene/3d/arvr_nodes.h @@ -62,7 +62,7 @@ public: }; /* - ARVRController is a helper node that automatically updates it's position based on tracker data. + ARVRController is a helper node that automatically updates its position based on tracker data. It must be a child node of our ARVROrigin node */ @@ -102,7 +102,7 @@ public: }; /* - ARVRAnchor is a helper node that automatically updates it's position based on anchor data, it represents a real world location. + ARVRAnchor is a helper node that automatically updates its position based on anchor data, it represents a real world location. It must be a child node of our ARVROrigin node */ diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index 8504a18f54..3046cad624 100644 --- a/scene/3d/audio_stream_player_3d.cpp +++ b/scene/3d/audio_stream_player_3d.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "audio_stream_player_3d.h" -#include "engine.h" +#include "core/engine.h" #include "scene/3d/area.h" #include "scene/3d/camera.h" #include "scene/main/viewport.h" @@ -290,7 +290,7 @@ void AudioStreamPlayer3D::_notification(int p_what) { PhysicsDirectSpaceState::ShapeResult sr[MAX_INTERSECT_AREAS]; - int areas = space_state->intersect_point(global_pos, sr, MAX_INTERSECT_AREAS, Set<RID>(), area_mask); + int areas = space_state->intersect_point(global_pos, sr, MAX_INTERSECT_AREAS, Set<RID>(), area_mask, false, true); Area *area = NULL; for (int i = 0; i < areas; i++) { @@ -417,7 +417,7 @@ void AudioStreamPlayer3D::_notification(int p_what) { } } - for (int k = 0; k < cc; k++) { + for (unsigned int k = 0; k < cc; k++) { output.vol[k] *= multiplier; } @@ -448,7 +448,7 @@ void AudioStreamPlayer3D::_notification(int p_what) { //float dist_att_db = -20 * Math::log(dist + 0.00001); //logarithmic attenuation, like in real life - float center_val[3] = { 0.5, 0.25, 0.16666 }; + float center_val[3] = { 0.5f, 0.25f, 0.16666f }; AudioFrame center_frame(center_val[vol_index_max - 1], center_val[vol_index_max - 1]); if (attenuation < 1.0) { @@ -814,7 +814,7 @@ AudioStreamPlayer3D::AttenuationModel AudioStreamPlayer3D::get_attenuation_model void AudioStreamPlayer3D::set_out_of_range_mode(OutOfRangeMode p_mode) { - ERR_FAIL_INDEX(p_mode, 2); + ERR_FAIL_INDEX((int)p_mode, 2); out_of_range_mode = p_mode; } diff --git a/scene/3d/baked_lightmap.cpp b/scene/3d/baked_lightmap.cpp index 2cb59c871c..62589bd67e 100644 --- a/scene/3d/baked_lightmap.cpp +++ b/scene/3d/baked_lightmap.cpp @@ -29,9 +29,10 @@ /*************************************************************************/ #include "baked_lightmap.h" -#include "io/resource_saver.h" -#include "os/dir_access.h" -#include "os/os.h" +#include "core/io/config_file.h" +#include "core/io/resource_saver.h" +#include "core/os/dir_access.h" +#include "core/os/os.h" #include "voxel_light_baker.h" void BakedLightmapData::set_bounds(const AABB &p_bounds) { @@ -365,7 +366,7 @@ BakedLightmap::BakeError BakedLightmap::bake(Node *p_from_node, bool p_create_vi { bake_bounds = AABB(-extents, extents * 2.0); int subdiv = nearest_power_of_2_templated(int(bake_bounds.get_longest_axis_size() / bake_cell_size)); - bake_bounds.size[bake_bounds.get_longest_axis_size()] = subdiv * bake_cell_size; + bake_bounds.size[bake_bounds.get_longest_axis_index()] = subdiv * bake_cell_size; bake_subdiv = nearest_shift(subdiv) + 1; capture_subdiv = bake_subdiv; @@ -526,21 +527,60 @@ BakedLightmap::BakeError BakedLightmap::bake(Node *p_from_node, bool p_create_vi tex_flags |= Texture::FLAG_CONVERT_TO_LINEAR; } - Ref<ImageTexture> tex; - String image_path = save_path.plus_file(mesh_name + ".tex"); - bool set_path = true; - if (ResourceCache::has(image_path)) { - tex = Ref<Resource>((Resource *)ResourceCache::get(image_path)); - set_path = false; - } + String image_path = save_path.plus_file(mesh_name); + Ref<Texture> texture; - if (!tex.is_valid()) { - tex.instance(); - } + if (ResourceLoader::import) { + + bool srgb = false; + if (false && hdr) { + //save hdr + } else { + image_path += ".png"; + print_line("image path saving png: " + image_path); + image->save_png(image_path); + srgb = true; + } - tex->create_from_image(image, tex_flags); + if (!FileAccess::exists(image_path + ".import")) { + Ref<ConfigFile> config; + config.instance(); + config->set_value("remap", "importer", "texture"); + config->set_value("remap", "type", "StreamTexture"); + config->set_value("params", "compress/mode", 2); + config->set_value("params", "detect_3d", false); + config->set_value("params", "flags/repeat", false); + config->set_value("params", "flags/filter", true); + config->set_value("params", "flags/mipmaps", false); + config->set_value("params", "flags/srgb", srgb); + + config->save(image_path + ".import"); + } + + ResourceLoader::import(image_path); + texture = ResourceLoader::load(image_path); //if already loaded, it will be updated on refocus? + } else { - err = ResourceSaver::save(image_path, tex, ResourceSaver::FLAG_CHANGE_PATH); + image_path += ".text"; + Ref<ImageTexture> tex; + bool set_path = true; + if (ResourceCache::has(image_path)) { + tex = Ref<Resource>((Resource *)ResourceCache::get(image_path)); + set_path = false; + } + + if (!tex.is_valid()) { + tex.instance(); + } + + tex->create_from_image(image, tex_flags); + + err = ResourceSaver::save(image_path, tex, ResourceSaver::FLAG_CHANGE_PATH); + if (set_path) { + tex->set_path(image_path); + } + texture = tex; + } if (err != OK) { if (bake_end_function) { bake_end_function(); @@ -548,10 +588,7 @@ BakedLightmap::BakeError BakedLightmap::bake(Node *p_from_node, bool p_create_vi ERR_FAIL_COND_V(err != OK, BAKE_ERROR_CANT_CREATE_IMAGE); } - if (set_path) { - tex->set_path(image_path); - } - new_light_data->add_user(E->get().path, tex, E->get().instance_idx); + new_light_data->add_user(E->get().path, texture, E->get().instance_idx); } } diff --git a/scene/3d/camera.cpp b/scene/3d/camera.cpp index a4582b7d7d..8ef64e2e80 100644 --- a/scene/3d/camera.cpp +++ b/scene/3d/camera.cpp @@ -30,9 +30,9 @@ #include "camera.h" -#include "camera_matrix.h" #include "collision_object.h" -#include "engine.h" +#include "core/engine.h" +#include "core/math/camera_matrix.h" #include "scene/resources/material.h" #include "scene/resources/surface_tool.h" void Camera::_update_audio_listener_state() { @@ -553,11 +553,13 @@ Camera::Projection Camera::get_projection() const { void Camera::set_fov(float p_fov) { fov = p_fov; _update_camera_mode(); + _change_notify("fov"); } void Camera::set_size(float p_size) { size = p_size; _update_camera_mode(); + _change_notify("size"); } void Camera::set_znear(float p_znear) { diff --git a/scene/3d/collision_object.cpp b/scene/3d/collision_object.cpp index e19e45b263..99b8ce0567 100644 --- a/scene/3d/collision_object.cpp +++ b/scene/3d/collision_object.cpp @@ -148,7 +148,7 @@ void CollisionObject::_bind_methods() { BIND_VMETHOD(MethodInfo("_input_event", PropertyInfo(Variant::OBJECT, "camera"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), PropertyInfo(Variant::VECTOR3, "click_position"), PropertyInfo(Variant::VECTOR3, "click_normal"), PropertyInfo(Variant::INT, "shape_idx"))); - ADD_SIGNAL(MethodInfo("input_event", PropertyInfo(Variant::OBJECT, "camera"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), PropertyInfo(Variant::VECTOR3, "click_position"), PropertyInfo(Variant::VECTOR3, "click_normal"), PropertyInfo(Variant::INT, "shape_idx"))); + ADD_SIGNAL(MethodInfo("input_event", PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), PropertyInfo(Variant::VECTOR3, "click_position"), PropertyInfo(Variant::VECTOR3, "click_normal"), PropertyInfo(Variant::INT, "shape_idx"))); ADD_SIGNAL(MethodInfo("mouse_entered")); ADD_SIGNAL(MethodInfo("mouse_exited")); diff --git a/scene/3d/collision_shape.cpp b/scene/3d/collision_shape.cpp index 943f4158f7..4fd68fb47d 100644 --- a/scene/3d/collision_shape.cpp +++ b/scene/3d/collision_shape.cpp @@ -38,9 +38,9 @@ #include "scene/resources/sphere_shape.h" #include "servers/visual_server.h" //TODO: Implement CylinderShape and HeightMapShape? +#include "core/math/quick_hull.h" #include "mesh_instance.h" #include "physics_body.h" -#include "quick_hull.h" void CollisionShape::make_convex_from_brothers() { diff --git a/scene/3d/cpu_particles.cpp b/scene/3d/cpu_particles.cpp index af8bfcbe9b..e17e65c97e 100644 --- a/scene/3d/cpu_particles.cpp +++ b/scene/3d/cpu_particles.cpp @@ -1,9 +1,38 @@ +/*************************************************************************/ +/* cpu_particles.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 "cpu_particles.h" -#include "particles.h" #include "scene/3d/camera.h" -#include "scene/main/viewport.h" -#include "scene/resources/surface_tool.h" +#include "scene/3d/particles.h" +#include "scene/resources/particles_material.h" #include "servers/visual_server.h" AABB CPUParticles::get_aabb() const { @@ -442,10 +471,6 @@ static float rand_from_seed(uint32_t &seed) { return float(seed % uint32_t(65536)) / 65535.0; } -float rand_from_seed_m1_p1(uint32_t &seed) { - return rand_from_seed(seed) * 2.0 - 1.0; -} - void CPUParticles::_particles_process(float p_delta) { p_delta *= speed_scale; @@ -567,7 +592,7 @@ void CPUParticles::_particles_process(float p_delta) { Vector3 direction_xz = Vector3(Math::sin(angle1_rad), 0, Math::cos(angle1_rad)); Vector3 direction_yz = Vector3(0, Math::sin(angle2_rad), Math::cos(angle2_rad)); - direction_yz.z = direction_yz.z / Math::sqrt(direction_yz.z); //better uniform distribution + direction_yz.z = direction_yz.z / MAX(0.0001, Math::sqrt(ABS(direction_yz.z))); //better uniform distribution Vector3 direction = Vector3(direction_xz.x * direction_yz.z, direction_yz.y, direction_xz.z * direction_yz.z); direction.normalize(); p.velocity = direction * parameters[PARAM_INITIAL_LINEAR_VELOCITY] * Math::lerp(1.0f, float(Math::randf()), randomness[PARAM_INITIAL_LINEAR_VELOCITY]); @@ -756,12 +781,6 @@ void CPUParticles::_particles_process(float p_delta) { base_angle += p.custom[1] * lifetime * (parameters[PARAM_ANGULAR_VELOCITY] + tex_angular_velocity) * Math::lerp(1.0f, rand_from_seed(alt_seed) * 2.0f - 1.0f, randomness[PARAM_ANGULAR_VELOCITY]); p.custom[0] = Math::deg2rad(base_angle); //angle p.custom[2] = (parameters[PARAM_ANIM_OFFSET] + tex_anim_offset) * Math::lerp(1.0f, p.anim_offset_rand, randomness[PARAM_ANIM_OFFSET]) + p.custom[1] * (parameters[PARAM_ANIM_SPEED] + tex_anim_speed) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_ANIM_SPEED]); //angle - if (flags[FLAG_ANIM_LOOP]) { - p.custom[2] = Math::fmod(p.custom[2], 1.0f); //loop - - } else { - p.custom[2] = CLAMP(p.custom[2], 0.0f, 1.0); //0 to 1 only - } } //apply color //apply hue rotation @@ -952,6 +971,8 @@ void CPUParticles::_update_particle_data_buffer() { ptr += 17; } + + can_update = true; } #ifndef NO_THREADS @@ -964,8 +985,10 @@ void CPUParticles::_update_render_thread() { #ifndef NO_THREADS update_mutex->lock(); #endif - - VS::get_singleton()->multimesh_set_as_bulk_array(multimesh, particle_data); + if (can_update) { + VS::get_singleton()->multimesh_set_as_bulk_array(multimesh, particle_data); + can_update = false; //wait for next time + } #ifndef NO_THREADS update_mutex->unlock(); @@ -1007,7 +1030,7 @@ void CPUParticles::_notification(int p_what) { if (p_what == NOTIFICATION_INTERNAL_PROCESS) { - if (particles.size() == 0) + if (particles.size() == 0 || !is_visible_in_tree()) return; float delta = get_process_delta_time(); @@ -1036,6 +1059,8 @@ void CPUParticles::_notification(int p_what) { } } + bool processed = false; + if (time == 0 && pre_process_time > 0.0) { float frame_time; @@ -1048,6 +1073,7 @@ void CPUParticles::_notification(int p_what) { while (todo >= 0) { _particles_process(frame_time); + processed = true; todo -= frame_time; } } @@ -1066,6 +1092,7 @@ void CPUParticles::_notification(int p_what) { while (todo >= frame_time) { _particles_process(frame_time); + processed = true; todo -= decr; } @@ -1073,9 +1100,12 @@ void CPUParticles::_notification(int p_what) { } else { _particles_process(delta); + processed = true; } - _update_particle_data_buffer(); + if (processed) { + _update_particle_data_buffer(); + } } } @@ -1115,7 +1145,6 @@ void CPUParticles::convert_from_particles(Node *p_particles) { set_particle_flag(FLAG_ALIGN_Y_TO_VELOCITY, material->get_flag(ParticlesMaterial::FLAG_ALIGN_Y_TO_VELOCITY)); set_particle_flag(FLAG_ROTATE_Y, material->get_flag(ParticlesMaterial::FLAG_ROTATE_Y)); set_particle_flag(FLAG_DISABLE_Z, material->get_flag(ParticlesMaterial::FLAG_DISABLE_Z)); - set_particle_flag(FLAG_ANIM_LOOP, material->get_flag(ParticlesMaterial::FLAG_ANIM_LOOP)); set_emission_shape(EmissionShape(material->get_emission_shape())); set_emission_sphere_radius(material->get_emission_sphere_radius()); @@ -1188,7 +1217,7 @@ void CPUParticles::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::REAL, "lifetime", PROPERTY_HINT_EXP_RANGE, "0.01,600.0,0.01"), "set_lifetime", "get_lifetime"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "preprocess", PROPERTY_HINT_EXP_RANGE, "0.00,600.0,0.01"), "set_pre_process_time", "get_pre_process_time"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "speed_scale", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_speed_scale", "get_speed_scale"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "speed_scale", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_speed_scale", "get_speed_scale"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio"); ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_fps", PROPERTY_HINT_RANGE, "0,1000,1"), "set_fixed_fps", "get_fixed_fps"); @@ -1321,7 +1350,6 @@ void CPUParticles::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_offset", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_ANIM_OFFSET); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_offset_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_OFFSET); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_offset_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANIM_OFFSET); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "anim_loop"), "set_particle_flag", "get_particle_flag", FLAG_ANIM_LOOP); BIND_ENUM_CONSTANT(PARAM_INITIAL_LINEAR_VELOCITY); BIND_ENUM_CONSTANT(PARAM_ANGULAR_VELOCITY); @@ -1399,6 +1427,8 @@ CPUParticles::CPUParticles() { flags[i] = false; } + can_update = false; + set_color(Color(1, 1, 1, 1)); #ifndef NO_THREADS diff --git a/scene/3d/cpu_particles.h b/scene/3d/cpu_particles.h index 1ee709719d..2e83924dfc 100644 --- a/scene/3d/cpu_particles.h +++ b/scene/3d/cpu_particles.h @@ -1,9 +1,38 @@ +/*************************************************************************/ +/* cpu_particles.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 CPU_PARTICLES_H #define CPU_PARTICLES_H -#include "rid.h" + +#include "core/rid.h" #include "scene/3d/visual_instance.h" -#include "scene/main/timer.h" -#include "scene/resources/material.h" /** @author Juan Linietsky <reduzio@gmail.com> @@ -41,7 +70,6 @@ public: FLAG_ALIGN_Y_TO_VELOCITY, FLAG_ROTATE_Y, FLAG_DISABLE_Z, - FLAG_ANIM_LOOP, FLAG_MAX }; @@ -113,6 +141,8 @@ private: int fixed_fps; bool fractional_delta; + volatile bool can_update; + DrawOrder draw_order; Ref<Mesh> mesh; @@ -139,7 +169,6 @@ private: PoolVector<Color> emission_colors; int emission_point_count; - bool anim_loop; Vector3 gravity; void _particles_process(float p_delta); diff --git a/scene/3d/interpolated_camera.cpp b/scene/3d/interpolated_camera.cpp index ffa283f634..93832f8e00 100644 --- a/scene/3d/interpolated_camera.cpp +++ b/scene/3d/interpolated_camera.cpp @@ -30,7 +30,7 @@ #include "interpolated_camera.h" -#include "engine.h" +#include "core/engine.h" void InterpolatedCamera::_notification(int p_what) { diff --git a/scene/3d/light.cpp b/scene/3d/light.cpp index 16164cf3bf..11d61315ba 100644 --- a/scene/3d/light.cpp +++ b/scene/3d/light.cpp @@ -30,8 +30,8 @@ #include "light.h" -#include "engine.h" -#include "project_settings.h" +#include "core/engine.h" +#include "core/project_settings.h" #include "scene/resources/surface_tool.h" bool Light::_can_gizmo_scale() const { @@ -48,6 +48,13 @@ void Light::set_param(Param p_param, float p_value) { if (p_param == PARAM_SPOT_ANGLE || p_param == PARAM_RANGE) { update_gizmo(); + + if (p_param == PARAM_SPOT_ANGLE) { + _change_notify("spot_angle"); + } else if (p_param == PARAM_RANGE) { + _change_notify("omni_range"); + _change_notify("spot_range"); + } } } @@ -172,7 +179,8 @@ void Light::_update_visibility() { } #endif - //VS::get_singleton()->instance_light_set_enabled(get_instance(),is_visible_in_tree() && editor_ok); + VS::get_singleton()->instance_set_visible(get_instance(), is_visible_in_tree() && editor_ok); + _change_notify("geometry/visible"); } @@ -312,7 +320,7 @@ Light::Light(VisualServer::LightType p_type) { Light::Light() { type = VisualServer::LIGHT_DIRECTIONAL; - ERR_PRINT("Light shouldn't be instanced dircetly, use the subtypes."); + ERR_PRINT("Light should not be instanced directly; use the DirectionalLight, OmniLight or SpotLight subtypes instead."); } Light::~Light() { diff --git a/scene/3d/mesh_instance.cpp b/scene/3d/mesh_instance.cpp index e277cae5b7..cf0317cd58 100644 --- a/scene/3d/mesh_instance.cpp +++ b/scene/3d/mesh_instance.cpp @@ -31,7 +31,7 @@ #include "mesh_instance.h" #include "collision_shape.h" -#include "core_string_names.h" +#include "core/core_string_names.h" #include "physics_body.h" #include "scene/resources/material.h" #include "scene/scene_string_names.h" @@ -253,6 +253,11 @@ void MeshInstance::_notification(int p_what) { } } +int MeshInstance::get_surface_material_count() const { + + return materials.size(); +} + void MeshInstance::set_surface_material(int p_surface, const Ref<Material> &p_material) { ERR_FAIL_INDEX(p_surface, materials.size()); @@ -359,6 +364,7 @@ void MeshInstance::_bind_methods() { ClassDB::bind_method(D_METHOD("set_skeleton_path", "skeleton_path"), &MeshInstance::set_skeleton_path); ClassDB::bind_method(D_METHOD("get_skeleton_path"), &MeshInstance::get_skeleton_path); + ClassDB::bind_method(D_METHOD("get_surface_material_count"), &MeshInstance::get_surface_material_count); ClassDB::bind_method(D_METHOD("set_surface_material", "surface", "material"), &MeshInstance::set_surface_material); ClassDB::bind_method(D_METHOD("get_surface_material", "surface"), &MeshInstance::get_surface_material); diff --git a/scene/3d/mesh_instance.h b/scene/3d/mesh_instance.h index 0dfec538f9..0b5b4b9e7b 100644 --- a/scene/3d/mesh_instance.h +++ b/scene/3d/mesh_instance.h @@ -76,6 +76,7 @@ public: void set_skeleton_path(const NodePath &p_skeleton); NodePath get_skeleton_path(); + int get_surface_material_count() const; void set_surface_material(int p_surface, const Ref<Material> &p_material); Ref<Material> get_surface_material(int p_surface) const; diff --git a/scene/3d/navigation.cpp b/scene/3d/navigation.cpp index 8d84d2408c..6e7b372647 100644 --- a/scene/3d/navigation.cpp +++ b/scene/3d/navigation.cpp @@ -30,6 +30,8 @@ #include "navigation.h" +#define USE_ENTRY_POINT + void Navigation::_navmesh_link(int p_id) { ERR_FAIL_COND(!navmesh_map.has(p_id)); @@ -331,7 +333,18 @@ Vector<Vector3> Navigation::get_simple_path(const Vector3 &p_start, const Vector if (begin_poly->edges[i].C) { begin_poly->edges[i].C->prev_edge = begin_poly->edges[i].C_edge; +#ifdef USE_ENTRY_POINT + Vector3 edge[2] = { + _get_vertex(begin_poly->edges[i].point), + _get_vertex(begin_poly->edges[(i + 1) % begin_poly->edges.size()].point) + }; + + Vector3 entry = Geometry::get_closest_point_to_segment(begin_poly->entry, edge); + begin_poly->edges[i].C->distance = begin_poly->entry.distance_to(entry); + begin_poly->edges[i].C->entry = entry; +#else begin_poly->edges[i].C->distance = begin_poly->center.distance_to(begin_poly->edges[i].C->center); +#endif open_list.push_back(begin_poly->edges[i].C); if (begin_poly->edges[i].C == end_poly) { @@ -356,10 +369,33 @@ Vector<Vector3> Navigation::get_simple_path(const Vector3 &p_start, const Vector Polygon *p = E->get(); float cost = p->distance; - cost += p->center.distance_to(end_point); +#ifdef USE_ENTRY_POINT + int es = p->edges.size(); - if (cost < least_cost) { + float shortest_distance = 1e30; + + for (int i = 0; i < es; i++) { + Polygon::Edge &e = p->edges.write[i]; + + if (!e.C) + continue; + Vector3 edge[2] = { + _get_vertex(p->edges[i].point), + _get_vertex(p->edges[(i + 1) % es].point) + }; + + Vector3 edge_point = Geometry::get_closest_point_to_segment(p->entry, edge); + float dist = p->entry.distance_to(edge_point); + if (dist < shortest_distance) + shortest_distance = dist; + } + + cost += shortest_distance; +#else + cost += p->center.distance_to(end_point); +#endif + if (cost < least_cost) { least_cost_poly = E; least_cost = cost; } @@ -527,7 +563,6 @@ Vector3 Navigation::get_closest_point_to_segment(const Vector3 &p_from, const Ve bool use_collision = p_use_collision; Vector3 closest_point; float closest_point_d = 1e20; - NavMesh *closest_navmesh = NULL; for (Map<int, NavMesh>::Element *E = navmesh_map.front(); E; E = E->next()) { @@ -546,12 +581,10 @@ Vector3 Navigation::get_closest_point_to_segment(const Vector3 &p_from, const Ve closest_point = inters; use_collision = true; closest_point_d = p_from.distance_to(inters); - closest_navmesh = p.owner; } else if (closest_point_d > inters.distance_to(p_from)) { closest_point = inters; closest_point_d = p_from.distance_to(inters); - closest_navmesh = p.owner; } } } @@ -569,7 +602,6 @@ Vector3 Navigation::get_closest_point_to_segment(const Vector3 &p_from, const Ve closest_point_d = d; closest_point = b; - closest_navmesh = p.owner; } } } diff --git a/scene/3d/navigation.h b/scene/3d/navigation.h index 5a501039c8..8f200997cd 100644 --- a/scene/3d/navigation.h +++ b/scene/3d/navigation.h @@ -94,6 +94,7 @@ class Navigation : public Spatial { Vector<Edge> edges; Vector3 center; + Vector3 entry; float distance; int prev_edge; diff --git a/scene/3d/particles.cpp b/scene/3d/particles.cpp index 4900692155..10b26778ef 100644 --- a/scene/3d/particles.cpp +++ b/scene/3d/particles.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "particles.h" -#include "scene/resources/surface_tool.h" + #include "servers/visual_server.h" AABB Particles::get_aabb() const { @@ -329,7 +329,7 @@ void Particles::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::REAL, "lifetime", PROPERTY_HINT_EXP_RANGE, "0.01,600.0,0.01"), "set_lifetime", "get_lifetime"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "preprocess", PROPERTY_HINT_EXP_RANGE, "0.00,600.0,0.01"), "set_pre_process_time", "get_pre_process_time"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "speed_scale", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_speed_scale", "get_speed_scale"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "speed_scale", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_speed_scale", "get_speed_scale"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio"); ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_fps", PROPERTY_HINT_RANGE, "0,1000,1"), "set_fixed_fps", "get_fixed_fps"); @@ -378,1219 +378,3 @@ Particles::~Particles() { VS::get_singleton()->free(particles); } - -////////////////////////////////////// - -Mutex *ParticlesMaterial::material_mutex = NULL; -SelfList<ParticlesMaterial>::List ParticlesMaterial::dirty_materials; -Map<ParticlesMaterial::MaterialKey, ParticlesMaterial::ShaderData> ParticlesMaterial::shader_map; -ParticlesMaterial::ShaderNames *ParticlesMaterial::shader_names = NULL; - -void ParticlesMaterial::init_shaders() { - -#ifndef NO_THREADS - material_mutex = Mutex::create(); -#endif - - shader_names = memnew(ShaderNames); - - shader_names->spread = "spread"; - shader_names->flatness = "flatness"; - shader_names->initial_linear_velocity = "initial_linear_velocity"; - shader_names->initial_angle = "initial_angle"; - shader_names->angular_velocity = "angular_velocity"; - shader_names->orbit_velocity = "orbit_velocity"; - shader_names->linear_accel = "linear_accel"; - shader_names->radial_accel = "radial_accel"; - shader_names->tangent_accel = "tangent_accel"; - shader_names->damping = "damping"; - shader_names->scale = "scale"; - shader_names->hue_variation = "hue_variation"; - shader_names->anim_speed = "anim_speed"; - shader_names->anim_offset = "anim_offset"; - - shader_names->initial_linear_velocity_random = "initial_linear_velocity_random"; - shader_names->initial_angle_random = "initial_angle_random"; - shader_names->angular_velocity_random = "angular_velocity_random"; - shader_names->orbit_velocity_random = "orbit_velocity_random"; - shader_names->linear_accel_random = "linear_accel_random"; - shader_names->radial_accel_random = "radial_accel_random"; - shader_names->tangent_accel_random = "tangent_accel_random"; - shader_names->damping_random = "damping_random"; - shader_names->scale_random = "scale_random"; - shader_names->hue_variation_random = "hue_variation_random"; - shader_names->anim_speed_random = "anim_speed_random"; - shader_names->anim_offset_random = "anim_offset_random"; - - shader_names->angle_texture = "angle_texture"; - shader_names->angular_velocity_texture = "angular_velocity_texture"; - shader_names->orbit_velocity_texture = "orbit_velocity_texture"; - shader_names->linear_accel_texture = "linear_accel_texture"; - shader_names->radial_accel_texture = "radial_accel_texture"; - shader_names->tangent_accel_texture = "tangent_accel_texture"; - shader_names->damping_texture = "damping_texture"; - shader_names->scale_texture = "scale_texture"; - shader_names->hue_variation_texture = "hue_variation_texture"; - shader_names->anim_speed_texture = "anim_speed_texture"; - shader_names->anim_offset_texture = "anim_offset_texture"; - - shader_names->color = "color_value"; - shader_names->color_ramp = "color_ramp"; - - shader_names->emission_sphere_radius = "emission_sphere_radius"; - shader_names->emission_box_extents = "emission_box_extents"; - shader_names->emission_texture_point_count = "emission_texture_point_count"; - shader_names->emission_texture_points = "emission_texture_points"; - shader_names->emission_texture_normal = "emission_texture_normal"; - shader_names->emission_texture_color = "emission_texture_color"; - - shader_names->trail_divisor = "trail_divisor"; - shader_names->trail_size_modifier = "trail_size_modifier"; - shader_names->trail_color_modifier = "trail_color_modifier"; - - shader_names->gravity = "gravity"; -} - -void ParticlesMaterial::finish_shaders() { - -#ifndef NO_THREADS - memdelete(material_mutex); -#endif - - memdelete(shader_names); -} - -void ParticlesMaterial::_update_shader() { - - dirty_materials.remove(&element); - - MaterialKey mk = _compute_key(); - if (mk.key == current_key.key) - return; //no update required in the end - - if (shader_map.has(current_key)) { - shader_map[current_key].users--; - if (shader_map[current_key].users == 0) { - //deallocate shader, as it's no longer in use - VS::get_singleton()->free(shader_map[current_key].shader); - shader_map.erase(current_key); - } - } - - current_key = mk; - - if (shader_map.has(mk)) { - - VS::get_singleton()->material_set_shader(_get_material(), shader_map[mk].shader); - shader_map[mk].users++; - return; - } - - //must create a shader! - - String code = "shader_type particles;\n"; - - code += "uniform float spread;\n"; - code += "uniform float flatness;\n"; - code += "uniform float initial_linear_velocity;\n"; - code += "uniform float initial_angle;\n"; - code += "uniform float angular_velocity;\n"; - code += "uniform float orbit_velocity;\n"; - code += "uniform float linear_accel;\n"; - code += "uniform float radial_accel;\n"; - code += "uniform float tangent_accel;\n"; - code += "uniform float damping;\n"; - code += "uniform float scale;\n"; - code += "uniform float hue_variation;\n"; - code += "uniform float anim_speed;\n"; - code += "uniform float anim_offset;\n"; - - code += "uniform float initial_linear_velocity_random;\n"; - code += "uniform float initial_angle_random;\n"; - code += "uniform float angular_velocity_random;\n"; - code += "uniform float orbit_velocity_random;\n"; - code += "uniform float linear_accel_random;\n"; - code += "uniform float radial_accel_random;\n"; - code += "uniform float tangent_accel_random;\n"; - code += "uniform float damping_random;\n"; - code += "uniform float scale_random;\n"; - code += "uniform float hue_variation_random;\n"; - code += "uniform float anim_speed_random;\n"; - code += "uniform float anim_offset_random;\n"; - - switch (emission_shape) { - case EMISSION_SHAPE_POINT: { - //do none - } break; - case EMISSION_SHAPE_SPHERE: { - code += "uniform float emission_sphere_radius;\n"; - } break; - case EMISSION_SHAPE_BOX: { - code += "uniform vec3 emission_box_extents;\n"; - } break; - case EMISSION_SHAPE_DIRECTED_POINTS: { - code += "uniform sampler2D emission_texture_normal : hint_black;\n"; - } //fallthrough - case EMISSION_SHAPE_POINTS: { - code += "uniform sampler2D emission_texture_points : hint_black;\n"; - code += "uniform int emission_texture_point_count;\n"; - if (emission_color_texture.is_valid()) { - code += "uniform sampler2D emission_texture_color : hint_white;\n"; - } - } break; - } - - code += "uniform vec4 color_value : hint_color;\n"; - - code += "uniform int trail_divisor;\n"; - - code += "uniform vec3 gravity;\n"; - - if (color_ramp.is_valid()) - code += "uniform sampler2D color_ramp;\n"; - - if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) - code += "uniform sampler2D linear_velocity_texture;\n"; - if (tex_parameters[PARAM_ORBIT_VELOCITY].is_valid()) - code += "uniform sampler2D orbit_velocity_texture;\n"; - if (tex_parameters[PARAM_ANGULAR_VELOCITY].is_valid()) - code += "uniform sampler2D angular_velocity_texture;\n"; - if (tex_parameters[PARAM_LINEAR_ACCEL].is_valid()) - code += "uniform sampler2D linear_accel_texture;\n"; - if (tex_parameters[PARAM_RADIAL_ACCEL].is_valid()) - code += "uniform sampler2D radial_accel_texture;\n"; - if (tex_parameters[PARAM_TANGENTIAL_ACCEL].is_valid()) - code += "uniform sampler2D tangent_accel_texture;\n"; - if (tex_parameters[PARAM_DAMPING].is_valid()) - code += "uniform sampler2D damping_texture;\n"; - if (tex_parameters[PARAM_ANGLE].is_valid()) - code += "uniform sampler2D angle_texture;\n"; - if (tex_parameters[PARAM_SCALE].is_valid()) - code += "uniform sampler2D scale_texture;\n"; - if (tex_parameters[PARAM_HUE_VARIATION].is_valid()) - code += "uniform sampler2D hue_variation_texture;\n"; - if (tex_parameters[PARAM_ANIM_SPEED].is_valid()) - code += "uniform sampler2D anim_speed_texture;\n"; - if (tex_parameters[PARAM_ANIM_OFFSET].is_valid()) - code += "uniform sampler2D anim_offset_texture;\n"; - - if (trail_size_modifier.is_valid()) { - code += "uniform sampler2D trail_size_modifier;\n"; - } - - if (trail_color_modifier.is_valid()) { - code += "uniform sampler2D trail_color_modifier;\n"; - } - - //need a random function - code += "\n\n"; - code += "float rand_from_seed(inout uint seed) {\n"; - code += " int k;\n"; - code += " int s = int(seed);\n"; - code += " if (s == 0)\n"; - code += " s = 305420679;\n"; - code += " k = s / 127773;\n"; - code += " s = 16807 * (s - k * 127773) - 2836 * k;\n"; - code += " if (s < 0)\n"; - code += " s += 2147483647;\n"; - code += " seed = uint(s);\n"; - code += " return float(seed % uint(65536))/65535.0;\n"; - code += "}\n"; - code += "\n"; - - code += "float rand_from_seed_m1_p1(inout uint seed) {\n"; - code += " return rand_from_seed(seed)*2.0-1.0;\n"; - code += "}\n"; - code += "\n"; - - //improve seed quality - code += "uint hash(uint x) {\n"; - code += " x = ((x >> uint(16)) ^ x) * uint(73244475);\n"; - code += " x = ((x >> uint(16)) ^ x) * uint(73244475);\n"; - code += " x = (x >> uint(16)) ^ x;\n"; - code += " return x;\n"; - code += "}\n"; - code += "\n"; - - code += "void vertex() {\n"; - code += " uint base_number = NUMBER/uint(trail_divisor);\n"; - code += " uint alt_seed = hash(base_number+uint(1)+RANDOM_SEED);\n"; - code += " float angle_rand = rand_from_seed(alt_seed);\n"; - code += " float scale_rand = rand_from_seed(alt_seed);\n"; - code += " float hue_rot_rand = rand_from_seed(alt_seed);\n"; - code += " float anim_offset_rand = rand_from_seed(alt_seed);\n"; - code += " float pi = 3.14159;\n"; - code += " float degree_to_rad = pi / 180.0;\n"; - code += "\n"; - - if (emission_shape >= EMISSION_SHAPE_POINTS) { - code += " int point = min(emission_texture_point_count-1,int(rand_from_seed(alt_seed) * float(emission_texture_point_count)));\n"; - code += " ivec2 emission_tex_size = textureSize( emission_texture_points, 0 );\n"; - code += " ivec2 emission_tex_ofs = ivec2( point % emission_tex_size.x, point / emission_tex_size.x );\n"; - } - code += " if (RESTART) {\n"; - - if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) - code += " float tex_linear_velocity = textureLod(linear_velocity_texture,vec2(0.0,0.0),0.0).r;\n"; - else - code += " float tex_linear_velocity = 0.0;\n"; - - if (tex_parameters[PARAM_ANGLE].is_valid()) - code += " float tex_angle = textureLod(angle_texture,vec2(0.0,0.0),0.0).r;\n"; - else - code += " float tex_angle = 0.0;\n"; - - if (tex_parameters[PARAM_ANIM_OFFSET].is_valid()) - code += " float tex_anim_offset = textureLod(anim_offset_texture,vec2(0.0,0.0),0.0).r;\n"; - else - code += " float tex_anim_offset = 0.0;\n"; - - code += " float spread_rad = spread*degree_to_rad;\n"; - - if (flags[FLAG_DISABLE_Z]) { - - code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed)*spread_rad;\n"; - code += " vec3 rot = vec3( cos(angle1_rad), sin(angle1_rad),0.0 );\n"; - code += " VELOCITY = rot*initial_linear_velocity*mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n"; - - } else { - //initiate velocity spread in 3D - code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed)*spread_rad;\n"; - code += " float angle2_rad = rand_from_seed_m1_p1(alt_seed)*spread_rad*(1.0-flatness);\n"; - code += " vec3 direction_xz = vec3( sin(angle1_rad), 0, cos(angle1_rad));\n"; - code += " vec3 direction_yz = vec3( 0, sin(angle2_rad), cos(angle2_rad));\n"; - code += " direction_yz.z = direction_yz.z / sqrt(direction_yz.z); //better uniform distribution\n"; - code += " vec3 direction = vec3(direction_xz.x * direction_yz.z, direction_yz.y, direction_xz.z * direction_yz.z);\n"; - code += " direction = normalize(direction);\n"; - code += " VELOCITY = direction*initial_linear_velocity*mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n"; - } - - code += " float base_angle = (initial_angle+tex_angle)*mix(1.0,angle_rand,initial_angle_random);\n"; - code += " CUSTOM.x = base_angle*degree_to_rad;\n"; //angle - code += " CUSTOM.y = 0.0;\n"; //phase - code += " CUSTOM.z = (anim_offset+tex_anim_offset)*mix(1.0,anim_offset_rand,anim_offset_random);\n"; //animation offset (0-1) - switch (emission_shape) { - case EMISSION_SHAPE_POINT: { - //do none - } break; - case EMISSION_SHAPE_SPHERE: { - code += " TRANSFORM[3].xyz = normalize(vec3(rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0-1.0, rand_from_seed(alt_seed) * 2.0-1.0 ))*emission_sphere_radius;\n"; - } break; - case EMISSION_SHAPE_BOX: { - code += " TRANSFORM[3].xyz = vec3(rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0-1.0, rand_from_seed(alt_seed) * 2.0-1.0)*emission_box_extents;\n"; - } break; - case EMISSION_SHAPE_POINTS: - case EMISSION_SHAPE_DIRECTED_POINTS: { - code += " TRANSFORM[3].xyz = texelFetch(emission_texture_points, emission_tex_ofs,0).xyz;\n"; - - if (emission_shape == EMISSION_SHAPE_DIRECTED_POINTS) { - if (flags[FLAG_DISABLE_Z]) { - - code += " mat2 rotm;"; - code += " rotm[0] = texelFetch(emission_texture_normal, emission_tex_ofs,0).xy;\n"; - code += " rotm[1] = rotm[0].yx * vec2(1.0,-1.0);\n"; - code += " VELOCITY.xy = rotm * VELOCITY.xy;\n"; - } else { - code += " vec3 normal = texelFetch(emission_texture_normal, emission_tex_ofs,0).xyz;\n"; - code += " vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0, 1.0, 0.0);\n"; - code += " vec3 tangent = normalize(cross(v0, normal));\n"; - code += " vec3 bitangent = normalize(cross(tangent, normal));\n"; - code += " VELOCITY = mat3(tangent,bitangent,normal) * VELOCITY;\n"; - } - } - } break; - } - code += " VELOCITY = (EMISSION_TRANSFORM * vec4(VELOCITY,0.0)).xyz;\n"; - code += " TRANSFORM = EMISSION_TRANSFORM * TRANSFORM;\n"; - if (flags[FLAG_DISABLE_Z]) { - code += " VELOCITY.z = 0.0;\n"; - code += " TRANSFORM[3].z = 0.0;\n"; - } - - code += " } else {\n"; - - code += " CUSTOM.y += DELTA/LIFETIME;\n"; - if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) - code += " float tex_linear_velocity = textureLod(linear_velocity_texture,vec2(CUSTOM.y,0.0),0.0).r;\n"; - else - code += " float tex_linear_velocity = 0.0;\n"; - - if (flags[FLAG_DISABLE_Z]) { - - if (tex_parameters[PARAM_ORBIT_VELOCITY].is_valid()) - code += " float tex_orbit_velocity = textureLod(orbit_velocity_texture,vec2(CUSTOM.y,0.0),0.0).r;\n"; - else - code += " float tex_orbit_velocity = 0.0;\n"; - } - - if (tex_parameters[PARAM_ANGULAR_VELOCITY].is_valid()) - code += " float tex_angular_velocity = textureLod(angular_velocity_texture,vec2(CUSTOM.y,0.0),0.0).r;\n"; - else - code += " float tex_angular_velocity = 0.0;\n"; - - if (tex_parameters[PARAM_LINEAR_ACCEL].is_valid()) - code += " float tex_linear_accel = textureLod(linear_accel_texture,vec2(CUSTOM.y,0.0),0.0).r;\n"; - else - code += " float tex_linear_accel = 0.0;\n"; - - if (tex_parameters[PARAM_RADIAL_ACCEL].is_valid()) - code += " float tex_radial_accel = textureLod(radial_accel_texture,vec2(CUSTOM.y,0.0),0.0).r;\n"; - else - code += " float tex_radial_accel = 0.0;\n"; - - if (tex_parameters[PARAM_TANGENTIAL_ACCEL].is_valid()) - code += " float tex_tangent_accel = textureLod(tangent_accel_texture,vec2(CUSTOM.y,0.0),0.0).r;\n"; - else - code += " float tex_tangent_accel = 0.0;\n"; - - if (tex_parameters[PARAM_DAMPING].is_valid()) - code += " float tex_damping = textureLod(damping_texture,vec2(CUSTOM.y,0.0),0.0).r;\n"; - else - code += " float tex_damping = 0.0;\n"; - - if (tex_parameters[PARAM_ANGLE].is_valid()) - code += " float tex_angle = textureLod(angle_texture,vec2(CUSTOM.y,0.0),0.0).r;\n"; - else - code += " float tex_angle = 0.0;\n"; - - if (tex_parameters[PARAM_ANIM_SPEED].is_valid()) - code += " float tex_anim_speed = textureLod(anim_speed_texture,vec2(CUSTOM.y,0.0),0.0).r;\n"; - else - code += " float tex_anim_speed = 0.0;\n"; - - if (tex_parameters[PARAM_ANIM_OFFSET].is_valid()) - code += " float tex_anim_offset = textureLod(anim_offset_texture,vec2(CUSTOM.y,0.0),0.0).r;\n"; - else - code += " float tex_anim_offset = 0.0;\n"; - - code += " vec3 force = gravity; \n"; - code += " vec3 pos = TRANSFORM[3].xyz; \n"; - if (flags[FLAG_DISABLE_Z]) { - code += " pos.z = 0.0; \n"; - } - code += " //apply linear acceleration\n"; - code += " force += length(VELOCITY) > 0.0 ? normalize(VELOCITY) * (linear_accel+tex_linear_accel)*mix(1.0,rand_from_seed(alt_seed),linear_accel_random) : vec3(0.0);\n"; - code += " //apply radial acceleration\n"; - code += " vec3 org = EMISSION_TRANSFORM[3].xyz;\n"; - code += " vec3 diff = pos-org;\n"; - code += " force += length(diff) > 0.0 ? normalize(diff) * (radial_accel+tex_radial_accel)*mix(1.0,rand_from_seed(alt_seed),radial_accel_random) : vec3(0.0);\n"; - code += " //apply tangential acceleration;\n"; - if (flags[FLAG_DISABLE_Z]) { - code += " force += length(diff.yx) > 0.0 ? vec3(normalize(diff.yx * vec2(-1.0,1.0)),0.0) * ((tangent_accel+tex_tangent_accel)*mix(1.0,rand_from_seed(alt_seed),tangent_accel_random)) : vec3(0.0);\n"; - - } else { - code += " vec3 crossDiff = cross(normalize(diff),normalize(gravity));\n"; - code += " force += length(crossDiff) > 0.0 ? normalize(crossDiff) * ((tangent_accel+tex_tangent_accel)*mix(1.0,rand_from_seed(alt_seed),tangent_accel_random)) : vec3(0.0);\n"; - } - code += " //apply attractor forces\n"; - code += " VELOCITY += force * DELTA;\n"; - code += " //orbit velocity\n"; - if (flags[FLAG_DISABLE_Z]) { - - code += " float orbit_amount = (orbit_velocity+tex_orbit_velocity)*mix(1.0,rand_from_seed(alt_seed),orbit_velocity_random);\n"; - code += " if (orbit_amount!=0.0) {\n"; - code += " float ang = orbit_amount * DELTA * pi * 2.0;\n"; - code += " mat2 rot = mat2(vec2(cos(ang),-sin(ang)),vec2(sin(ang),cos(ang)));\n"; - code += " TRANSFORM[3].xy-=diff.xy;\n"; - code += " TRANSFORM[3].xy+=rot * diff.xy;\n"; - code += " }\n"; - } - - if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { - code += " VELOCITY = normalize(VELOCITY)*tex_linear_velocity;\n"; - } - code += " if (damping + tex_damping > 0.0) {\n"; - code += " \n"; - code += " float v = length(VELOCITY);\n"; - code += " float damp = (damping+tex_damping)*mix(1.0,rand_from_seed(alt_seed),damping_random);\n"; - code += " v -= damp * DELTA;\n"; - code += " if (v < 0.0) {\n"; - code += " VELOCITY = vec3(0.0);\n"; - code += " } else {\n"; - code += " VELOCITY = normalize(VELOCITY) * v;\n"; - code += " }\n"; - code += " }\n"; - code += " float base_angle = (initial_angle+tex_angle)*mix(1.0,angle_rand,initial_angle_random);\n"; - code += " base_angle += CUSTOM.y*LIFETIME*(angular_velocity+tex_angular_velocity)*mix(1.0,rand_from_seed(alt_seed)*2.0-1.0,angular_velocity_random);\n"; - code += " CUSTOM.x = base_angle*degree_to_rad;\n"; //angle - code += " CUSTOM.z = (anim_offset+tex_anim_offset)*mix(1.0,anim_offset_rand,anim_offset_random)+CUSTOM.y*(anim_speed+tex_anim_speed)*mix(1.0,rand_from_seed(alt_seed),anim_speed_random);\n"; //angle - if (flags[FLAG_ANIM_LOOP]) { - code += " CUSTOM.z = mod(CUSTOM.z,1.0);\n"; //loop - - } else { - code += " CUSTOM.z = clamp(CUSTOM.z,0.0,1.0);\n"; //0 to 1 only - } - code += " }\n"; - //apply color - //apply hue rotation - if (tex_parameters[PARAM_SCALE].is_valid()) - code += " float tex_scale = textureLod(scale_texture,vec2(CUSTOM.y,0.0),0.0).r;\n"; - else - code += " float tex_scale = 1.0;\n"; - - if (tex_parameters[PARAM_HUE_VARIATION].is_valid()) - code += " float tex_hue_variation = textureLod(hue_variation_texture,vec2(CUSTOM.y,0.0),0.0).r;\n"; - else - code += " float tex_hue_variation = 0.0;\n"; - - code += " float hue_rot_angle = (hue_variation+tex_hue_variation)*pi*2.0*mix(1.0,hue_rot_rand*2.0-1.0,hue_variation_random);\n"; - code += " float hue_rot_c = cos(hue_rot_angle);\n"; - code += " float hue_rot_s = sin(hue_rot_angle);\n"; - code += " mat4 hue_rot_mat = mat4( vec4(0.299, 0.587, 0.114, 0.0),\n"; - code += " vec4(0.299, 0.587, 0.114, 0.0),\n"; - code += " vec4(0.299, 0.587, 0.114, 0.0),\n"; - code += " vec4(0.000, 0.000, 0.000, 1.0)) +\n"; - code += " mat4( vec4(0.701, -0.587, -0.114, 0.0),\n"; - code += " vec4(-0.299, 0.413, -0.114, 0.0),\n"; - code += " vec4(-0.300, -0.588, 0.886, 0.0),\n"; - code += " vec4(0.000, 0.000, 0.000, 0.0)) * hue_rot_c +\n"; - code += " mat4( vec4(0.168, 0.330, -0.497, 0.0),\n"; - code += " vec4(-0.328, 0.035, 0.292, 0.0),\n"; - code += " vec4(1.250, -1.050, -0.203, 0.0),\n"; - code += " vec4(0.000, 0.000, 0.000, 0.0)) * hue_rot_s;\n"; - if (color_ramp.is_valid()) { - code += " COLOR = hue_rot_mat * textureLod(color_ramp,vec2(CUSTOM.y,0.0),0.0);\n"; - } else { - code += " COLOR = hue_rot_mat * color_value;\n"; - } - if (emission_color_texture.is_valid() && emission_shape >= EMISSION_SHAPE_POINTS) { - code += " COLOR*= texelFetch(emission_texture_color,emission_tex_ofs,0);\n"; - } - if (trail_color_modifier.is_valid()) { - code += " if (trail_divisor > 1) { COLOR *= textureLod(trail_color_modifier,vec2(float(int(NUMBER)%trail_divisor)/float(trail_divisor-1),0.0),0.0); }\n"; - } - code += "\n"; - - if (flags[FLAG_DISABLE_Z]) { - - if (flags[FLAG_ALIGN_Y_TO_VELOCITY]) { - code += " if (length(VELOCITY) > 0.0) { TRANSFORM[1].xyz = normalize(VELOCITY); } else { TRANSFORM[1].xyz = normalize(TRANSFORM[1].xyz); }\n"; - code += " TRANSFORM[0].xyz = normalize(cross(TRANSFORM[1].xyz,TRANSFORM[2].xyz));\n"; - code += " TRANSFORM[2] = vec4(0.0,0.0,1.0,0.0);\n"; - } else { - code += " TRANSFORM[0] = vec4(cos(CUSTOM.x),-sin(CUSTOM.x),0.0,0.0);\n"; - code += " TRANSFORM[1] = vec4(sin(CUSTOM.x),cos(CUSTOM.x),0.0,0.0);\n"; - code += " TRANSFORM[2] = vec4(0.0,0.0,1.0,0.0);\n"; - } - - } else { - //orient particle Y towards velocity - if (flags[FLAG_ALIGN_Y_TO_VELOCITY]) { - code += " if (length(VELOCITY) > 0.0) { TRANSFORM[1].xyz = normalize(VELOCITY); } else { TRANSFORM[1].xyz = normalize(TRANSFORM[1].xyz); }\n"; - code += " if (TRANSFORM[1].xyz == normalize(TRANSFORM[0].xyz)) {\n"; - code += " TRANSFORM[0].xyz = normalize(cross(normalize(TRANSFORM[1].xyz),normalize(TRANSFORM[2].xyz)));\n"; - code += " TRANSFORM[2].xyz = normalize(cross(normalize(TRANSFORM[0].xyz),normalize(TRANSFORM[1].xyz)));\n"; - code += " } else {\n"; - code += " TRANSFORM[2].xyz = normalize(cross(normalize(TRANSFORM[0].xyz),normalize(TRANSFORM[1].xyz)));\n"; - code += " TRANSFORM[0].xyz = normalize(cross(normalize(TRANSFORM[1].xyz),normalize(TRANSFORM[2].xyz)));\n"; - code += " }\n"; - } else { - code += " TRANSFORM[0].xyz = normalize(TRANSFORM[0].xyz);\n"; - code += " TRANSFORM[1].xyz = normalize(TRANSFORM[1].xyz);\n"; - code += " TRANSFORM[2].xyz = normalize(TRANSFORM[2].xyz);\n"; - } - //turn particle by rotation in Y - if (flags[FLAG_ROTATE_Y]) { - code += " TRANSFORM = TRANSFORM * mat4( vec4(cos(CUSTOM.x),0.0,-sin(CUSTOM.x),0.0), vec4(0.0,1.0,0.0,0.0),vec4(sin(CUSTOM.x),0.0,cos(CUSTOM.x),0.0),vec4(0.0,0.0,0.0,1.0));\n"; - } - } - //scale by scale - code += " float base_scale = mix(scale*tex_scale,1.0,scale_random*scale_rand);\n"; - code += " if (base_scale==0.0) base_scale=0.000001;\n"; - if (trail_size_modifier.is_valid()) { - code += " if (trail_divisor > 1) { base_scale *= textureLod(trail_size_modifier,vec2(float(int(NUMBER)%trail_divisor)/float(trail_divisor-1),0.0),0.0).r; } \n"; - } - - code += " TRANSFORM[0].xyz *= base_scale;\n"; - code += " TRANSFORM[1].xyz *= base_scale;\n"; - code += " TRANSFORM[2].xyz *= base_scale;\n"; - if (flags[FLAG_DISABLE_Z]) { - code += " VELOCITY.z = 0.0;\n"; - code += " TRANSFORM[3].z = 0.0;\n"; - } - code += "}\n"; - code += "\n"; - - ShaderData shader_data; - shader_data.shader = VS::get_singleton()->shader_create(); - shader_data.users = 1; - - VS::get_singleton()->shader_set_code(shader_data.shader, code); - - shader_map[mk] = shader_data; - - VS::get_singleton()->material_set_shader(_get_material(), shader_data.shader); -} - -void ParticlesMaterial::flush_changes() { - - if (material_mutex) - material_mutex->lock(); - - while (dirty_materials.first()) { - - dirty_materials.first()->self()->_update_shader(); - } - - if (material_mutex) - material_mutex->unlock(); -} - -void ParticlesMaterial::_queue_shader_change() { - - if (material_mutex) - material_mutex->lock(); - - if (!element.in_list()) { - dirty_materials.add(&element); - } - - if (material_mutex) - material_mutex->unlock(); -} - -bool ParticlesMaterial::_is_shader_dirty() const { - - bool dirty = false; - - if (material_mutex) - material_mutex->lock(); - - dirty = element.in_list(); - - if (material_mutex) - material_mutex->unlock(); - - return dirty; -} - -void ParticlesMaterial::set_spread(float p_spread) { - - spread = p_spread; - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->spread, p_spread); -} - -float ParticlesMaterial::get_spread() const { - - return spread; -} - -void ParticlesMaterial::set_flatness(float p_flatness) { - - flatness = p_flatness; - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->flatness, p_flatness); -} -float ParticlesMaterial::get_flatness() const { - - return flatness; -} - -void ParticlesMaterial::set_param(Parameter p_param, float p_value) { - - ERR_FAIL_INDEX(p_param, PARAM_MAX); - - parameters[p_param] = p_value; - - switch (p_param) { - case PARAM_INITIAL_LINEAR_VELOCITY: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->initial_linear_velocity, p_value); - } break; - case PARAM_ANGULAR_VELOCITY: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->angular_velocity, p_value); - } break; - case PARAM_ORBIT_VELOCITY: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->orbit_velocity, p_value); - } break; - case PARAM_LINEAR_ACCEL: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->linear_accel, p_value); - } break; - case PARAM_RADIAL_ACCEL: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->radial_accel, p_value); - } break; - case PARAM_TANGENTIAL_ACCEL: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->tangent_accel, p_value); - } break; - case PARAM_DAMPING: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->damping, p_value); - } break; - case PARAM_ANGLE: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->initial_angle, p_value); - } break; - case PARAM_SCALE: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->scale, p_value); - } break; - case PARAM_HUE_VARIATION: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->hue_variation, p_value); - } break; - case PARAM_ANIM_SPEED: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_speed, p_value); - } break; - case PARAM_ANIM_OFFSET: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset, p_value); - } break; - } -} -float ParticlesMaterial::get_param(Parameter p_param) const { - - ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0); - - return parameters[p_param]; -} - -void ParticlesMaterial::set_param_randomness(Parameter p_param, float p_value) { - - ERR_FAIL_INDEX(p_param, PARAM_MAX); - - randomness[p_param] = p_value; - - switch (p_param) { - case PARAM_INITIAL_LINEAR_VELOCITY: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->initial_linear_velocity_random, p_value); - } break; - case PARAM_ANGULAR_VELOCITY: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->angular_velocity_random, p_value); - } break; - case PARAM_ORBIT_VELOCITY: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->orbit_velocity_random, p_value); - } break; - case PARAM_LINEAR_ACCEL: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->linear_accel_random, p_value); - } break; - case PARAM_RADIAL_ACCEL: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->radial_accel_random, p_value); - } break; - case PARAM_TANGENTIAL_ACCEL: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->tangent_accel_random, p_value); - } break; - case PARAM_DAMPING: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->damping_random, p_value); - } break; - case PARAM_ANGLE: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->initial_angle_random, p_value); - } break; - case PARAM_SCALE: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->scale_random, p_value); - } break; - case PARAM_HUE_VARIATION: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->hue_variation_random, p_value); - } break; - case PARAM_ANIM_SPEED: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_speed_random, p_value); - } break; - case PARAM_ANIM_OFFSET: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset_random, p_value); - } break; - } -} -float ParticlesMaterial::get_param_randomness(Parameter p_param) const { - - ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0); - - return randomness[p_param]; -} - -static void _adjust_curve_range(const Ref<Texture> &p_texture, float p_min, float p_max) { - - Ref<CurveTexture> curve_tex = p_texture; - if (!curve_tex.is_valid()) - return; - - curve_tex->ensure_default_setup(p_min, p_max); -} - -void ParticlesMaterial::set_param_texture(Parameter p_param, const Ref<Texture> &p_texture) { - - ERR_FAIL_INDEX(p_param, PARAM_MAX); - - tex_parameters[p_param] = p_texture; - - switch (p_param) { - case PARAM_INITIAL_LINEAR_VELOCITY: { - //do none for this one - } break; - case PARAM_ANGULAR_VELOCITY: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->angular_velocity_texture, p_texture); - _adjust_curve_range(p_texture, -360, 360); - } break; - case PARAM_ORBIT_VELOCITY: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->orbit_velocity_texture, p_texture); - _adjust_curve_range(p_texture, -500, 500); - } break; - case PARAM_LINEAR_ACCEL: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->linear_accel_texture, p_texture); - _adjust_curve_range(p_texture, -200, 200); - } break; - case PARAM_RADIAL_ACCEL: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->radial_accel_texture, p_texture); - _adjust_curve_range(p_texture, -200, 200); - } break; - case PARAM_TANGENTIAL_ACCEL: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->tangent_accel_texture, p_texture); - _adjust_curve_range(p_texture, -200, 200); - } break; - case PARAM_DAMPING: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->damping_texture, p_texture); - _adjust_curve_range(p_texture, 0, 100); - } break; - case PARAM_ANGLE: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->angle_texture, p_texture); - _adjust_curve_range(p_texture, -360, 360); - } break; - case PARAM_SCALE: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->scale_texture, p_texture); - - Ref<CurveTexture> curve_tex = p_texture; - if (curve_tex.is_valid()) { - curve_tex->ensure_default_setup(); - } - - } break; - case PARAM_HUE_VARIATION: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->hue_variation_texture, p_texture); - _adjust_curve_range(p_texture, -1, 1); - } break; - case PARAM_ANIM_SPEED: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_speed_texture, p_texture); - _adjust_curve_range(p_texture, 0, 200); - } break; - case PARAM_ANIM_OFFSET: { - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset_texture, p_texture); - } break; - } - - _queue_shader_change(); -} -Ref<Texture> ParticlesMaterial::get_param_texture(Parameter p_param) const { - - ERR_FAIL_INDEX_V(p_param, PARAM_MAX, Ref<Texture>()); - - return tex_parameters[p_param]; -} - -void ParticlesMaterial::set_color(const Color &p_color) { - - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->color, p_color); - color = p_color; -} - -Color ParticlesMaterial::get_color() const { - - return color; -} - -void ParticlesMaterial::set_color_ramp(const Ref<Texture> &p_texture) { - - color_ramp = p_texture; - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->color_ramp, p_texture); - _queue_shader_change(); - _change_notify(); -} - -Ref<Texture> ParticlesMaterial::get_color_ramp() const { - - return color_ramp; -} - -void ParticlesMaterial::set_flag(Flags p_flag, bool p_enable) { - ERR_FAIL_INDEX(p_flag, FLAG_MAX); - flags[p_flag] = p_enable; - _queue_shader_change(); - if (p_flag == FLAG_DISABLE_Z) { - _change_notify(); - } -} - -bool ParticlesMaterial::get_flag(Flags p_flag) const { - ERR_FAIL_INDEX_V(p_flag, FLAG_MAX, false); - return flags[p_flag]; -} - -void ParticlesMaterial::set_emission_shape(EmissionShape p_shape) { - - emission_shape = p_shape; - _change_notify(); - _queue_shader_change(); -} - -void ParticlesMaterial::set_emission_sphere_radius(float p_radius) { - - emission_sphere_radius = p_radius; - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_sphere_radius, p_radius); -} - -void ParticlesMaterial::set_emission_box_extents(Vector3 p_extents) { - - emission_box_extents = p_extents; - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_box_extents, p_extents); -} - -void ParticlesMaterial::set_emission_point_texture(const Ref<Texture> &p_points) { - - emission_point_texture = p_points; - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_points, p_points); -} - -void ParticlesMaterial::set_emission_normal_texture(const Ref<Texture> &p_normals) { - - emission_normal_texture = p_normals; - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_normal, p_normals); -} - -void ParticlesMaterial::set_emission_color_texture(const Ref<Texture> &p_colors) { - - emission_color_texture = p_colors; - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_color, p_colors); - _queue_shader_change(); -} - -void ParticlesMaterial::set_emission_point_count(int p_count) { - - emission_point_count = p_count; - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_point_count, p_count); -} - -ParticlesMaterial::EmissionShape ParticlesMaterial::get_emission_shape() const { - - return emission_shape; -} - -float ParticlesMaterial::get_emission_sphere_radius() const { - - return emission_sphere_radius; -} -Vector3 ParticlesMaterial::get_emission_box_extents() const { - - return emission_box_extents; -} -Ref<Texture> ParticlesMaterial::get_emission_point_texture() const { - - return emission_point_texture; -} -Ref<Texture> ParticlesMaterial::get_emission_normal_texture() const { - - return emission_normal_texture; -} - -Ref<Texture> ParticlesMaterial::get_emission_color_texture() const { - - return emission_color_texture; -} - -int ParticlesMaterial::get_emission_point_count() const { - - return emission_point_count; -} - -void ParticlesMaterial::set_trail_divisor(int p_divisor) { - - trail_divisor = p_divisor; - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->trail_divisor, p_divisor); -} - -int ParticlesMaterial::get_trail_divisor() const { - - return trail_divisor; -} - -void ParticlesMaterial::set_trail_size_modifier(const Ref<CurveTexture> &p_trail_size_modifier) { - - trail_size_modifier = p_trail_size_modifier; - - Ref<CurveTexture> curve = trail_size_modifier; - if (curve.is_valid()) { - curve->ensure_default_setup(); - } - - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->trail_size_modifier, curve); - _queue_shader_change(); -} - -Ref<CurveTexture> ParticlesMaterial::get_trail_size_modifier() const { - - return trail_size_modifier; -} - -void ParticlesMaterial::set_trail_color_modifier(const Ref<GradientTexture> &p_trail_color_modifier) { - - trail_color_modifier = p_trail_color_modifier; - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->trail_color_modifier, p_trail_color_modifier); - _queue_shader_change(); -} - -Ref<GradientTexture> ParticlesMaterial::get_trail_color_modifier() const { - - return trail_color_modifier; -} - -void ParticlesMaterial::set_gravity(const Vector3 &p_gravity) { - - gravity = p_gravity; - Vector3 gset = gravity; - if (gset == Vector3()) { - gset = Vector3(0, -0.000001, 0); //as gravity is used as upvector in some calculations - } - VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->gravity, gset); -} - -Vector3 ParticlesMaterial::get_gravity() const { - - return gravity; -} - -RID ParticlesMaterial::get_shader_rid() const { - - ERR_FAIL_COND_V(!shader_map.has(current_key), RID()); - return shader_map[current_key].shader; -} - -void ParticlesMaterial::_validate_property(PropertyInfo &property) const { - - if (property.name == "color" && color_ramp.is_valid()) { - property.usage = 0; - } - - if (property.name == "emission_sphere_radius" && emission_shape != EMISSION_SHAPE_SPHERE) { - property.usage = 0; - } - - if (property.name == "emission_box_extents" && emission_shape != EMISSION_SHAPE_BOX) { - property.usage = 0; - } - - if ((property.name == "emission_point_texture" || property.name == "emission_color_texture") && (emission_shape < EMISSION_SHAPE_POINTS)) { - property.usage = 0; - } - - if (property.name == "emission_normal_texture" && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) { - property.usage = 0; - } - - if (property.name == "emission_point_count" && (emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS)) { - property.usage = 0; - } - - if (property.name.begins_with("orbit_") && !flags[FLAG_DISABLE_Z]) { - property.usage = 0; - } -} - -Shader::Mode ParticlesMaterial::get_shader_mode() const { - - return Shader::MODE_PARTICLES; -} - -void ParticlesMaterial::_bind_methods() { - - ClassDB::bind_method(D_METHOD("set_spread", "degrees"), &ParticlesMaterial::set_spread); - ClassDB::bind_method(D_METHOD("get_spread"), &ParticlesMaterial::get_spread); - - ClassDB::bind_method(D_METHOD("set_flatness", "amount"), &ParticlesMaterial::set_flatness); - ClassDB::bind_method(D_METHOD("get_flatness"), &ParticlesMaterial::get_flatness); - - ClassDB::bind_method(D_METHOD("set_param", "param", "value"), &ParticlesMaterial::set_param); - ClassDB::bind_method(D_METHOD("get_param", "param"), &ParticlesMaterial::get_param); - - ClassDB::bind_method(D_METHOD("set_param_randomness", "param", "randomness"), &ParticlesMaterial::set_param_randomness); - ClassDB::bind_method(D_METHOD("get_param_randomness", "param"), &ParticlesMaterial::get_param_randomness); - - ClassDB::bind_method(D_METHOD("set_param_texture", "param", "texture"), &ParticlesMaterial::set_param_texture); - ClassDB::bind_method(D_METHOD("get_param_texture", "param"), &ParticlesMaterial::get_param_texture); - - ClassDB::bind_method(D_METHOD("set_color", "color"), &ParticlesMaterial::set_color); - ClassDB::bind_method(D_METHOD("get_color"), &ParticlesMaterial::get_color); - - ClassDB::bind_method(D_METHOD("set_color_ramp", "ramp"), &ParticlesMaterial::set_color_ramp); - ClassDB::bind_method(D_METHOD("get_color_ramp"), &ParticlesMaterial::get_color_ramp); - - ClassDB::bind_method(D_METHOD("set_flag", "flag", "enable"), &ParticlesMaterial::set_flag); - ClassDB::bind_method(D_METHOD("get_flag", "flag"), &ParticlesMaterial::get_flag); - - ClassDB::bind_method(D_METHOD("set_emission_shape", "shape"), &ParticlesMaterial::set_emission_shape); - ClassDB::bind_method(D_METHOD("get_emission_shape"), &ParticlesMaterial::get_emission_shape); - - ClassDB::bind_method(D_METHOD("set_emission_sphere_radius", "radius"), &ParticlesMaterial::set_emission_sphere_radius); - ClassDB::bind_method(D_METHOD("get_emission_sphere_radius"), &ParticlesMaterial::get_emission_sphere_radius); - - ClassDB::bind_method(D_METHOD("set_emission_box_extents", "extents"), &ParticlesMaterial::set_emission_box_extents); - ClassDB::bind_method(D_METHOD("get_emission_box_extents"), &ParticlesMaterial::get_emission_box_extents); - - ClassDB::bind_method(D_METHOD("set_emission_point_texture", "texture"), &ParticlesMaterial::set_emission_point_texture); - ClassDB::bind_method(D_METHOD("get_emission_point_texture"), &ParticlesMaterial::get_emission_point_texture); - - ClassDB::bind_method(D_METHOD("set_emission_normal_texture", "texture"), &ParticlesMaterial::set_emission_normal_texture); - ClassDB::bind_method(D_METHOD("get_emission_normal_texture"), &ParticlesMaterial::get_emission_normal_texture); - - ClassDB::bind_method(D_METHOD("set_emission_color_texture", "texture"), &ParticlesMaterial::set_emission_color_texture); - ClassDB::bind_method(D_METHOD("get_emission_color_texture"), &ParticlesMaterial::get_emission_color_texture); - - ClassDB::bind_method(D_METHOD("set_emission_point_count", "point_count"), &ParticlesMaterial::set_emission_point_count); - ClassDB::bind_method(D_METHOD("get_emission_point_count"), &ParticlesMaterial::get_emission_point_count); - - ClassDB::bind_method(D_METHOD("set_trail_divisor", "divisor"), &ParticlesMaterial::set_trail_divisor); - ClassDB::bind_method(D_METHOD("get_trail_divisor"), &ParticlesMaterial::get_trail_divisor); - - ClassDB::bind_method(D_METHOD("set_trail_size_modifier", "texture"), &ParticlesMaterial::set_trail_size_modifier); - ClassDB::bind_method(D_METHOD("get_trail_size_modifier"), &ParticlesMaterial::get_trail_size_modifier); - - ClassDB::bind_method(D_METHOD("set_trail_color_modifier", "texture"), &ParticlesMaterial::set_trail_color_modifier); - ClassDB::bind_method(D_METHOD("get_trail_color_modifier"), &ParticlesMaterial::get_trail_color_modifier); - - ClassDB::bind_method(D_METHOD("get_gravity"), &ParticlesMaterial::get_gravity); - ClassDB::bind_method(D_METHOD("set_gravity", "accel_vec"), &ParticlesMaterial::set_gravity); - - ADD_GROUP("Trail", "trail_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "trail_divisor", PROPERTY_HINT_RANGE, "1,1000000,1"), "set_trail_divisor", "get_trail_divisor"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "trail_size_modifier", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_trail_size_modifier", "get_trail_size_modifier"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "trail_color_modifier", PROPERTY_HINT_RESOURCE_TYPE, "GradientTexture"), "set_trail_color_modifier", "get_trail_color_modifier"); - ADD_GROUP("Emission Shape", "emission_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Box,Points,Directed Points"), "set_emission_shape", "get_emission_shape"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "emission_sphere_radius", PROPERTY_HINT_RANGE, "0.01,128,0.01,or_greater"), "set_emission_sphere_radius", "get_emission_sphere_radius"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "emission_box_extents"), "set_emission_box_extents", "get_emission_box_extents"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_point_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_emission_point_texture", "get_emission_point_texture"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_normal_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_emission_normal_texture", "get_emission_normal_texture"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_color_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_emission_color_texture", "get_emission_color_texture"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_point_count", PROPERTY_HINT_RANGE, "0,1000000,1"), "set_emission_point_count", "get_emission_point_count"); - ADD_GROUP("Flags", "flag_"); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_align_y"), "set_flag", "get_flag", FLAG_ALIGN_Y_TO_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_rotate_y"), "set_flag", "get_flag", FLAG_ROTATE_Y); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_disable_z"), "set_flag", "get_flag", FLAG_DISABLE_Z); - ADD_GROUP("Spread", ""); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "spread", PROPERTY_HINT_RANGE, "0,180,0.01"), "set_spread", "get_spread"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "flatness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_flatness", "get_flatness"); - ADD_GROUP("Gravity", ""); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity"), "set_gravity", "get_gravity"); - ADD_GROUP("Initial Velocity", "initial_"); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "initial_velocity", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param", "get_param", PARAM_INITIAL_LINEAR_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "initial_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_INITIAL_LINEAR_VELOCITY); - ADD_GROUP("Angular Velocity", "angular_"); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity", PROPERTY_HINT_RANGE, "-360,360,0.01"), "set_param", "get_param", PARAM_ANGULAR_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGULAR_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angular_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANGULAR_VELOCITY); - ADD_GROUP("Orbit Velocity", "orbit_"); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "orbit_velocity", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_ORBIT_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "orbit_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ORBIT_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "orbit_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ORBIT_VELOCITY); - ADD_GROUP("Linear Accel", "linear_"); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_LINEAR_ACCEL); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_LINEAR_ACCEL); - ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "linear_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_LINEAR_ACCEL); - ADD_GROUP("Radial Accel", "radial_"); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "radial_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_RADIAL_ACCEL); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "radial_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_RADIAL_ACCEL); - ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "radial_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_RADIAL_ACCEL); - ADD_GROUP("Tangential Accel", "tangential_"); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "tangential_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_TANGENTIAL_ACCEL); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "tangential_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_TANGENTIAL_ACCEL); - ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "tangential_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_TANGENTIAL_ACCEL); - ADD_GROUP("Damping", ""); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "damping", PROPERTY_HINT_RANGE, "0,100,0.01,or_greater"), "set_param", "get_param", PARAM_DAMPING); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "damping_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_DAMPING); - ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "damping_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_DAMPING); - ADD_GROUP("Angle", ""); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angle", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater"), "set_param", "get_param", PARAM_ANGLE); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angle_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGLE); - ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angle_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANGLE); - ADD_GROUP("Scale", ""); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "scale", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param", "get_param", PARAM_SCALE); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "scale_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_SCALE); - ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "scale_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_SCALE); - ADD_GROUP("Color", ""); - ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "GradientTexture"), "set_color_ramp", "get_color_ramp"); - - ADD_GROUP("Hue Variation", "hue_"); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "hue_variation", PROPERTY_HINT_RANGE, "-1,1,0.1"), "set_param", "get_param", PARAM_HUE_VARIATION); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "hue_variation_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_HUE_VARIATION); - ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "hue_variation_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_HUE_VARIATION); - ADD_GROUP("Animation", "anim_"); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_speed", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater"), "set_param", "get_param", PARAM_ANIM_SPEED); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_speed_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_SPEED); - ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_speed_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANIM_SPEED); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_offset", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_ANIM_OFFSET); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_offset_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_OFFSET); - ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_offset_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANIM_OFFSET); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "anim_loop"), "set_flag", "get_flag", FLAG_ANIM_LOOP); - - BIND_ENUM_CONSTANT(PARAM_INITIAL_LINEAR_VELOCITY); - BIND_ENUM_CONSTANT(PARAM_ANGULAR_VELOCITY); - BIND_ENUM_CONSTANT(PARAM_ORBIT_VELOCITY); - BIND_ENUM_CONSTANT(PARAM_LINEAR_ACCEL); - BIND_ENUM_CONSTANT(PARAM_RADIAL_ACCEL); - BIND_ENUM_CONSTANT(PARAM_TANGENTIAL_ACCEL); - BIND_ENUM_CONSTANT(PARAM_DAMPING); - BIND_ENUM_CONSTANT(PARAM_ANGLE); - BIND_ENUM_CONSTANT(PARAM_SCALE); - BIND_ENUM_CONSTANT(PARAM_HUE_VARIATION); - BIND_ENUM_CONSTANT(PARAM_ANIM_SPEED); - BIND_ENUM_CONSTANT(PARAM_ANIM_OFFSET); - BIND_ENUM_CONSTANT(PARAM_MAX); - - BIND_ENUM_CONSTANT(FLAG_ALIGN_Y_TO_VELOCITY); - BIND_ENUM_CONSTANT(FLAG_ROTATE_Y); - BIND_ENUM_CONSTANT(FLAG_MAX); - - BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINT); - BIND_ENUM_CONSTANT(EMISSION_SHAPE_SPHERE); - BIND_ENUM_CONSTANT(EMISSION_SHAPE_BOX); - BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINTS); - BIND_ENUM_CONSTANT(EMISSION_SHAPE_DIRECTED_POINTS); -} - -ParticlesMaterial::ParticlesMaterial() : - element(this) { - - set_spread(45); - set_flatness(0); - set_param(PARAM_INITIAL_LINEAR_VELOCITY, 1); - set_param(PARAM_ORBIT_VELOCITY, 0); - set_param(PARAM_LINEAR_ACCEL, 0); - set_param(PARAM_RADIAL_ACCEL, 0); - set_param(PARAM_TANGENTIAL_ACCEL, 0); - set_param(PARAM_DAMPING, 0); - set_param(PARAM_ANGLE, 0); - set_param(PARAM_SCALE, 1); - set_param(PARAM_HUE_VARIATION, 0); - set_param(PARAM_ANIM_SPEED, 0); - set_param(PARAM_ANIM_OFFSET, 0); - set_emission_shape(EMISSION_SHAPE_POINT); - set_emission_sphere_radius(1); - set_emission_box_extents(Vector3(1, 1, 1)); - set_trail_divisor(1); - set_gravity(Vector3(0, -9.8, 0)); - emission_point_count = 1; - - for (int i = 0; i < PARAM_MAX; i++) { - set_param_randomness(Parameter(i), 0); - } - - for (int i = 0; i < FLAG_MAX; i++) { - flags[i] = false; - } - - set_color(Color(1, 1, 1, 1)); - - current_key.key = 0; - current_key.invalid_key = 1; - - _queue_shader_change(); -} - -ParticlesMaterial::~ParticlesMaterial() { - - if (material_mutex) - material_mutex->lock(); - - if (shader_map.has(current_key)) { - shader_map[current_key].users--; - if (shader_map[current_key].users == 0) { - //deallocate shader, as it's no longer in use - VS::get_singleton()->free(shader_map[current_key].shader); - shader_map.erase(current_key); - } - - VS::get_singleton()->material_set_shader(_get_material(), RID()); - } - - if (material_mutex) - material_mutex->unlock(); -} diff --git a/scene/3d/particles.h b/scene/3d/particles.h index 17e21c6cee..72241c5c89 100644 --- a/scene/3d/particles.h +++ b/scene/3d/particles.h @@ -28,12 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef VISUALINSTANCEPARTICLES_H -#define VISUALINSTANCEPARTICLES_H +#ifndef PARTICLES_H +#define PARTICLES_H -#include "rid.h" +#include "core/rid.h" #include "scene/3d/visual_instance.h" -#include "scene/main/timer.h" #include "scene/resources/material.h" /** @@ -135,269 +134,4 @@ public: VARIANT_ENUM_CAST(Particles::DrawOrder) -class ParticlesMaterial : public Material { - - GDCLASS(ParticlesMaterial, Material) - -public: - enum Parameter { - - PARAM_INITIAL_LINEAR_VELOCITY, - PARAM_ANGULAR_VELOCITY, - PARAM_ORBIT_VELOCITY, - PARAM_LINEAR_ACCEL, - PARAM_RADIAL_ACCEL, - PARAM_TANGENTIAL_ACCEL, - PARAM_DAMPING, - PARAM_ANGLE, - PARAM_SCALE, - PARAM_HUE_VARIATION, - PARAM_ANIM_SPEED, - PARAM_ANIM_OFFSET, - PARAM_MAX - }; - - enum Flags { - FLAG_ALIGN_Y_TO_VELOCITY, - FLAG_ROTATE_Y, - FLAG_DISABLE_Z, - FLAG_ANIM_LOOP, - FLAG_MAX - }; - - enum EmissionShape { - EMISSION_SHAPE_POINT, - EMISSION_SHAPE_SPHERE, - EMISSION_SHAPE_BOX, - EMISSION_SHAPE_POINTS, - EMISSION_SHAPE_DIRECTED_POINTS, - }; - -private: - union MaterialKey { - - struct { - uint32_t texture_mask : 16; - uint32_t texture_color : 1; - uint32_t flags : 4; - uint32_t emission_shape : 2; - uint32_t trail_size_texture : 1; - uint32_t trail_color_texture : 1; - uint32_t invalid_key : 1; - uint32_t has_emission_color : 1; - }; - - uint32_t key; - - bool operator<(const MaterialKey &p_key) const { - return key < p_key.key; - } - }; - - struct ShaderData { - RID shader; - int users; - }; - - static Map<MaterialKey, ShaderData> shader_map; - - MaterialKey current_key; - - _FORCE_INLINE_ MaterialKey _compute_key() const { - - MaterialKey mk; - mk.key = 0; - for (int i = 0; i < PARAM_MAX; i++) { - if (tex_parameters[i].is_valid()) { - mk.texture_mask |= (1 << i); - } - } - for (int i = 0; i < FLAG_MAX; i++) { - if (flags[i]) { - mk.flags |= (1 << i); - } - } - - mk.texture_color = color_ramp.is_valid() ? 1 : 0; - mk.emission_shape = emission_shape; - mk.trail_color_texture = trail_color_modifier.is_valid() ? 1 : 0; - mk.trail_size_texture = trail_size_modifier.is_valid() ? 1 : 0; - mk.has_emission_color = emission_shape >= EMISSION_SHAPE_POINTS && emission_color_texture.is_valid(); - - return mk; - } - - static Mutex *material_mutex; - static SelfList<ParticlesMaterial>::List dirty_materials; - - struct ShaderNames { - StringName spread; - StringName flatness; - StringName initial_linear_velocity; - StringName initial_angle; - StringName angular_velocity; - StringName orbit_velocity; - StringName linear_accel; - StringName radial_accel; - StringName tangent_accel; - StringName damping; - StringName scale; - StringName hue_variation; - StringName anim_speed; - StringName anim_offset; - - StringName initial_linear_velocity_random; - StringName initial_angle_random; - StringName angular_velocity_random; - StringName orbit_velocity_random; - StringName linear_accel_random; - StringName radial_accel_random; - StringName tangent_accel_random; - StringName damping_random; - StringName scale_random; - StringName hue_variation_random; - StringName anim_speed_random; - StringName anim_offset_random; - - StringName angle_texture; - StringName angular_velocity_texture; - StringName orbit_velocity_texture; - StringName linear_accel_texture; - StringName radial_accel_texture; - StringName tangent_accel_texture; - StringName damping_texture; - StringName scale_texture; - StringName hue_variation_texture; - StringName anim_speed_texture; - StringName anim_offset_texture; - - StringName color; - StringName color_ramp; - - StringName emission_sphere_radius; - StringName emission_box_extents; - StringName emission_texture_point_count; - StringName emission_texture_points; - StringName emission_texture_normal; - StringName emission_texture_color; - - StringName trail_divisor; - StringName trail_size_modifier; - StringName trail_color_modifier; - - StringName gravity; - }; - - static ShaderNames *shader_names; - - SelfList<ParticlesMaterial> element; - - void _update_shader(); - _FORCE_INLINE_ void _queue_shader_change(); - _FORCE_INLINE_ bool _is_shader_dirty() const; - - float spread; - float flatness; - - float parameters[PARAM_MAX]; - float randomness[PARAM_MAX]; - - Ref<Texture> tex_parameters[PARAM_MAX]; - Color color; - Ref<Texture> color_ramp; - - bool flags[FLAG_MAX]; - - EmissionShape emission_shape; - float emission_sphere_radius; - Vector3 emission_box_extents; - Ref<Texture> emission_point_texture; - Ref<Texture> emission_normal_texture; - Ref<Texture> emission_color_texture; - int emission_point_count; - - bool anim_loop; - - int trail_divisor; - - Ref<CurveTexture> trail_size_modifier; - Ref<GradientTexture> trail_color_modifier; - - Vector3 gravity; - - //do not save emission points here - -protected: - static void _bind_methods(); - virtual void _validate_property(PropertyInfo &property) const; - -public: - void set_spread(float p_spread); - float get_spread() const; - - void set_flatness(float p_flatness); - float get_flatness() const; - - void set_param(Parameter p_param, float p_value); - float get_param(Parameter p_param) const; - - void set_param_randomness(Parameter p_param, float p_value); - float get_param_randomness(Parameter p_param) const; - - void set_param_texture(Parameter p_param, const Ref<Texture> &p_texture); - Ref<Texture> get_param_texture(Parameter p_param) const; - - void set_color(const Color &p_color); - Color get_color() const; - - void set_color_ramp(const Ref<Texture> &p_texture); - Ref<Texture> get_color_ramp() const; - - void set_flag(Flags p_flag, bool p_enable); - bool get_flag(Flags p_flag) const; - - void set_emission_shape(EmissionShape p_shape); - void set_emission_sphere_radius(float p_radius); - void set_emission_box_extents(Vector3 p_extents); - void set_emission_point_texture(const Ref<Texture> &p_points); - void set_emission_normal_texture(const Ref<Texture> &p_normals); - void set_emission_color_texture(const Ref<Texture> &p_colors); - void set_emission_point_count(int p_count); - - EmissionShape get_emission_shape() const; - float get_emission_sphere_radius() const; - Vector3 get_emission_box_extents() const; - Ref<Texture> get_emission_point_texture() const; - Ref<Texture> get_emission_normal_texture() const; - Ref<Texture> get_emission_color_texture() const; - int get_emission_point_count() const; - - void set_trail_divisor(int p_divisor); - int get_trail_divisor() const; - - void set_trail_size_modifier(const Ref<CurveTexture> &p_trail_size_modifier); - Ref<CurveTexture> get_trail_size_modifier() const; - - void set_trail_color_modifier(const Ref<GradientTexture> &p_trail_color_modifier); - Ref<GradientTexture> get_trail_color_modifier() const; - - void set_gravity(const Vector3 &p_gravity); - Vector3 get_gravity() const; - - static void init_shaders(); - static void finish_shaders(); - static void flush_changes(); - - RID get_shader_rid() const; - - virtual Shader::Mode get_shader_mode() const; - - ParticlesMaterial(); - ~ParticlesMaterial(); -}; - -VARIANT_ENUM_CAST(ParticlesMaterial::Parameter) -VARIANT_ENUM_CAST(ParticlesMaterial::Flags) -VARIANT_ENUM_CAST(ParticlesMaterial::EmissionShape) - -#endif +#endif // PARTICLES_H diff --git a/scene/3d/path.cpp b/scene/3d/path.cpp index 9acaa15641..339a434a6e 100644 --- a/scene/3d/path.cpp +++ b/scene/3d/path.cpp @@ -30,7 +30,7 @@ #include "path.h" -#include "engine.h" +#include "core/engine.h" #include "scene/scene_string_names.h" void Path::_notification(int p_what) { @@ -43,6 +43,16 @@ void Path::_curve_changed() { if (is_inside_tree()) { emit_signal("curve_changed"); } + + // update the configuration warnings of all children of type OrientedPathFollows + if (is_inside_tree()) { + for (int i = 0; i < get_child_count(); i++) { + OrientedPathFollow *child = Object::cast_to<OrientedPathFollow>(get_child(i)); + if (child) { + child->update_configuration_warning(); + } + } + } } void Path::set_curve(const Ref<Curve3D> &p_curve) { @@ -207,6 +217,18 @@ void PathFollow::_validate_property(PropertyInfo &property) const { } } +String PathFollow::get_configuration_warning() const { + + if (!is_visible_in_tree() || !is_inside_tree()) + return String(); + + if (!Object::cast_to<Path>(get_parent())) { + return TTR("PathFollow only works when set as a child of a Path node."); + } + + return String(); +} + void PathFollow::_bind_methods() { ClassDB::bind_method(D_METHOD("set_offset", "offset"), &PathFollow::set_offset); @@ -444,6 +466,23 @@ void OrientedPathFollow::_validate_property(PropertyInfo &property) const { } } +String OrientedPathFollow::get_configuration_warning() const { + + if (!is_visible_in_tree() || !is_inside_tree()) + return String(); + + if (!Object::cast_to<Path>(get_parent())) { + return TTR("OrientedPathFollow only works when set as a child of a Path node."); + } else { + Path *path = Object::cast_to<Path>(get_parent()); + if (path->get_curve().is_valid() && !path->get_curve()->is_up_vector_enabled()) { + return TTR("OrientedPathFollow requires up vectors enabled in its parent Path."); + } + } + + return String(); +} + void OrientedPathFollow::_bind_methods() { ClassDB::bind_method(D_METHOD("set_offset", "offset"), &OrientedPathFollow::set_offset); diff --git a/scene/3d/path.h b/scene/3d/path.h index f73bf17dfe..beb37d9714 100644 --- a/scene/3d/path.h +++ b/scene/3d/path.h @@ -106,6 +106,8 @@ public: void set_cubic_interpolation(bool p_enable); bool get_cubic_interpolation() const; + String get_configuration_warning() const; + PathFollow(); }; @@ -151,6 +153,8 @@ public: void set_cubic_interpolation(bool p_enable); bool get_cubic_interpolation() const; + String get_configuration_warning() const; + OrientedPathFollow(); }; diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp index 2df6ef7c8a..970e8cf1ee 100644 --- a/scene/3d/physics_body.cpp +++ b/scene/3d/physics_body.cpp @@ -31,8 +31,11 @@ #include "physics_body.h" #include "core/core_string_names.h" -#include "engine.h" -#include "method_bind_ext.gen.inc" +#include "core/engine.h" +#include "core/list.h" +#include "core/method_bind_ext.gen.inc" +#include "core/object.h" +#include "core/rid.h" #include "scene/scene_string_names.h" #ifdef TOOLS_ENABLED @@ -40,17 +43,6 @@ #endif void PhysicsBody::_notification(int p_what) { - - /* - switch(p_what) { - - case NOTIFICATION_TRANSFORM_CHANGED: { - - PhysicsServer::get_singleton()->body_set_state(get_rid(),PhysicsServer::BODY_STATE_TRANSFORM,get_global_transform()); - - } break; - } - */ } Vector3 PhysicsBody::get_linear_velocity() const { @@ -119,6 +111,20 @@ bool PhysicsBody::get_collision_layer_bit(int p_bit) const { return get_collision_layer() & (1 << p_bit); } +Array PhysicsBody::get_collision_exceptions() { + List<RID> exceptions; + PhysicsServer::get_singleton()->body_get_collision_exceptions(get_rid(), &exceptions); + Array ret; + for (List<RID>::Element *E = exceptions.front(); E; E = E->next()) { + RID body = E->get(); + ObjectID instance_id = PhysicsServer::get_singleton()->body_get_object_instance_id(body); + Object *obj = ObjectDB::get_instance(instance_id); + PhysicsBody *physics_body = Object::cast_to<PhysicsBody>(obj); + ret.append(physics_body); + } + return ret; +} + void PhysicsBody::add_collision_exception_with(Node *p_node) { ERR_FAIL_NULL(p_node); @@ -182,7 +188,11 @@ PhysicsBody::PhysicsBody(PhysicsServer::BodyMode p_mode) : #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") + if (p_friction == 1.0) { // default value, don't create an override for that + return; + } + + ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physics material instead.") WARN_DEPRECATED ERR_FAIL_COND(p_friction < 0 || p_friction > 1); @@ -191,13 +201,12 @@ void StaticBody::set_friction(real_t p_friction) { physics_material_override.instance(); set_physics_material_override(physics_material_override); } - physics_material_override->set_friction(p_friction); } real_t StaticBody::get_friction() const { - ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physical material") + ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physics material instead.") WARN_DEPRECATED if (physics_material_override.is_null()) { @@ -209,7 +218,11 @@ real_t StaticBody::get_friction() const { 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") + if (p_bounce == 0.0) { // default value, don't create an override for that + return; + } + + ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physics material instead.") WARN_DEPRECATED ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1); @@ -223,7 +236,7 @@ void StaticBody::set_bounce(real_t p_bounce) { real_t StaticBody::get_bounce() const { - ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physical material") + ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physics material instead.") WARN_DEPRECATED if (physics_material_override.is_null()) { @@ -293,14 +306,15 @@ void StaticBody::_bind_methods() { ClassDB::bind_method(D_METHOD("_reload_physics_characteristics"), &StaticBody::_reload_physics_characteristics); + ClassDB::bind_method(D_METHOD("get_collision_exceptions"), &PhysicsBody::get_collision_exceptions); 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"); + ADD_PROPERTYNO(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01", 0), "set_friction", "get_friction"); + ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01", 0), "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_PROPERTYNZ(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"); } @@ -419,7 +433,7 @@ void RigidBody::_body_inout(int p_status, ObjectID p_instance, int p_body_shape, node->disconnect(SceneStringNames::get_singleton()->tree_entered, this, SceneStringNames::get_singleton()->_body_enter_tree); node->disconnect(SceneStringNames::get_singleton()->tree_exiting, this, SceneStringNames::get_singleton()->_body_exit_tree); if (in_tree) - emit_signal(SceneStringNames::get_singleton()->body_exited, obj); + emit_signal(SceneStringNames::get_singleton()->body_exited, node); } contact_monitor->body_map.erase(E); @@ -618,8 +632,13 @@ real_t RigidBody::get_weight() const { #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") + if (p_friction == 1.0) { // default value, don't create an override for that + return; + } + + ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physics material instead.") WARN_DEPRECATED + ERR_FAIL_COND(p_friction < 0 || p_friction > 1); if (physics_material_override.is_null()) { @@ -630,8 +649,9 @@ void RigidBody::set_friction(real_t p_friction) { } real_t RigidBody::get_friction() const { - ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physical material") + ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physics material instead.") WARN_DEPRECATED + if (physics_material_override.is_null()) { return 1; } @@ -640,8 +660,14 @@ real_t RigidBody::get_friction() const { } 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") + + if (p_bounce == 0.0) { // default value, don't create an override for that + return; + } + + ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physics material instead.") WARN_DEPRECATED + ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1); if (physics_material_override.is_null()) { @@ -651,7 +677,7 @@ void RigidBody::set_bounce(real_t p_bounce) { 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") + ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physics material instead.") WARN_DEPRECATED if (physics_material_override.is_null()) { return 0; @@ -998,10 +1024,10 @@ void RigidBody::_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, "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"); + ADD_PROPERTYNO(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01", 0), "set_friction", "get_friction"); + ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01", 0), "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_PROPERTYNZ(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"); @@ -1023,10 +1049,10 @@ void RigidBody::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "angular_velocity"), "set_angular_velocity", "get_angular_velocity"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_damp", PROPERTY_HINT_RANGE, "-1,128,0.01"), "set_angular_damp", "get_angular_damp"); - ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape"))); - ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape"))); - ADD_SIGNAL(MethodInfo("body_entered", PropertyInfo(Variant::OBJECT, "body"))); - ADD_SIGNAL(MethodInfo("body_exited", PropertyInfo(Variant::OBJECT, "body"))); + ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape"))); + ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape"))); + ADD_SIGNAL(MethodInfo("body_entered", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); + ADD_SIGNAL(MethodInfo("body_exited", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); ADD_SIGNAL(MethodInfo("sleeping_state_changed")); BIND_ENUM_CONSTANT(MODE_RIGID); @@ -2279,6 +2305,8 @@ void PhysicalBone::_reload_joint() { } } break; + case JOINT_TYPE_NONE: { + } break; } } @@ -2316,7 +2344,8 @@ void PhysicalBone::set_joint_type(JointType p_joint_type) { if (p_joint_type == get_joint_type()) return; - memdelete(joint_data); + if (joint_data) + memdelete(joint_data); joint_data = NULL; switch (p_joint_type) { case JOINT_TYPE_PIN: @@ -2334,6 +2363,8 @@ void PhysicalBone::set_joint_type(JointType p_joint_type) { case JOINT_TYPE_6DOF: joint_data = memnew(SixDOFJointData); break; + case JOINT_TYPE_NONE: + break; } _reload_joint(); @@ -2497,12 +2528,12 @@ PhysicalBone::PhysicalBone() : gizmo_move_joint(false), #endif joint_data(NULL), + parent_skeleton(NULL), static_body(false), - simulate_physics(false), _internal_static_body(false), + simulate_physics(false), _internal_simulate_physics(false), bone_id(-1), - parent_skeleton(NULL), bone_name(""), bounce(0), mass(1), @@ -2514,7 +2545,8 @@ PhysicalBone::PhysicalBone() : } PhysicalBone::~PhysicalBone() { - memdelete(joint_data); + if (joint_data) + memdelete(joint_data); } void PhysicalBone::update_bone_id() { diff --git a/scene/3d/physics_body.h b/scene/3d/physics_body.h index c4db41f577..fa319e6fbb 100644 --- a/scene/3d/physics_body.h +++ b/scene/3d/physics_body.h @@ -31,11 +31,11 @@ #ifndef PHYSICS_BODY__H #define PHYSICS_BODY__H +#include "core/vset.h" #include "scene/3d/collision_object.h" #include "scene/resources/physics_material.h" #include "servers/physics_server.h" #include "skeleton.h" -#include "vset.h" class PhysicsBody : public CollisionObject { @@ -69,6 +69,7 @@ public: void set_collision_mask_bit(int p_bit, bool p_value); bool get_collision_mask_bit(int p_bit) const; + Array get_collision_exceptions(); void add_collision_exception_with(Node *p_node); //must be physicsbody void remove_collision_exception_with(Node *p_node); @@ -389,6 +390,8 @@ public: virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID()); virtual bool _get(const StringName &p_name, Variant &r_ret) const; virtual void _get_property_list(List<PropertyInfo> *p_list) const; + + virtual ~JointData() {} }; struct PinJointData : public JointData { diff --git a/scene/3d/portal.cpp b/scene/3d/portal.cpp index d16d9ed7c5..137338d79e 100644 --- a/scene/3d/portal.cpp +++ b/scene/3d/portal.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "portal.h" -#include "project_settings.h" +#include "core/project_settings.h" #include "scene/resources/surface_tool.h" #include "servers/visual_server.h" diff --git a/scene/3d/proximity_group.cpp b/scene/3d/proximity_group.cpp index 101d9ed70c..f56f728d99 100644 --- a/scene/3d/proximity_group.cpp +++ b/scene/3d/proximity_group.cpp @@ -30,7 +30,7 @@ #include "proximity_group.h" -#include "math_funcs.h" +#include "core/math/math_funcs.h" void ProximityGroup::clear_groups() { diff --git a/scene/3d/ray_cast.cpp b/scene/3d/ray_cast.cpp index b846a5b6c0..17f069bbc8 100644 --- a/scene/3d/ray_cast.cpp +++ b/scene/3d/ray_cast.cpp @@ -31,7 +31,7 @@ #include "ray_cast.h" #include "collision_object.h" -#include "engine.h" +#include "core/engine.h" #include "mesh_instance.h" #include "servers/physics_server.h" diff --git a/scene/3d/room_instance.cpp b/scene/3d/room_instance.cpp index 0d83a9942e..3914d7190e 100644 --- a/scene/3d/room_instance.cpp +++ b/scene/3d/room_instance.cpp @@ -34,8 +34,8 @@ // FIXME: Will be removed, kept as reference for new implementation #if 0 -#include "geometry.h" -#include "project_settings.h" +#include "core/math/geometry.h" +#include "core/project_settings.h" #include "scene/resources/surface_tool.h" void Room::_notification(int p_what) { diff --git a/scene/3d/skeleton.cpp b/scene/3d/skeleton.cpp index c796e47f25..db82ef2a5c 100644 --- a/scene/3d/skeleton.cpp +++ b/scene/3d/skeleton.cpp @@ -30,7 +30,7 @@ #include "skeleton.h" -#include "message_queue.h" +#include "core/message_queue.h" #include "core/project_settings.h" #include "scene/3d/physics_body.h" diff --git a/scene/3d/skeleton.h b/scene/3d/skeleton.h index e044e08437..07abdc1fe7 100644 --- a/scene/3d/skeleton.h +++ b/scene/3d/skeleton.h @@ -31,7 +31,7 @@ #ifndef SKELETON_H #define SKELETON_H -#include "rid.h" +#include "core/rid.h" #include "scene/3d/spatial.h" /** diff --git a/scene/3d/soft_body.cpp b/scene/3d/soft_body.cpp index 980c348c9b..1e730d0b3d 100644 --- a/scene/3d/soft_body.cpp +++ b/scene/3d/soft_body.cpp @@ -1,13 +1,12 @@ /*************************************************************************/ -/* soft_physics_body.cpp */ -/* Author: AndreaCatania */ +/* soft_body.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ -/* http://www.godotengine.org */ +/* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -30,8 +29,12 @@ /*************************************************************************/ #include "soft_body.h" -#include "os/os.h" +#include "core/list.h" +#include "core/object.h" +#include "core/os/os.h" +#include "core/rid.h" #include "scene/3d/collision_object.h" +#include "scene/3d/physics_body.h" #include "scene/3d/skeleton.h" #include "servers/physics_server.h" @@ -336,6 +339,7 @@ void SoftBody::_bind_methods() { 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("get_collision_exceptions"), &SoftBody::get_collision_exceptions); 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); @@ -401,7 +405,7 @@ String SoftBody::get_configuration_warning() const { 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."); + warning += TTR("Size changes to SoftBody will be overridden by the physics engine when running.\nChange the size in children collision shapes instead."); } return warning; @@ -548,6 +552,20 @@ PoolVector<SoftBody::PinnedPoint> SoftBody::get_pinned_points_indices() { return pinned_points; } +Array SoftBody::get_collision_exceptions() { + List<RID> exceptions; + PhysicsServer::get_singleton()->soft_body_get_collision_exceptions(physics_rid, &exceptions); + Array ret; + for (List<RID>::Element *E = exceptions.front(); E; E = E->next()) { + RID body = E->get(); + ObjectID instance_id = PhysicsServer::get_singleton()->body_get_object_instance_id(body); + Object *obj = ObjectDB::get_instance(instance_id); + PhysicsBody *physics_body = Object::cast_to<PhysicsBody>(obj); + ret.append(physics_body); + } + return ret; +} + void SoftBody::add_collision_exception_with(Node *p_node) { ERR_FAIL_NULL(p_node); CollisionObject *collision_object = Object::cast_to<CollisionObject>(p_node); diff --git a/scene/3d/soft_body.h b/scene/3d/soft_body.h index cee32b9651..b1e699e839 100644 --- a/scene/3d/soft_body.h +++ b/scene/3d/soft_body.h @@ -1,13 +1,12 @@ /*************************************************************************/ -/* soft_physics_body.h */ -/* Author: AndreaCatania */ +/* soft_body.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ -/* http://www.godotengine.org */ +/* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -167,6 +166,7 @@ public: void set_drag_coefficient(real_t p_drag_coefficient); real_t get_drag_coefficient(); + Array get_collision_exceptions(); void add_collision_exception_with(Node *p_node); void remove_collision_exception_with(Node *p_node); diff --git a/scene/3d/spatial.cpp b/scene/3d/spatial.cpp index 3f494264e7..fcc908cdc6 100644 --- a/scene/3d/spatial.cpp +++ b/scene/3d/spatial.cpp @@ -30,8 +30,8 @@ #include "spatial.h" -#include "engine.h" -#include "message_queue.h" +#include "core/engine.h" +#include "core/message_queue.h" #include "scene/main/viewport.h" #include "scene/scene_string_names.h" @@ -185,10 +185,8 @@ void Spatial::_notification(int p_what) { if (data.gizmo.is_valid()) { data.gizmo->create(); - if (data.gizmo->can_draw()) { - if (is_visible_in_tree()) { - data.gizmo->redraw(); - } + if (is_visible_in_tree()) { + data.gizmo->redraw(); } data.gizmo->transform(); } @@ -423,10 +421,8 @@ void Spatial::set_gizmo(const Ref<SpatialGizmo> &p_gizmo) { if (data.gizmo.is_valid() && is_inside_world()) { data.gizmo->create(); - if (data.gizmo->can_draw()) { - if (is_visible_in_tree()) { - data.gizmo->redraw(); - } + if (is_visible_in_tree()) { + data.gizmo->redraw(); } data.gizmo->transform(); } @@ -452,12 +448,10 @@ void Spatial::_update_gizmo() { return; data.gizmo_dirty = false; if (data.gizmo.is_valid()) { - if (data.gizmo->can_draw()) { - if (is_visible_in_tree()) - data.gizmo->redraw(); - else - data.gizmo->clear(); - } + if (is_visible_in_tree()) + data.gizmo->redraw(); + else + data.gizmo->clear(); } #endif } @@ -727,6 +721,16 @@ bool Spatial::is_local_transform_notification_enabled() const { return data.notify_local_transform; } +void Spatial::force_update_transform() { + ERR_FAIL_COND(!is_inside_tree()); + if (!xform_change.in_list()) { + return; //nothing to update + } + get_tree()->xform_change_list.remove(&xform_change); + + notification(NOTIFICATION_TRANSFORM_CHANGED); +} + void Spatial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_transform", "local"), &Spatial::set_transform); @@ -749,6 +753,8 @@ void Spatial::_bind_methods() { ClassDB::bind_method(D_METHOD("is_scale_disabled"), &Spatial::is_scale_disabled); ClassDB::bind_method(D_METHOD("get_world"), &Spatial::get_world); + ClassDB::bind_method(D_METHOD("force_update_transform"), &Spatial::force_update_transform); + ClassDB::bind_method(D_METHOD("_update_gizmo"), &Spatial::_update_gizmo); ClassDB::bind_method(D_METHOD("update_gizmo"), &Spatial::update_gizmo); diff --git a/scene/3d/spatial.h b/scene/3d/spatial.h index bc054a8763..815ca16bc5 100644 --- a/scene/3d/spatial.h +++ b/scene/3d/spatial.h @@ -48,7 +48,6 @@ public: virtual void clear() = 0; virtual void redraw() = 0; virtual void free() = 0; - virtual bool can_draw() const = 0; SpatialGizmo(); virtual ~SpatialGizmo() {} @@ -203,6 +202,8 @@ public: void hide(); bool is_visible_in_tree() const; + void force_update_transform(); + Spatial(); ~Spatial(); }; diff --git a/scene/3d/spatial_velocity_tracker.cpp b/scene/3d/spatial_velocity_tracker.cpp index d96b003a81..3850a0c7e6 100644 --- a/scene/3d/spatial_velocity_tracker.cpp +++ b/scene/3d/spatial_velocity_tracker.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "spatial_velocity_tracker.h" -#include "engine.h" +#include "core/engine.h" void SpatialVelocityTracker::set_track_physics_step(bool p_track_physics_step) { diff --git a/scene/3d/spring_arm.cpp b/scene/3d/spring_arm.cpp index 492c6b806e..f74784c2f9 100644 --- a/scene/3d/spring_arm.cpp +++ b/scene/3d/spring_arm.cpp @@ -29,15 +29,16 @@ /*************************************************************************/ #include "spring_arm.h" -#include "engine.h" +#include "core/engine.h" #include "scene/3d/collision_object.h" #include "scene/resources/sphere_shape.h" #include "servers/physics_server.h" SpringArm::SpringArm() : spring_length(1), - mask(1), current_spring_length(0), + keep_child_basis(false), + mask(1), margin(0.01) {} void SpringArm::_notification(int p_what) { diff --git a/scene/3d/spring_arm.h b/scene/3d/spring_arm.h index 24d912d371..e0c3f2992d 100644 --- a/scene/3d/spring_arm.h +++ b/scene/3d/spring_arm.h @@ -39,8 +39,8 @@ class SpringArm : public Spatial { Ref<Shape> shape; Set<RID> excluded_objects; float spring_length; - bool keep_child_basis; float current_spring_length; + bool keep_child_basis; uint32_t mask; float margin; diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index 036a748c83..774ee49af2 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "sprite_3d.h" -#include "core_string_names.h" +#include "core/core_string_names.h" #include "scene/scene_string_names.h" Color SpriteBase3D::_get_color_accum() { @@ -431,7 +431,7 @@ void Sprite3D::_draw() { }; - Vector2 src_tsize = Vector2(texture->get_width(), texture->get_height()); + Vector2 src_tsize = tsize; // Properly setup UVs for impostor textures (AtlasTexture). Ref<AtlasTexture> atlas_tex = texture; @@ -441,10 +441,10 @@ void Sprite3D::_draw() { } Vector2 uvs[4] = { - final_src_rect.position / tsize, - (final_src_rect.position + Vector2(final_src_rect.size.x, 0)) / tsize, - (final_src_rect.position + final_src_rect.size) / tsize, - (final_src_rect.position + Vector2(0, final_src_rect.size.y)) / tsize, + final_src_rect.position / src_tsize, + (final_src_rect.position + Vector2(final_src_rect.size.x, 0)) / src_tsize, + (final_src_rect.position + final_src_rect.size) / src_tsize, + (final_src_rect.position + Vector2(0, final_src_rect.size.y)) / src_tsize, }; if (is_flipped_h()) { @@ -731,7 +731,7 @@ void AnimatedSprite3D::_draw() { }; - Vector2 src_tsize = Vector2(texture->get_width(), texture->get_height()); + Vector2 src_tsize = tsize; // Properly setup UVs for impostor textures (AtlasTexture). Ref<AtlasTexture> atlas_tex = texture; @@ -741,10 +741,10 @@ void AnimatedSprite3D::_draw() { } Vector2 uvs[4] = { - final_src_rect.position / tsize, - (final_src_rect.position + Vector2(final_src_rect.size.x, 0)) / tsize, - (final_src_rect.position + final_src_rect.size) / tsize, - (final_src_rect.position + Vector2(0, final_src_rect.size.y)) / tsize, + final_src_rect.position / src_tsize, + (final_src_rect.position + Vector2(final_src_rect.size.x, 0)) / src_tsize, + (final_src_rect.position + final_src_rect.size) / src_tsize, + (final_src_rect.position + Vector2(0, final_src_rect.size.y)) / src_tsize, }; if (is_flipped_h()) { diff --git a/scene/3d/vehicle_body.cpp b/scene/3d/vehicle_body.cpp index f9d096633c..56a86b6a17 100644 --- a/scene/3d/vehicle_body.cpp +++ b/scene/3d/vehicle_body.cpp @@ -562,7 +562,7 @@ void VehicleBody::_resolve_single_bilateral(PhysicsDirectBodyState *s, const Vec b2invmass); // FIXME: rel_vel assignment here is overwritten by the following assignment. - // What seemes to be intended in the next next assignment is: rel_vel = normal.dot(rel_vel); + // What seems to be intended in the next next assignment is: rel_vel = normal.dot(rel_vel); // Investigate why. real_t rel_vel = jac.getRelativeVelocity( s->get_linear_velocity(), diff --git a/scene/3d/visibility_notifier.cpp b/scene/3d/visibility_notifier.cpp index 9d6e4941f3..c69387d082 100644 --- a/scene/3d/visibility_notifier.cpp +++ b/scene/3d/visibility_notifier.cpp @@ -30,7 +30,7 @@ #include "visibility_notifier.h" -#include "engine.h" +#include "core/engine.h" #include "scene/3d/camera.h" #include "scene/3d/physics_body.h" #include "scene/animation/animation_player.h" diff --git a/scene/3d/visibility_notifier.h b/scene/3d/visibility_notifier.h index b1985f4a0c..2cf685a92c 100644 --- a/scene/3d/visibility_notifier.h +++ b/scene/3d/visibility_notifier.h @@ -48,7 +48,7 @@ protected: void _notification(int p_what); static void _bind_methods(); - friend class SpatialIndexer; + friend struct SpatialIndexer; void _enter_camera(Camera *p_camera); void _exit_camera(Camera *p_camera); diff --git a/scene/3d/visual_instance.cpp b/scene/3d/visual_instance.cpp index 767518dc83..6dc821c3f5 100644 --- a/scene/3d/visual_instance.cpp +++ b/scene/3d/visual_instance.cpp @@ -294,7 +294,7 @@ void GeometryInstance::_bind_methods() { ADD_GROUP("Geometry", ""); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material_override", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,SpatialMaterial"), "set_material_override", "get_material_override"); ADD_PROPERTY(PropertyInfo(Variant::INT, "cast_shadow", PROPERTY_HINT_ENUM, "Off,On,Double-Sided,Shadows Only"), "set_cast_shadows_setting", "get_cast_shadows_setting"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "extra_cull_margin", PROPERTY_HINT_RANGE, "0,16384,0"), "set_extra_cull_margin", "get_extra_cull_margin"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "extra_cull_margin", PROPERTY_HINT_RANGE, "0,16384,0.01"), "set_extra_cull_margin", "get_extra_cull_margin"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "use_in_baked_light"), "set_flag", "get_flag", FLAG_USE_BAKED_LIGHT); ADD_GROUP("LOD", "lod_"); diff --git a/scene/3d/visual_instance.h b/scene/3d/visual_instance.h index 9249bc04ce..784f2a358a 100644 --- a/scene/3d/visual_instance.h +++ b/scene/3d/visual_instance.h @@ -31,8 +31,8 @@ #ifndef VISUAL_INSTANCE_H #define VISUAL_INSTANCE_H -#include "face3.h" -#include "rid.h" +#include "core/math/face3.h" +#include "core/rid.h" #include "scene/3d/spatial.h" #include "scene/resources/material.h" /** diff --git a/scene/3d/voxel_light_baker.cpp b/scene/3d/voxel_light_baker.cpp index e846e1763d..0eccbbc8f9 100644 --- a/scene/3d/voxel_light_baker.cpp +++ b/scene/3d/voxel_light_baker.cpp @@ -29,8 +29,8 @@ /*************************************************************************/ #include "voxel_light_baker.h" -#include "os/os.h" -#include "os/threaded_array_processor.h" +#include "core/os/os.h" +#include "core/os/threaded_array_processor.h" #include <stdlib.h> @@ -261,7 +261,7 @@ static _FORCE_INLINE_ void get_uv_and_normal(const Vector3 &p_pos, const Vector3 void VoxelLightBaker::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, const Vector3 *p_vtx, const Vector3 *p_normal, const Vector2 *p_uv, const MaterialCache &p_material, const AABB &p_aabb) { if (p_level == cell_subdiv - 1) { - //plot the face by guessing it's albedo and emission value + //plot the face by guessing its albedo and emission value //find best axis to map to, for scanning values int closest_axis = 0; @@ -734,7 +734,8 @@ void VoxelLightBaker::_check_init_light() { leaf_voxel_count = 0; _fixup_plot(0, 0); //pre fixup, so normal, albedo, emission, etc. work for lighting. bake_light.resize(bake_cells.size()); - zeromem(bake_light.ptrw(), bake_light.size() * sizeof(Light)); + print_line("bake light size: " + itos(bake_light.size())); + //zeromem(bake_light.ptrw(), bake_light.size() * sizeof(Light)); first_leaf = -1; _init_light_plot(0, 0, 0, 0, 0, CHILD_EMPTY); } @@ -1586,10 +1587,10 @@ Vector3 VoxelLightBaker::_compute_pixel_light_at_pos(const Vector3 &p_pos, const Vector3 bitangent = tangent.cross(p_normal).normalized(); Basis normal_xform = Basis(tangent, bitangent, p_normal).transposed(); - const Vector3 *cone_dirs; - const float *cone_weights; - int cone_dir_count; - float cone_aperture; + const Vector3 *cone_dirs = NULL; + const float *cone_weights = NULL; + int cone_dir_count = 0; + float cone_aperture = 0; switch (bake_quality) { case BAKE_QUALITY_LOW: { @@ -1618,7 +1619,7 @@ Vector3 VoxelLightBaker::_compute_pixel_light_at_pos(const Vector3 &p_pos, const Vector3(-0.700629, -0.509037, 0.5), Vector3(0.267617, -0.823639, 0.5) }; - static const float weights[6] = { 0.25, 0.15, 0.15, 0.15, 0.15, 0.15 }; + static const float weights[6] = { 0.25f, 0.15f, 0.15f, 0.15f, 0.15f, 0.15f }; // cone_dirs = dirs; cone_dir_count = 6; @@ -1640,7 +1641,7 @@ Vector3 VoxelLightBaker::_compute_pixel_light_at_pos(const Vector3 &p_pos, const Vector3(0.19124006749743122, 0.39355745585016605, 0.8991883926788214), Vector3(0.19124006749743122, -0.39355745585016605, 0.8991883926788214), }; - static const float weights[10] = { 0.08571, 0.08571, 0.08571, 0.08571, 0.08571, 0.08571, 0.08571, 0.133333, 0.133333, 0.13333 }; + static const float weights[10] = { 0.08571f, 0.08571f, 0.08571f, 0.08571f, 0.08571f, 0.08571f, 0.08571f, 0.133333f, 0.133333f, 0.13333f }; cone_dirs = dirs; cone_dir_count = 10; cone_aperture = 0.404; // tan(angle) 45 degrees @@ -1874,7 +1875,7 @@ Error VoxelLightBaker::make_lightmap(const Transform &p_xform, Ref<Mesh> &p_mesh if (bake_mode == BAKE_MODE_RAY_TRACE) { //blur //gauss kernel, 7 step sigma 2 - static const float gauss_kernel[4] = { 0.214607, 0.189879, 0.131514, 0.071303 }; + static const float gauss_kernel[4] = { 0.214607f, 0.189879f, 0.131514f, 0.071303f }; //horizontal pass for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { diff --git a/scene/3d/voxel_light_baker.h b/scene/3d/voxel_light_baker.h index 6a1f1253a3..3d55c053f3 100644 --- a/scene/3d/voxel_light_baker.h +++ b/scene/3d/voxel_light_baker.h @@ -92,6 +92,16 @@ private: float accum[6][3]; //rgb anisotropic float direct_accum[6][3]; //for direct bake int next_leaf; + Light() { + x = y = z = 0; + for (int i = 0; i < 6; i++) { + for (int j = 0; j < 3; j++) { + accum[i][j] = 0; + direct_accum[i][j] = 0; + } + } + next_leaf = 0; + } }; int first_leaf; diff --git a/scene/SCsub b/scene/SCsub index 5d81e818ba..d8839ce3a8 100644 --- a/scene/SCsub +++ b/scene/SCsub @@ -4,16 +4,19 @@ Import('env') env.scene_sources = [] - # Thirdparty code thirdparty_dir = "#thirdparty/misc/" thirdparty_sources = [ + # C++ sources + "easing_equations.cpp", # C sources "mikktspace.c", ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env.add_source_files(env.scene_sources, thirdparty_sources) +env_thirdparty = env.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(env.scene_sources, thirdparty_sources) # Godot's own sources env.add_source_files(env.scene_sources, "*.cpp") @@ -32,5 +35,3 @@ SConscript('resources/SCsub') # Build it all as a library lib = env.add_library("scene", env.scene_sources) env.Prepend(LIBS=[lib]) - -Export('env') diff --git a/scene/animation/SCsub b/scene/animation/SCsub index bf9125be7f..b01e2fd54d 100644 --- a/scene/animation/SCsub +++ b/scene/animation/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.scene_sources, "*.cpp") - -Export('env') diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp index 289cf7a3a7..76de39c3e6 100644 --- a/scene/animation/animation_blend_space_1d.cpp +++ b/scene/animation/animation_blend_space_1d.cpp @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* animation_blend_space_1d.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 "animation_blend_space_1d.h" void AnimationNodeBlendSpace1D::get_parameter_list(List<PropertyInfo> *r_list) const { diff --git a/scene/animation/animation_blend_space_1d.h b/scene/animation/animation_blend_space_1d.h index f4e20f0d70..a83a813744 100644 --- a/scene/animation/animation_blend_space_1d.h +++ b/scene/animation/animation_blend_space_1d.h @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* animation_blend_space_1d.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 ANIMATION_BLEND_SPACE_1D_H #define ANIMATION_BLEND_SPACE_1D_H diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp index 3dc7f2a86f..9321133d5f 100644 --- a/scene/animation/animation_blend_space_2d.cpp +++ b/scene/animation/animation_blend_space_2d.cpp @@ -1,5 +1,35 @@ +/*************************************************************************/ +/* animation_blend_space_2d.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 "animation_blend_space_2d.h" -#include "math/delaunay.h" +#include "core/math/delaunay.h" void AnimationNodeBlendSpace2D::get_parameter_list(List<PropertyInfo> *r_list) const { r_list->push_back(PropertyInfo(Variant::VECTOR2, blend_position)); @@ -438,7 +468,7 @@ float AnimationNodeBlendSpace2D::process(float p_time, bool p_seek) { } first = true; - float mind; + float mind = 0; for (int i = 0; i < blend_points_used; i++) { bool found = false; diff --git a/scene/animation/animation_blend_space_2d.h b/scene/animation/animation_blend_space_2d.h index 3e1c66c924..2c684687de 100644 --- a/scene/animation/animation_blend_space_2d.h +++ b/scene/animation/animation_blend_space_2d.h @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* animation_blend_space_2d.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 ANIMATION_BLEND_SPACE_2D_H #define ANIMATION_BLEND_SPACE_2D_H diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index 10bab3ce38..b037596fda 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* animation_blend_tree.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 "animation_blend_tree.h" #include "scene/scene_string_names.h" @@ -236,7 +266,6 @@ float AnimationNodeOneShot::process(float p_time, bool p_seek) { if (do_start) { remaining = os_rem; - do_start = false; } if (!p_seek) { @@ -740,8 +769,6 @@ float AnimationNodeTransition::process(float p_time, bool p_seek) { rem = blend_input(current, p_time, p_seek, 1.0 - blend, FILTER_IGNORE, false); } - switched = false; - if (p_seek) { // don't seek prev animation blend_input(prev, 0, false, blend, FILTER_IGNORE, false); time = p_time; diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h index 7bf2917c1e..4ca11e464b 100644 --- a/scene/animation/animation_blend_tree.h +++ b/scene/animation/animation_blend_tree.h @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* animation_blend_tree.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 ANIMATION_BLEND_TREE_H #define ANIMATION_BLEND_TREE_H diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp index 09c36eb081..a7af3f687b 100644 --- a/scene/animation/animation_node_state_machine.cpp +++ b/scene/animation/animation_node_state_machine.cpp @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* animation_node_state_machine.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 "animation_node_state_machine.h" ///////////////////////////////////////////////// @@ -290,7 +320,7 @@ float AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *sm, } if (!_travel(sm, start_request)) { - //cant travel, then teleport + //can't travel, then teleport path.clear(); current = start_request; } @@ -315,7 +345,6 @@ float AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *sm, len_current = sm->blend_node(current, sm->states[current].node, 0, true, 1.0, AnimationNode::FILTER_IGNORE, false); pos_current = 0; loops_current = 0; - play_start = false; } if (!sm->states.has(current)) { @@ -390,7 +419,7 @@ float AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *sm, auto_advance = true; } - if (sm->transitions[i].from == current && sm->transitions[i].transition->has_auto_advance()) { + if (sm->transitions[i].from == current && auto_advance) { if (sm->transitions[i].transition->get_priority() < priority_best) { auto_advance_to = i; diff --git a/scene/animation/animation_node_state_machine.h b/scene/animation/animation_node_state_machine.h index 5d633d6334..a88b3eb482 100644 --- a/scene/animation/animation_node_state_machine.h +++ b/scene/animation/animation_node_state_machine.h @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* animation_node_state_machine.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 ANIMATION_NODE_STATE_MACHINE_H #define ANIMATION_NODE_STATE_MACHINE_H diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index d8db1973d2..102f05a146 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -30,8 +30,8 @@ #include "animation_player.h" -#include "engine.h" -#include "message_queue.h" +#include "core/engine.h" +#include "core/message_queue.h" #include "scene/scene_string_names.h" #include "servers/audio/audio_stream.h" #ifdef TOOLS_ENABLED @@ -263,43 +263,36 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim) { key.id = id; key.bone_idx = bone_idx; - if (node_cache_map.has(key)) { - - p_anim->node_cache.write[i] = &node_cache_map[key]; - } else { - + if (!node_cache_map.has(key)) node_cache_map[key] = TrackNodeCache(); - 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; - p_anim->node_cache[i]->node_2d = Object::cast_to<Node2D>(child); - if (a->track_get_type(i) == Animation::TYPE_TRANSFORM) { - // special cases and caches for transform tracks - - // cache spatial - p_anim->node_cache[i]->spatial = Object::cast_to<Spatial>(child); - // cache skeleton - p_anim->node_cache[i]->skeleton = Object::cast_to<Skeleton>(child); - if (p_anim->node_cache[i]->skeleton) { - - if (a->track_get_path(i).get_subname_count() == 1) { - StringName bone_name = a->track_get_path(i).get_subname(0); - - p_anim->node_cache[i]->bone_idx = p_anim->node_cache[i]->skeleton->find_bone(bone_name); - if (p_anim->node_cache[i]->bone_idx < 0) { - // broken track (nonexistent bone) - p_anim->node_cache[i]->skeleton = NULL; - p_anim->node_cache[i]->spatial = NULL; - printf("bone is %ls\n", String(bone_name).c_str()); - ERR_CONTINUE(p_anim->node_cache[i]->bone_idx < 0); - } else { - } - } else { - // no property, just use spatialnode + 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; + p_anim->node_cache[i]->node_2d = Object::cast_to<Node2D>(child); + if (a->track_get_type(i) == Animation::TYPE_TRANSFORM) { + // special cases and caches for transform tracks + + // cache spatial + p_anim->node_cache[i]->spatial = Object::cast_to<Spatial>(child); + // cache skeleton + p_anim->node_cache[i]->skeleton = Object::cast_to<Skeleton>(child); + if (p_anim->node_cache[i]->skeleton) { + if (a->track_get_path(i).get_subname_count() == 1) { + StringName bone_name = a->track_get_path(i).get_subname(0); + + p_anim->node_cache[i]->bone_idx = p_anim->node_cache[i]->skeleton->find_bone(bone_name); + if (p_anim->node_cache[i]->bone_idx < 0) { + // broken track (nonexistent bone) p_anim->node_cache[i]->skeleton = NULL; + p_anim->node_cache[i]->spatial = NULL; + printf("bone is %ls\n", String(bone_name).c_str()); + ERR_CONTINUE(p_anim->node_cache[i]->bone_idx < 0); } + } else { + // no property, just use spatialnode + p_anim->node_cache[i]->skeleton = NULL; } } } @@ -426,7 +419,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, float if (first_key_time == 0.0) { //ignore, use for transition if (key_count == 1) - continue; //with one key we cant do anything + continue; //with one key we can't do anything transition = a->track_get_key_transition(i, 0); first_key_time = a->track_get_key_time(i, 1); first_key = 1; @@ -1458,6 +1451,7 @@ void AnimationPlayer::_set_process(bool p_process, bool p_force) { case ANIMATION_PROCESS_PHYSICS: set_physics_process_internal(p_process && active); break; case ANIMATION_PROCESS_IDLE: set_process_internal(p_process && active); break; + case ANIMATION_PROCESS_MANUAL: break; } processing = p_process; diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index 73bd00e456..eb00f91bb3 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -1,7 +1,38 @@ +/*************************************************************************/ +/* animation_tree.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 "animation_tree.h" + #include "animation_blend_tree.h" +#include "core/engine.h" #include "core/method_bind_ext.gen.inc" -#include "engine.h" #include "scene/scene_string_names.h" #include "servers/audio/audio_stream.h" @@ -161,7 +192,7 @@ float AnimationNode::_blend_node(const StringName &p_subpath, const Vector<Strin case FILTER_IGNORE: break; //will not happen anyway case FILTER_PASS: { - //values filtered pass, the rest dont + //values filtered pass, the rest don't for (int i = 0; i < blend_count; i++) { if (blendw[i] == 0) //not filtered, does not pass continue; @@ -175,7 +206,7 @@ float AnimationNode::_blend_node(const StringName &p_subpath, const Vector<Strin } break; case FILTER_STOP: { - //values filtered dont pass, the rest are blended + //values filtered don't pass, the rest are blended for (int i = 0; i < blend_count; i++) { if (blendw[i] > 0) //filtered, does not pass @@ -261,7 +292,7 @@ String AnimationNode::get_caption() const { } void AnimationNode::add_input(const String &p_name) { - //root nodes cant add inputs + //root nodes can't add inputs ERR_FAIL_COND(Object::cast_to<AnimationRootNode>(this) != NULL) Input input; ERR_FAIL_COND(p_name.find(".") != -1 || p_name.find("/") != -1); @@ -1196,7 +1227,7 @@ void AnimationTree::_process_graph(float p_delta) { t->object->set_indexed(t->subpath, t->value); } break; - default: {} //the rest dont matter + default: {} //the rest don't matter } } } diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h index 6cb363d50a..cda7f8ae74 100644 --- a/scene/animation/animation_tree.h +++ b/scene/animation/animation_tree.h @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* animation_tree.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 ANIMATION_GRAPH_PLAYER_H #define ANIMATION_GRAPH_PLAYER_H diff --git a/scene/animation/animation_tree_player.cpp b/scene/animation/animation_tree_player.cpp index 179f5d9698..524784df53 100644 --- a/scene/animation/animation_tree_player.cpp +++ b/scene/animation/animation_tree_player.cpp @@ -877,6 +877,7 @@ void AnimationTreePlayer::_process_animation(float p_delta) { tr.track->object->call(method, args[0], args[1], args[2], args[3], args[4]); } } break; + default: {} } } } diff --git a/scene/animation/root_motion_view.cpp b/scene/animation/root_motion_view.cpp index a28c63064a..01a10dbc2b 100644 --- a/scene/animation/root_motion_view.cpp +++ b/scene/animation/root_motion_view.cpp @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* root_motion_view.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 "root_motion_view.h" #include "scene/animation/animation_tree.h" #include "scene/resources/material.h" @@ -82,7 +112,7 @@ void RootMotionView::_notification(int p_what) { first = false; - transform.orthonormalize(); //dont want scale, too imprecise + transform.orthonormalize(); //don't want scale, too imprecise transform.affine_invert(); accumulated = transform * accumulated; diff --git a/scene/animation/root_motion_view.h b/scene/animation/root_motion_view.h index 611183d364..5198ab21aa 100644 --- a/scene/animation/root_motion_view.h +++ b/scene/animation/root_motion_view.h @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* root_motion_view.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 ROOT_MOTION_VIEW_H #define ROOT_MOTION_VIEW_H diff --git a/scene/animation/skeleton_ik.cpp b/scene/animation/skeleton_ik.cpp index 9b1cb1369a..83f45afac8 100644 --- a/scene/animation/skeleton_ik.cpp +++ b/scene/animation/skeleton_ik.cpp @@ -54,9 +54,9 @@ FabrikInverseKinematic::ChainItem *FabrikInverseKinematic::ChainItem::add_child( } /// Build a chain that starts from the root to tip -void FabrikInverseKinematic::build_chain(Task *p_task, bool p_force_simple_chain) { +bool FabrikInverseKinematic::build_chain(Task *p_task, bool p_force_simple_chain) { - ERR_FAIL_COND(-1 == p_task->root_bone); + ERR_FAIL_COND_V(-1 == p_task->root_bone, false); Chain &chain(p_task->chain); @@ -77,8 +77,8 @@ void FabrikInverseKinematic::build_chain(Task *p_task, bool p_force_simple_chain 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()); + ERR_FAIL_COND_V(p_task->root_bone >= ee->tip_bone, false); + ERR_FAIL_INDEX_V(ee->tip_bone, p_task->skeleton->get_bone_count(), false); sub_chain_size = 0; // Picks all IDs that composing a single chain in reverse order (except the root) @@ -133,6 +133,7 @@ void FabrikInverseKinematic::build_chain(Task *p_task, bool p_force_simple_chain break; } } + return true; } void FabrikInverseKinematic::update_chain(const Skeleton *p_sk, ChainItem *p_chain_item) { @@ -247,7 +248,10 @@ FabrikInverseKinematic::Task *FabrikInverseKinematic::create_simple_task(Skeleto task->end_effectors.push_back(ee); task->goal_global_transform = goal_transform; - build_chain(task); + if (!build_chain(task)) { + free_task(task); + return NULL; + } return task; } @@ -414,11 +418,11 @@ void SkeletonIK::_notification(int p_what) { SkeletonIK::SkeletonIK() : Node(), interpolation(1), - skeleton(NULL), - target_node_override(NULL), use_magnet(false), min_distance(0.01), max_iterations(10), + skeleton(NULL), + target_node_override(NULL), task(NULL) { set_process_priority(1); @@ -535,8 +539,10 @@ void SkeletonIK::reload_chain() { 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; + if (task) { + task->max_iterations = max_iterations; + task->min_distance = min_distance; + } } void SkeletonIK::reload_goal() { diff --git a/scene/animation/skeleton_ik.h b/scene/animation/skeleton_ik.h index 08fb00e798..202d6959bb 100644 --- a/scene/animation/skeleton_ik.h +++ b/scene/animation/skeleton_ik.h @@ -123,7 +123,7 @@ public: 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 bool build_chain(Task *p_task, bool p_force_simple_chain = true); static void update_chain(const Skeleton *p_sk, ChainItem *p_chain_item); diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index 58be636e44..3521782417 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -29,7 +29,8 @@ /*************************************************************************/ #include "tween.h" -#include "method_bind_ext.gen.inc" + +#include "core/method_bind_ext.gen.inc" void Tween::_add_pending_command(StringName p_key, const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4, const Variant &p_arg5, const Variant &p_arg6, const Variant &p_arg7, const Variant &p_arg8, const Variant &p_arg9, const Variant &p_arg10) { @@ -276,7 +277,10 @@ Variant &Tween::_get_initial_val(InterpolateData &p_data) { ERR_FAIL_COND_V(error.error != Variant::CallError::CALL_OK, p_data.initial_val); } return initial_val; - } break; + } + + case INTER_CALLBACK: + break; } return p_data.delta_val; } @@ -312,7 +316,7 @@ Variant &Tween::_get_delta_val(InterpolateData &p_data) { if (final_val.get_type() == Variant::INT) final_val = final_val.operator real_t(); _calc_delta_val(p_data.initial_val, final_val, p_data.delta_val); return p_data.delta_val; - } break; + } case TARGETING_PROPERTY: case TARGETING_METHOD: { @@ -324,7 +328,10 @@ Variant &Tween::_get_delta_val(InterpolateData &p_data) { //_calc_delta_val(p_data.initial_val, p_data.final_val, p_data.delta_val); _calc_delta_val(initial_val, p_data.final_val, p_data.delta_val); return p_data.delta_val; - } break; + } + + case INTER_CALLBACK: + break; } return p_data.initial_val; } @@ -673,6 +680,10 @@ float Tween::get_speed_scale() const { } bool Tween::start() { + if (pending_update != 0) { + call_deferred("start"); + return true; + } set_active(true); return true; @@ -852,12 +863,8 @@ bool Tween::seek(real_t p_time) { data.finish = false; } - switch (data.type) { - case INTER_PROPERTY: - case INTER_METHOD: - break; - case INTER_CALLBACK: - continue; + if (data.type == INTER_CALLBACK) { + continue; } Variant result = _run_equation(data); diff --git a/scene/audio/SCsub b/scene/audio/SCsub index bf9125be7f..b01e2fd54d 100644 --- a/scene/audio/SCsub +++ b/scene/audio/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.scene_sources, "*.cpp") - -Export('env') diff --git a/scene/audio/audio_player.cpp b/scene/audio/audio_player.cpp index 7a9f2bd8d0..64af7efb16 100644 --- a/scene/audio/audio_player.cpp +++ b/scene/audio/audio_player.cpp @@ -30,7 +30,7 @@ #include "audio_player.h" -#include "engine.h" +#include "core/engine.h" void AudioStreamPlayer::_mix_internal(bool p_fadeout) { diff --git a/scene/gui/SCsub b/scene/gui/SCsub index bf9125be7f..b01e2fd54d 100644 --- a/scene/gui/SCsub +++ b/scene/gui/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.scene_sources, "*.cpp") - -Export('env') diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index d17ae1d84c..6dfaacc776 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -30,7 +30,7 @@ #include "base_button.h" -#include "os/keyboard.h" +#include "core/os/keyboard.h" #include "scene/main/viewport.h" #include "scene/scene_string_names.h" @@ -315,6 +315,14 @@ void BaseButton::set_disabled(bool p_disabled) { return; status.disabled = p_disabled; + if (p_disabled) { + if (!toggle_mode) { + status.pressed = false; + } + status.press_attempt = false; + status.pressing_inside = false; + status.pressing_button = 0; + } update(); _change_notify("disabled"); } @@ -360,7 +368,9 @@ BaseButton::DrawMode BaseButton::get_draw_mode() const { return DRAW_DISABLED; }; - if (status.press_attempt == false && status.hovering && !status.pressed) { + if (!status.press_attempt && status.hovering) { + if (status.pressed) + return DRAW_HOVER_PRESSED; return DRAW_HOVER; } else { @@ -536,6 +546,7 @@ void BaseButton::_bind_methods() { BIND_ENUM_CONSTANT(DRAW_PRESSED); BIND_ENUM_CONSTANT(DRAW_HOVER); BIND_ENUM_CONSTANT(DRAW_DISABLED); + BIND_ENUM_CONSTANT(DRAW_HOVER_PRESSED); BIND_ENUM_CONSTANT(ACTION_MODE_BUTTON_PRESS); BIND_ENUM_CONSTANT(ACTION_MODE_BUTTON_RELEASE); diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h index 79638bbcce..176d9fc213 100644 --- a/scene/gui/base_button.h +++ b/scene/gui/base_button.h @@ -85,6 +85,7 @@ public: DRAW_PRESSED, DRAW_HOVER, DRAW_DISABLED, + DRAW_HOVER_PRESSED, }; DrawMode get_draw_mode() const; diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index a34f2f1ad5..2d17fb1391 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -29,8 +29,8 @@ /*************************************************************************/ #include "button.h" +#include "core/translation.h" #include "servers/visual_server.h" -#include "translation.h" Size2 Button::get_minimum_size() const { @@ -88,6 +88,21 @@ void Button::_notification(int p_what) { if (has_color("icon_color_normal")) color_icon = get_color("icon_color_normal"); } break; + case DRAW_HOVER_PRESSED: { + if (has_stylebox("hover_pressed") && has_stylebox_override("hover_pressed")) { + style = get_stylebox("hover_pressed"); + if (!flat) + style->draw(ci, Rect2(Point2(0, 0), size)); + if (has_color("font_color_hover_pressed")) + color = get_color("font_color_hover_pressed"); + else + color = get_color("font_color"); + if (has_color("icon_color_hover_pressed")) + color_icon = get_color("icon_color_hover_pressed"); + + break; + } + } case DRAW_PRESSED: { style = get_stylebox("pressed"); diff --git a/scene/gui/check_button.cpp b/scene/gui/check_button.cpp index f9ed0ecdbb..fa9538da28 100644 --- a/scene/gui/check_button.cpp +++ b/scene/gui/check_button.cpp @@ -30,7 +30,7 @@ #include "check_button.h" -#include "print_string.h" +#include "core/print_string.h" #include "servers/visual_server.h" Size2 CheckButton::get_icon_size() const { diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 8e232c6f46..c5d3def4c1 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -30,9 +30,14 @@ #include "color_picker.h" -#include "os/input.h" -#include "os/keyboard.h" -#include "os/os.h" +#include "core/os/input.h" +#include "core/os/keyboard.h" +#include "core/os/os.h" + +#ifdef TOOLS_ENABLED +#include "editor_settings.h" +#endif + #include "scene/gui/separator.h" #include "scene/main/viewport.h" @@ -52,6 +57,16 @@ void ColorPicker::_notification(int p_what) { bt_add_preset->set_icon(get_icon("add_preset")); _update_color(); + +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + PoolColorArray saved_presets = EditorSettings::get_singleton()->get_project_metadata("color_picker", "presets", PoolColorArray()); + + for (int i = 0; i < saved_presets.size(); i++) { + add_preset(saved_presets[i]); + } + } +#endif } break; case NOTIFICATION_PARENTED: { @@ -144,7 +159,10 @@ void ColorPicker::_html_entered(const String &p_html) { if (updating) return; + float last_alpha = color.a; color = Color::html(p_html); + if (!is_editing_alpha()) + color.a = last_alpha; if (!is_inside_tree()) return; @@ -186,9 +204,22 @@ void ColorPicker::_update_presets() { preset->draw_texture_rect(get_icon("preset_bg", "ColorPicker"), Rect2(Point2(), preset_size), true); +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + PoolColorArray arr_to_save = PoolColorArray(); + + for (int i = 0; i < presets.size(); i++) { + preset->draw_rect(Rect2(Point2(size.width * i, 0), size), presets[i]); + arr_to_save.insert(i, presets[i]); + } + + EditorSettings::get_singleton()->set_project_metadata("color_picker", "presets", arr_to_save); + } +#else for (int i = 0; i < presets.size(); i++) { preset->draw_rect(Rect2(Point2(size.width * i, 0), size), presets[i]); } +#endif } void ColorPicker::_text_type_toggled() { diff --git a/scene/gui/container.cpp b/scene/gui/container.cpp index 7df03bf7c6..d606629041 100644 --- a/scene/gui/container.cpp +++ b/scene/gui/container.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "container.h" -#include "message_queue.h" +#include "core/message_queue.h" #include "scene/scene_string_names.h" void Container::_child_minsize_changed() { diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index e094a063be..e155d0ef86 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -29,15 +29,15 @@ /*************************************************************************/ #include "control.h" -#include "project_settings.h" +#include "core/project_settings.h" #include "scene/main/canvas_layer.h" #include "scene/main/viewport.h" #include "servers/visual_server.h" -#include "message_queue.h" -#include "os/keyboard.h" -#include "os/os.h" -#include "print_string.h" +#include "core/message_queue.h" +#include "core/os/keyboard.h" +#include "core/os/os.h" +#include "core/print_string.h" #include "scene/gui/label.h" #include "scene/gui/panel.h" #include "scene/scene_string_names.h" @@ -769,7 +769,7 @@ void Control::force_drag(const Variant &p_data, Control *p_control) { void Control::set_drag_preview(Control *p_control) { ERR_FAIL_COND(!is_inside_tree()); - ERR_FAIL_COND(get_viewport()->gui_is_dragging()); + ERR_FAIL_COND(!get_viewport()->gui_is_dragging()); get_viewport()->_gui_set_drag_preview(this, p_control); } @@ -1079,7 +1079,7 @@ bool Control::has_constant_override(const StringName &p_name) const { bool Control::has_icon(const StringName &p_name, const StringName &p_type) const { if (p_type == StringName() || p_type == "") { - if (has_icon_override(p_name) == true) + if (has_icon_override(p_name)) return true; } @@ -1113,7 +1113,7 @@ bool Control::has_icon(const StringName &p_name, const StringName &p_type) const bool Control::has_shader(const StringName &p_name, const StringName &p_type) const { if (p_type == StringName() || p_type == "") { - if (has_shader_override(p_name) == true) + if (has_shader_override(p_name)) return true; } @@ -1146,7 +1146,7 @@ bool Control::has_shader(const StringName &p_name, const StringName &p_type) con bool Control::has_stylebox(const StringName &p_name, const StringName &p_type) const { if (p_type == StringName() || p_type == "") { - if (has_stylebox_override(p_name) == true) + if (has_stylebox_override(p_name)) return true; } @@ -1179,7 +1179,7 @@ bool Control::has_stylebox(const StringName &p_name, const StringName &p_type) c bool Control::has_font(const StringName &p_name, const StringName &p_type) const { if (p_type == StringName() || p_type == "") { - if (has_font_override(p_name) == true) + if (has_font_override(p_name)) return true; } @@ -1213,7 +1213,7 @@ bool Control::has_font(const StringName &p_name, const StringName &p_type) const bool Control::has_color(const StringName &p_name, const StringName &p_type) const { if (p_type == StringName() || p_type == "") { - if (has_color_override(p_name) == true) + if (has_color_override(p_name)) return true; } @@ -1247,7 +1247,7 @@ bool Control::has_color(const StringName &p_name, const StringName &p_type) cons bool Control::has_constant(const StringName &p_name, const StringName &p_type) const { if (p_type == StringName() || p_type == "") { - if (has_constant_override(p_name) == true) + if (has_constant_override(p_name)) return true; } @@ -1993,7 +1993,7 @@ Control *Control::find_prev_valid_focus() const { if (!from) { - ERR_EXPLAIN("Prev focus node is not a control: " + n->get_name()); + ERR_EXPLAIN("Previous focus node is not a control: " + n->get_name()); ERR_FAIL_V(NULL); } } else { @@ -2062,8 +2062,11 @@ void Control::grab_focus() { if (!is_inside_tree()) { ERR_FAIL_COND(!is_inside_tree()); } - if (data.focus_mode == FOCUS_NONE) + + if (data.focus_mode == FOCUS_NONE) { + WARN_PRINT("This control can't grab focus. Use set_focus_mode() to allow a control to get focus."); return; + } get_viewport()->_gui_control_grab_focus(this); } @@ -2232,13 +2235,13 @@ String Control::_get_tooltip() const { void Control::set_focus_neighbour(Margin p_margin, const NodePath &p_neighbour) { - ERR_FAIL_INDEX(p_margin, 4); + ERR_FAIL_INDEX((int)p_margin, 4); data.focus_neighbour[p_margin] = p_neighbour; } NodePath Control::get_focus_neighbour(Margin p_margin) const { - ERR_FAIL_INDEX_V(p_margin, 4, NodePath()); + ERR_FAIL_INDEX_V((int)p_margin, 4, NodePath()); return data.focus_neighbour[p_margin]; } @@ -2874,7 +2877,7 @@ void Control::_bind_methods() { ADD_GROUP("Mouse", "mouse_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "mouse_filter", PROPERTY_HINT_ENUM, "Stop,Pass,Ignore"), "set_mouse_filter", "get_mouse_filter"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "mouse_default_cursor_shape", PROPERTY_HINT_ENUM, "Arrow,Ibeam,Pointing hand,Cross,Wait,Busy,Drag,Can drop,Forbidden,Vertical resize,Horizontal resize,Secondary diagonal resize,Main diagonal resize,Move,Vertial split,Horizontal split,Help"), "set_default_cursor_shape", "get_default_cursor_shape"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "mouse_default_cursor_shape", PROPERTY_HINT_ENUM, "Arrow,Ibeam,Pointing hand,Cross,Wait,Busy,Drag,Can drop,Forbidden,Vertical resize,Horizontal resize,Secondary diagonal resize,Main diagonal resize,Move,Vertical split,Horizontal split,Help"), "set_default_cursor_shape", "get_default_cursor_shape"); ADD_GROUP("Size Flags", "size_flags_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_horizontal", PROPERTY_HINT_FLAGS, "Fill,Expand,Shrink Center,Shrink End"), "set_h_size_flags", "get_h_size_flags"); @@ -2956,7 +2959,7 @@ void Control::_bind_methods() { BIND_ENUM_CONSTANT(ANCHOR_END); ADD_SIGNAL(MethodInfo("resized")); - ADD_SIGNAL(MethodInfo("gui_input", PropertyInfo(Variant::OBJECT, "ev", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"))); + ADD_SIGNAL(MethodInfo("gui_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"))); ADD_SIGNAL(MethodInfo("mouse_entered")); ADD_SIGNAL(MethodInfo("mouse_exited")); ADD_SIGNAL(MethodInfo("focus_entered")); diff --git a/scene/gui/control.h b/scene/gui/control.h index c6bd2f097d..eb39d9ca0f 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -31,13 +31,13 @@ #ifndef CONTROL_H #define CONTROL_H -#include "rid.h" +#include "core/math/transform_2d.h" +#include "core/rid.h" #include "scene/2d/canvas_item.h" #include "scene/gui/shortcut.h" #include "scene/main/node.h" #include "scene/main/timer.h" #include "scene/resources/theme.h" -#include "transform_2d.h" /** @author Juan Linietsky <reduzio@gmail.com> */ @@ -76,7 +76,7 @@ public: SIZE_EXPAND = 2, SIZE_EXPAND_FILL = SIZE_EXPAND | SIZE_FILL, SIZE_SHRINK_CENTER = 4, //ignored by expand or fill - SIZE_SHRINK_END = 8, //ignored by expand or fil + SIZE_SHRINK_END = 8, //ignored by expand or fill }; diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp index d9737fa21a..e3a21eb10d 100644 --- a/scene/gui/dialogs.cpp +++ b/scene/gui/dialogs.cpp @@ -29,9 +29,9 @@ /*************************************************************************/ #include "dialogs.h" +#include "core/print_string.h" +#include "core/translation.h" #include "line_edit.h" -#include "print_string.h" -#include "translation.h" #ifdef TOOLS_ENABLED #include "editor/editor_node.h" diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index 635f812805..1e9f4df4a3 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -29,8 +29,8 @@ /*************************************************************************/ #include "file_dialog.h" -#include "os/keyboard.h" -#include "print_string.h" +#include "core/os/keyboard.h" +#include "core/print_string.h" #include "scene/gui/label.h" FileDialog::GetIconFunc FileDialog::get_icon_func = NULL; @@ -284,7 +284,13 @@ bool FileDialog::_is_open_should_be_disabled() { if (mode == MODE_OPEN_ANY || mode == MODE_SAVE_FILE) return false; - TreeItem *ti = tree->get_selected(); + TreeItem *ti = tree->get_next_selected(tree->get_root()); + while (ti) { + TreeItem *prev_ti = ti; + ti = tree->get_next_selected(tree->get_root()); + if (ti == prev_ti) + break; + } // We have something that we can't select? if (!ti) return mode != MODE_OPEN_DIR; // In "Open folder" mode, having nothing selected picks the current folder. @@ -324,10 +330,18 @@ void FileDialog::deselect_items() { case MODE_OPEN_DIR: get_ok()->set_text(RTR("Select Current Folder")); break; + case MODE_OPEN_ANY: + case MODE_SAVE_FILE: + // FIXME: Implement, or refactor to avoid duplication with set_mode + break; } } } +void FileDialog::_tree_multi_selected(Object *p_object, int p_cell, bool p_selected) { + _tree_selected(); +} + void FileDialog::_tree_selected() { TreeItem *ti = tree->get_selected(); @@ -339,7 +353,7 @@ void FileDialog::_tree_selected() { file->set_text(d["name"]); } else if (mode == MODE_OPEN_DIR) { - get_ok()->set_text(RTR("Select this Folder")); + get_ok()->set_text(RTR("Select This Folder")); } get_ok()->set_disabled(_is_open_should_be_disabled()); @@ -754,6 +768,7 @@ void FileDialog::_bind_methods() { ClassDB::bind_method(D_METHOD("_unhandled_input"), &FileDialog::_unhandled_input); + ClassDB::bind_method(D_METHOD("_tree_multi_selected"), &FileDialog::_tree_multi_selected); ClassDB::bind_method(D_METHOD("_tree_selected"), &FileDialog::_tree_selected); ClassDB::bind_method(D_METHOD("_tree_item_activated"), &FileDialog::_tree_item_activated); ClassDB::bind_method(D_METHOD("_dir_entered"), &FileDialog::_dir_entered); @@ -794,7 +809,7 @@ void FileDialog::_bind_methods() { ClassDB::bind_method(D_METHOD("invalidate"), &FileDialog::invalidate); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "mode_overrides_title"), "set_mode_overrides_title", "is_mode_overriding_title"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Open one,Open many,Open folder,Open any,Save"), "set_mode", "get_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Open File,Open Files,Open Folder,Open Any,Save"), "set_mode", "get_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "access", PROPERTY_HINT_ENUM, "Resources,User data,File system"), "set_access", "get_access"); ADD_PROPERTY(PropertyInfo(Variant::POOL_STRING_ARRAY, "filters"), "set_filters", "get_filters"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_hidden_files"), "set_show_hidden_files", "is_showing_hidden_files"); @@ -890,6 +905,7 @@ FileDialog::FileDialog() { _update_drives(); connect("confirmed", this, "_action_pressed"); + tree->connect("multi_selected", this, "_tree_multi_selected", varray(), CONNECT_DEFERRED); tree->connect("cell_selected", this, "_tree_selected", varray(), CONNECT_DEFERRED); tree->connect("item_activated", this, "_tree_item_activated", varray()); tree->connect("nothing_selected", this, "deselect_items"); diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h index ad483d5dab..8bd15080d3 100644 --- a/scene/gui/file_dialog.h +++ b/scene/gui/file_dialog.h @@ -32,7 +32,7 @@ #define FILE_DIALOG_H #include "box_container.h" -#include "os/dir_access.h" +#include "core/os/dir_access.h" #include "scene/gui/dialogs.h" #include "scene/gui/line_edit.h" #include "scene/gui/option_button.h" @@ -104,6 +104,7 @@ private: void update_file_list(); void update_filters(); + void _tree_multi_selected(Object *p_object, int p_cell, bool p_selected); void _tree_selected(); void _select_drive(int p_idx); diff --git a/scene/gui/gradient_edit.cpp b/scene/gui/gradient_edit.cpp index e82c0c4ad1..c13964d196 100644 --- a/scene/gui/gradient_edit.cpp +++ b/scene/gui/gradient_edit.cpp @@ -29,7 +29,10 @@ /*************************************************************************/ #include "gradient_edit.h" -#include "os/keyboard.h" +#include "core/os/keyboard.h" +#include "editor/editor_scale.h" + +#define SPACING (3 * EDSCALE) GradientEdit::GradientEdit() { grabbed = -1; @@ -49,11 +52,15 @@ GradientEdit::GradientEdit() { int GradientEdit::_get_point_from_pos(int x) { int result = -1; - int total_w = get_size().width - get_size().height - 3; + int total_w = get_size().width - get_size().height - SPACING; + float min_distance = 1e20; for (int i = 0; i < points.size(); i++) { //Check if we clicked at point - if (ABS(x - points[i].offset * total_w + 1) < (POINT_WIDTH / 2 + 1)) { + float distance = ABS(x - points[i].offset * total_w); + float min = (POINT_WIDTH / 2 * 1.7); //make it easier to grab + if (distance <= min && distance < min_distance) { result = i; + min_distance = distance; } } return result; @@ -63,7 +70,16 @@ void GradientEdit::_show_color_picker() { if (grabbed == -1) return; picker->set_pick_color(points[grabbed].color); - popup->set_position(get_global_position() - popup->get_combined_minimum_size()); + Size2 minsize = popup->get_combined_minimum_size(); + bool show_above = false; + if (get_global_position().y + get_size().y + minsize.y > get_viewport_rect().size.y) { + show_above = true; + } + if (show_above) { + popup->set_position(get_global_position() - Vector2(0, minsize.y)); + } else { + popup->set_position(get_global_position() + Vector2(0, get_size().y)); + } popup->popup(); } @@ -112,7 +128,7 @@ void GradientEdit::_gui_input(const Ref<InputEvent> &p_event) { grabbed = _get_point_from_pos(x); if (grabbed != -1) { - int total_w = get_size().width - get_size().height - 3; + int total_w = get_size().width - get_size().height - SPACING; Gradient::Point newPoint = points[grabbed]; newPoint.offset = CLAMP(x / float(total_w), 0, 1); @@ -130,14 +146,15 @@ void GradientEdit::_gui_input(const Ref<InputEvent> &p_event) { } } + //select if (mb.is_valid() && mb->get_button_index() == 1 && mb->is_pressed()) { update(); int x = mb->get_position().x; - int total_w = get_size().width - get_size().height - 3; + int total_w = get_size().width - get_size().height - SPACING; //Check if color selector was clicked. - if (x > total_w + 3) { + if (x > total_w + SPACING) { _show_color_picker(); return; } @@ -211,7 +228,7 @@ void GradientEdit::_gui_input(const Ref<InputEvent> &p_event) { if (mm.is_valid() && grabbing) { - int total_w = get_size().width - get_size().height - 3; + int total_w = get_size().width - get_size().height - SPACING; int x = mm->get_position().x; @@ -219,23 +236,23 @@ void GradientEdit::_gui_input(const Ref<InputEvent> &p_event) { //Snap to nearest point if holding shift if (mm->get_shift()) { - float snap_treshhold = 0.03; - float smallest_ofs = snap_treshhold; - bool founded = false; - int nearest_point; + float snap_threshold = 0.03; + float smallest_ofs = snap_threshold; + bool found = false; + int nearest_point = 0; for (int i = 0; i < points.size(); ++i) { if (i != grabbed) { float temp_ofs = ABS(points[i].offset - newofs); if (temp_ofs < smallest_ofs) { smallest_ofs = temp_ofs; nearest_point = i; - if (founded) + if (found) break; - founded = true; + found = true; } } } - if (founded) { + if (found) { if (points[nearest_point].offset < newofs) newofs = points[nearest_point].offset + 0.00001; else @@ -286,7 +303,7 @@ void GradientEdit::_notification(int p_what) { if (w == 0 || h == 0) return; //Safety check. We have division by 'h'. And in any case there is nothing to draw with such size - int total_w = get_size().width - get_size().height - 3; + int total_w = get_size().width - get_size().height - SPACING; //Draw checker pattern for ramp _draw_checker(0, 0, total_w, h); @@ -335,27 +352,36 @@ void GradientEdit::_notification(int p_what) { //Draw point markers for (int i = 0; i < points.size(); i++) { - Color col = i == grabbed ? Color(1, 0.0, 0.0, 0.9) : points[i].color.contrasted(); + Color col = points[i].color.contrasted(); col.a = 0.9; draw_line(Vector2(points[i].offset * total_w, 0), Vector2(points[i].offset * total_w, h / 2), col); - draw_rect(Rect2(points[i].offset * total_w - POINT_WIDTH / 2, h / 2, POINT_WIDTH, h / 2), Color(0.6, 0.6, 0.6, i == grabbed ? 0.9 : 0.4)); - draw_line(Vector2(points[i].offset * total_w - POINT_WIDTH / 2, h / 2), Vector2(points[i].offset * total_w - POINT_WIDTH / 2, h - 1), col); - draw_line(Vector2(points[i].offset * total_w + POINT_WIDTH / 2, h / 2), Vector2(points[i].offset * total_w + POINT_WIDTH / 2, h - 1), col); - draw_line(Vector2(points[i].offset * total_w - POINT_WIDTH / 2, h / 2), Vector2(points[i].offset * total_w + POINT_WIDTH / 2, h / 2), col); - draw_line(Vector2(points[i].offset * total_w - POINT_WIDTH / 2, h - 1), Vector2(points[i].offset * total_w + POINT_WIDTH / 2, h - 1), col); + Rect2 rect = Rect2(points[i].offset * total_w - POINT_WIDTH / 2, h / 2, POINT_WIDTH, h / 2); + draw_rect(rect, points[i].color, true); + draw_rect(rect, col, false); + if (grabbed == i) { + rect = rect.grow(-1); + if (has_focus()) { + draw_rect(rect, Color(1, 0, 0, 0.9), false); + } else { + draw_rect(rect, Color(0.6, 0, 0, 0.9), false); + } + + rect = rect.grow(-1); + draw_rect(rect, col, false); + } } //Draw "button" for color selector - _draw_checker(total_w + 3, 0, h, h); + _draw_checker(total_w + SPACING, 0, h, h); if (grabbed != -1) { //Draw with selection color - draw_rect(Rect2(total_w + 3, 0, h, h), points[grabbed].color); + draw_rect(Rect2(total_w + SPACING, 0, h, h), points[grabbed].color); } else { //if no color selected draw grey color with 'X' on top. - draw_rect(Rect2(total_w + 3, 0, h, h), Color(0.5, 0.5, 0.5, 1)); - draw_line(Vector2(total_w + 3, 0), Vector2(total_w + 3 + h, h), Color(1, 1, 1, 0.6)); - draw_line(Vector2(total_w + 3, h), Vector2(total_w + 3 + h, 0), Color(1, 1, 1, 0.6)); + draw_rect(Rect2(total_w + SPACING, 0, h, h), Color(0.5, 0.5, 0.5, 1)); + draw_line(Vector2(total_w + SPACING, 0), Vector2(total_w + SPACING + h, h), Color(1, 1, 1, 0.6)); + draw_line(Vector2(total_w + SPACING, h), Vector2(total_w + SPACING + h, 0), Color(1, 1, 1, 0.6)); } //Draw borders around color ramp if in focus diff --git a/scene/gui/gradient_edit.h b/scene/gui/gradient_edit.h index e7834ea0de..f6927ad0b7 100644 --- a/scene/gui/gradient_edit.h +++ b/scene/gui/gradient_edit.h @@ -36,7 +36,7 @@ #include "scene/resources/color_ramp.h" #include "scene/resources/default_theme/theme_data.h" -#define POINT_WIDTH 8 +#define POINT_WIDTH (8 * EDSCALE) class GradientEdit : public Control { diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index a7163adbe6..b3bebc88ec 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -30,8 +30,8 @@ #include "graph_edit.h" -#include "os/input.h" -#include "os/keyboard.h" +#include "core/os/input.h" +#include "core/os/keyboard.h" #include "scene/gui/box_container.h" #define ZOOM_SCALE 1.2 @@ -406,7 +406,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { connecting_color = Object::cast_to<GraphNode>(to)->get_connection_input_color(E->get().to_port); connecting_target = false; connecting_to = pos; - just_disconected = true; + just_disconnected = true; emit_signal("disconnection_request", E->get().from, E->get().from_port, E->get().to, E->get().to_port); to = get_node(String(connecting_from)); //maybe it was erased @@ -427,7 +427,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { connecting_color = gn->get_connection_output_color(j); connecting_target = false; connecting_to = pos; - just_disconected = false; + just_disconnected = false; return; } } @@ -453,7 +453,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { connecting_color = Object::cast_to<GraphNode>(fr)->get_connection_output_color(E->get().from_port); connecting_target = false; connecting_to = pos; - just_disconected = true; + just_disconnected = true; emit_signal("disconnection_request", E->get().from, E->get().from_port, E->get().to, E->get().to_port); fr = get_node(String(connecting_from)); //maybe it was erased @@ -474,7 +474,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { connecting_color = gn->get_connection_input_color(j); connecting_target = false; connecting_to = pos; - just_disconected = true; + just_disconnected = true; return; } @@ -544,7 +544,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { } emit_signal("connection_request", from, from_slot, to, to_slot); - } else if (!just_disconected) { + } else if (!just_disconnected) { String from = connecting_from; int from_slot = connecting_index; Vector2 ofs = Vector2(mb->get_position().x, mb->get_position().y); @@ -1042,7 +1042,7 @@ void GraphEdit::set_connection_activity(const StringName &p_from, int p_from_por if (E->get().from == p_from && E->get().from_port == p_from_port && E->get().to == p_to && E->get().to_port == p_to_port) { - if (ABS(E->get().activity != p_activity)) { + if (ABS(E->get().activity - p_activity) < CMP_EPSILON) { //update only if changed top_layer->update(); connections_layer->update(); @@ -1279,7 +1279,7 @@ void GraphEdit::_bind_methods() { ADD_SIGNAL(MethodInfo("disconnection_request", PropertyInfo(Variant::STRING, "from"), PropertyInfo(Variant::INT, "from_slot"), PropertyInfo(Variant::STRING, "to"), PropertyInfo(Variant::INT, "to_slot"))); ADD_SIGNAL(MethodInfo("popup_request", PropertyInfo(Variant::VECTOR2, "p_position"))); ADD_SIGNAL(MethodInfo("duplicate_nodes_request")); - ADD_SIGNAL(MethodInfo("node_selected", PropertyInfo(Variant::OBJECT, "node"))); + ADD_SIGNAL(MethodInfo("node_selected", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); ADD_SIGNAL(MethodInfo("connection_to_empty", PropertyInfo(Variant::STRING, "from"), PropertyInfo(Variant::INT, "from_slot"), PropertyInfo(Variant::VECTOR2, "release_position"))); ADD_SIGNAL(MethodInfo("delete_nodes_request")); ADD_SIGNAL(MethodInfo("_begin_node_move")); @@ -1304,7 +1304,7 @@ GraphEdit::GraphEdit() { add_child(connections_layer); connections_layer->connect("draw", this, "_connections_layer_draw"); connections_layer->set_name("CLAYER"); - connections_layer->set_disable_visibility_clip(true); // so it can draw freely and be offseted + connections_layer->set_disable_visibility_clip(true); // so it can draw freely and be offset connections_layer->set_mouse_filter(MOUSE_FILTER_IGNORE); h_scroll = memnew(HScrollBar); @@ -1368,6 +1368,6 @@ GraphEdit::GraphEdit() { zoom_hb->add_child(snap_amount); setting_scroll_ofs = false; - just_disconected = false; + just_disconnected = false; set_clip_contents(true); } diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h index 31a449eb59..71165e3dc9 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -94,7 +94,7 @@ private: Vector2 connecting_to; String connecting_target_to; int connecting_target_index; - bool just_disconected; + bool just_disconnected; bool dragging; bool just_selected; diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp index 24857d49fa..b189afd30b 100644 --- a/scene/gui/graph_node.cpp +++ b/scene/gui/graph_node.cpp @@ -29,7 +29,8 @@ /*************************************************************************/ #include "graph_node.h" -#include "method_bind_ext.gen.inc" + +#include "core/method_bind_ext.gen.inc" bool GraphNode::_set(const StringName &p_name, const Variant &p_value) { diff --git a/scene/gui/grid_container.cpp b/scene/gui/grid_container.cpp index 278e4123d7..bb1d1d7695 100644 --- a/scene/gui/grid_container.cpp +++ b/scene/gui/grid_container.cpp @@ -184,8 +184,6 @@ void GridContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_columns", "columns"), &GridContainer::set_columns); ClassDB::bind_method(D_METHOD("get_columns"), &GridContainer::get_columns); - ClassDB::bind_method(D_METHOD("get_child_control_at_cell", "row", "column"), - &GridContainer::get_child_control_at_cell); ADD_PROPERTY(PropertyInfo(Variant::INT, "columns", PROPERTY_HINT_RANGE, "1,1024,1"), "set_columns", "get_columns"); } @@ -241,21 +239,6 @@ Size2 GridContainer::get_minimum_size() const { return ms; } -Control *GridContainer::get_child_control_at_cell(int row, int column) { - Control *c; - int grid_index = row * columns + column; - for (int i = 0; i < get_child_count(); i++) { - c = Object::cast_to<Control>(get_child(i)); - if (!c || !c->is_visible_in_tree()) - continue; - - if (grid_index == i) { - break; - } - } - return c; -} - GridContainer::GridContainer() { set_mouse_filter(MOUSE_FILTER_PASS); diff --git a/scene/gui/grid_container.h b/scene/gui/grid_container.h index 7e3470dc89..243d06f034 100644 --- a/scene/gui/grid_container.h +++ b/scene/gui/grid_container.h @@ -47,7 +47,6 @@ public: void set_columns(int p_columns); int get_columns() const; virtual Size2 get_minimum_size() const; - Control *get_child_control_at_cell(int row, int column); GridContainer(); }; diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index d61bd97c2a..cb37fa396d 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -29,13 +29,14 @@ /*************************************************************************/ #include "item_list.h" -#include "os/os.h" -#include "project_settings.h" +#include "core/os/os.h" +#include "core/project_settings.h" void ItemList::add_item(const String &p_item, const Ref<Texture> &p_texture, bool p_selectable) { Item item; item.icon = p_texture; + item.icon_transposed = false; item.icon_region = Rect2i(); item.icon_modulate = Color(1, 1, 1, 1); item.text = p_item; @@ -54,6 +55,7 @@ void ItemList::add_icon_item(const Ref<Texture> &p_item, bool p_selectable) { Item item; item.icon = p_item; + item.icon_transposed = false; item.icon_region = Rect2i(); item.icon_modulate = Color(1, 1, 1, 1); //item.text=p_item; @@ -124,6 +126,22 @@ Ref<Texture> ItemList::get_item_icon(int p_idx) const { return items[p_idx].icon; } +void ItemList::set_item_icon_transposed(int p_idx, const bool p_transposed) { + + ERR_FAIL_INDEX(p_idx, items.size()); + + items.write[p_idx].icon_transposed = p_transposed; + update(); + shape_changed = true; +} + +bool ItemList::is_item_icon_transposed(int p_idx) const { + + ERR_FAIL_INDEX_V(p_idx, items.size(), false); + + return items[p_idx].icon_transposed; +} + void ItemList::set_item_icon_region(int p_idx, const Rect2 &p_region) { ERR_FAIL_INDEX(p_idx, items.size()); @@ -416,6 +434,7 @@ void ItemList::set_icon_mode(IconMode p_mode) { update(); shape_changed = true; } + ItemList::IconMode ItemList::get_icon_mode() const { return icon_mode; @@ -435,10 +454,15 @@ Size2 ItemList::Item::get_icon_size() const { if (icon.is_null()) return Size2(); - if (icon_region.has_no_area()) - return icon->get_size(); - return icon_region.size; + Size2 size_result = Size2(icon_region.size).abs(); + if (icon_transposed) { + Size2 size_tmp = size_result; + size_result.x = size_tmp.y; + size_result.y = size_tmp.x; + } + + return size_result; } void ItemList::_gui_input(const Ref<InputEvent> &p_event) { @@ -502,7 +526,7 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) { bool selected = !items[j].selected; select(j, false); if (selected) - emit_signal("multi_selected", i, true); + emit_signal("multi_selected", j, true); } if (mb->get_button_index() == BUTTON_RIGHT) { @@ -1067,10 +1091,14 @@ void ItemList::_notification(int p_what) { if (items[i].disabled) modulate.a *= 0.5; - if (items[i].icon_region.has_no_area()) - draw_texture_rect(items[i].icon, draw_rect, false, modulate); - else - draw_texture_rect_region(items[i].icon, draw_rect, items[i].icon_region, modulate); + // If the icon is transposed, we have to swith the size so that it is drawn correctly + if (items[i].icon_transposed) { + Size2 size_tmp = draw_rect.size; + draw_rect.size.x = size_tmp.y; + draw_rect.size.y = size_tmp.x; + } + + draw_texture_rect_region(items[i].icon, draw_rect, items[i].icon_region, modulate, items[i].icon_transposed); } if (items[i].tag_icon.is_valid()) { @@ -1405,6 +1433,9 @@ void ItemList::_bind_methods() { ClassDB::bind_method(D_METHOD("set_item_icon", "idx", "icon"), &ItemList::set_item_icon); ClassDB::bind_method(D_METHOD("get_item_icon", "idx"), &ItemList::get_item_icon); + ClassDB::bind_method(D_METHOD("set_item_icon_transposed", "idx", "rect"), &ItemList::set_item_icon_transposed); + ClassDB::bind_method(D_METHOD("is_item_icon_transposed", "idx"), &ItemList::is_item_icon_transposed); + ClassDB::bind_method(D_METHOD("set_item_icon_region", "idx", "rect"), &ItemList::set_item_icon_region); ClassDB::bind_method(D_METHOD("get_item_icon_region", "idx"), &ItemList::get_item_icon_region); @@ -1524,6 +1555,7 @@ void ItemList::_bind_methods() { ADD_SIGNAL(MethodInfo("nothing_selected")); GLOBAL_DEF("gui/timers/incremental_search_max_interval_msec", 2000); + ProjectSettings::get_singleton()->set_custom_property_info("gui/timers/incremental_search_max_interval_msec", PropertyInfo(Variant::INT, "gui/timers/incremental_search_max_interval_msec", PROPERTY_HINT_RANGE, "0,10000,1,or_greater")); // No negative numbers } ItemList::ItemList() { diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h index 58771c1777..acd17986eb 100644 --- a/scene/gui/item_list.h +++ b/scene/gui/item_list.h @@ -53,6 +53,7 @@ private: struct Item { Ref<Texture> icon; + bool icon_transposed; Rect2i icon_region; Color icon_modulate; Ref<Texture> tag_icon; @@ -133,6 +134,9 @@ public: void set_item_icon(int p_idx, const Ref<Texture> &p_icon); Ref<Texture> get_item_icon(int p_idx) const; + void set_item_icon_transposed(int p_idx, const bool transposed); + bool is_item_icon_transposed(int p_idx) const; + void set_item_icon_region(int p_idx, const Rect2 &p_region); Rect2 get_item_icon_region(int p_idx) const; diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index 0b36e1663c..6ff2d232f0 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -29,9 +29,9 @@ /*************************************************************************/ #include "label.h" -#include "print_string.h" -#include "project_settings.h" -#include "translation.h" +#include "core/print_string.h" +#include "core/project_settings.h" +#include "core/translation.h" void Label::set_autowrap(bool p_autowrap) { @@ -295,14 +295,13 @@ Size2 Label::get_minimum_size() const { Size2 min_style = get_stylebox("normal")->get_minimum_size(); + // don't want to mutable everything + if (word_cache_dirty) + const_cast<Label *>(this)->regenerate_word_cache(); + if (autowrap) return Size2(1, clip ? 1 : minsize.height) + min_style; else { - - // don't want to mutable everything - if (word_cache_dirty) - const_cast<Label *>(this)->regenerate_word_cache(); - Size2 ms = minsize; if (clip) ms.width = 1; @@ -512,7 +511,7 @@ void Label::regenerate_word_cache() { void Label::set_align(Align p_align) { - ERR_FAIL_INDEX(p_align, 4); + ERR_FAIL_INDEX((int)p_align, 4); align = p_align; update(); } @@ -524,7 +523,7 @@ Label::Align Label::get_align() const { void Label::set_valign(VAlign p_align) { - ERR_FAIL_INDEX(p_align, 4); + ERR_FAIL_INDEX((int)p_align, 4); valign = p_align; update(); } diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 549daecdae..c4373876b1 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -29,12 +29,12 @@ /*************************************************************************/ #include "line_edit.h" +#include "core/message_queue.h" +#include "core/os/keyboard.h" +#include "core/os/os.h" +#include "core/print_string.h" +#include "core/translation.h" #include "label.h" -#include "message_queue.h" -#include "os/keyboard.h" -#include "os/os.h" -#include "print_string.h" -#include "translation.h" #ifdef TOOLS_ENABLED #include "editor/editor_scale.h" @@ -527,7 +527,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { void LineEdit::set_align(Align p_align) { - ERR_FAIL_INDEX(p_align, 4); + ERR_FAIL_INDEX((int)p_align, 4); align = p_align; update(); } @@ -866,15 +866,14 @@ void LineEdit::_notification(int p_what) { void LineEdit::copy_text() { - if (selection.enabled) { - + if (selection.enabled && !pass) { OS::get_singleton()->set_clipboard(text.substr(selection.begin, selection.end - selection.begin)); } } void LineEdit::cut_text() { - if (selection.enabled) { + if (selection.enabled && !pass) { OS::get_singleton()->set_clipboard(text.substr(selection.begin, selection.end - selection.begin)); selection_delete(); } @@ -1609,6 +1608,8 @@ LineEdit::LineEdit() { text_changed_dirty = false; placeholder_alpha = 0.6; clear_button_enabled = false; + clear_button_status.press_attempt = false; + clear_button_status.pressing_inside = false; deselect(); set_focus_mode(FOCUS_ALL); diff --git a/scene/gui/link_button.cpp b/scene/gui/link_button.cpp index 8560efdde5..c1561e97ba 100644 --- a/scene/gui/link_button.cpp +++ b/scene/gui/link_button.cpp @@ -75,6 +75,7 @@ void LinkButton::_notification(int p_what) { color = get_color("font_color"); do_underline = underline_mode == UNDERLINE_MODE_ALWAYS; } break; + case DRAW_HOVER_PRESSED: case DRAW_PRESSED: { if (has_color("font_color_pressed")) diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp index 87cf4dc334..95ec618c3b 100644 --- a/scene/gui/menu_button.cpp +++ b/scene/gui/menu_button.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "menu_button.h" -#include "os/keyboard.h" +#include "core/os/keyboard.h" #include "scene/main/viewport.h" void MenuButton::_unhandled_key_input(Ref<InputEvent> p_event) { diff --git a/scene/gui/nine_patch_rect.cpp b/scene/gui/nine_patch_rect.cpp index b8f6ffe6d2..c6a34921c7 100644 --- a/scene/gui/nine_patch_rect.cpp +++ b/scene/gui/nine_patch_rect.cpp @@ -110,7 +110,7 @@ Ref<Texture> NinePatchRect::get_texture() const { void NinePatchRect::set_patch_margin(Margin p_margin, int p_size) { - ERR_FAIL_INDEX(p_margin, 4); + ERR_FAIL_INDEX((int)p_margin, 4); margin[p_margin] = p_size; update(); minimum_size_changed(); @@ -132,7 +132,7 @@ void NinePatchRect::set_patch_margin(Margin p_margin, int p_size) { int NinePatchRect::get_patch_margin(Margin p_margin) const { - ERR_FAIL_INDEX_V(p_margin, 4, 0); + ERR_FAIL_INDEX_V((int)p_margin, 4, 0); return margin[p_margin]; } diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index 2901176a69..6b847c6483 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "option_button.h" -#include "print_string.h" +#include "core/print_string.h" Size2 OptionButton::get_minimum_size() const { diff --git a/scene/gui/panel.cpp b/scene/gui/panel.cpp index 4375e03a50..d3b7b72ee1 100644 --- a/scene/gui/panel.cpp +++ b/scene/gui/panel.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "panel.h" -#include "print_string.h" +#include "core/print_string.h" void Panel::_notification(int p_what) { diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp index 26d01ecc09..bfbe62e1c7 100644 --- a/scene/gui/popup.cpp +++ b/scene/gui/popup.cpp @@ -30,8 +30,8 @@ #include "popup.h" -#include "engine.h" -#include "os/keyboard.h" +#include "core/engine.h" +#include "core/os/keyboard.h" void Popup::_gui_input(Ref<InputEvent> p_event) { } @@ -234,15 +234,46 @@ String Popup::get_configuration_warning() const { Popup::~Popup() { } -void PopupPanel::set_child_rect(Control *p_child) { - ERR_FAIL_NULL(p_child); +Size2 PopupPanel::get_minimum_size() const { Ref<StyleBox> p = get_stylebox("panel"); - p_child->set_anchors_preset(Control::PRESET_WIDE); - p_child->set_margin(MARGIN_LEFT, p->get_margin(MARGIN_LEFT)); - p_child->set_margin(MARGIN_RIGHT, -p->get_margin(MARGIN_RIGHT)); - p_child->set_margin(MARGIN_TOP, p->get_margin(MARGIN_TOP)); - p_child->set_margin(MARGIN_BOTTOM, -p->get_margin(MARGIN_BOTTOM)); + + Size2 ms; + + for (int i = 0; i < get_child_count(); i++) { + Control *c = Object::cast_to<Control>(get_child(i)); + if (!c) + continue; + + if (c->is_set_as_toplevel()) + continue; + + Size2 cms = c->get_combined_minimum_size(); + ms.x = MAX(cms.x, ms.x); + ms.y = MAX(cms.y, ms.y); + } + + return ms + p->get_minimum_size(); +} + +void PopupPanel::_update_child_rects() { + + Ref<StyleBox> p = get_stylebox("panel"); + + Vector2 cpos(p->get_offset()); + Vector2 csize(get_size() - p->get_minimum_size()); + + for (int i = 0; i < get_child_count(); i++) { + Control *c = Object::cast_to<Control>(get_child(i)); + if (!c) + continue; + + if (c->is_set_as_toplevel()) + continue; + + c->set_position(cpos); + c->set_size(csize); + } } void PopupPanel::_notification(int p_what) { @@ -250,6 +281,12 @@ void PopupPanel::_notification(int p_what) { if (p_what == NOTIFICATION_DRAW) { get_stylebox("panel")->draw(get_canvas_item(), Rect2(Point2(), get_size())); + } else if (p_what == NOTIFICATION_READY) { + + _update_child_rects(); + } else if (p_what == NOTIFICATION_RESIZED) { + + _update_child_rects(); } } diff --git a/scene/gui/popup.h b/scene/gui/popup.h index 550e803578..5b1ef7d6ca 100644 --- a/scene/gui/popup.h +++ b/scene/gui/popup.h @@ -77,10 +77,12 @@ class PopupPanel : public Popup { GDCLASS(PopupPanel, Popup); protected: + void _update_child_rects(); void _notification(int p_what); public: void set_child_rect(Control *p_child); + virtual Size2 get_minimum_size() const; PopupPanel(); }; diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 436dda41a4..f84b75d8d8 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -29,10 +29,10 @@ /*************************************************************************/ #include "popup_menu.h" -#include "os/input.h" -#include "os/keyboard.h" -#include "print_string.h" -#include "translation.h" +#include "core/os/input.h" +#include "core/os/keyboard.h" +#include "core/print_string.h" +#include "core/translation.h" String PopupMenu::_get_accel_text(int p_item) const { @@ -557,6 +557,21 @@ void PopupMenu::_notification(int p_what) { mouse_over = -1; update(); } + + for (int i = 0; i < items.size(); i++) { + if (items[i].submenu == "") + continue; + + Node *n = get_node(items[i].submenu); + if (!n) + continue; + + PopupMenu *pm = Object::cast_to<PopupMenu>(n); + if (!pm || !pm->is_visible()) + continue; + + pm->hide(); + } } break; } } @@ -1012,8 +1027,7 @@ bool PopupMenu::activate_item_by_event(const Ref<InputEvent> &p_event, bool p_fo code |= KEY_MASK_SHIFT; } - int il = items.size(); - for (int i = 0; i < il; i++) { + for (int i = 0; i < items.size(); i++) { if (is_item_disabled(i) || items[i].shortcut_is_disabled) continue; diff --git a/scene/gui/range.cpp b/scene/gui/range.cpp index 09d8664240..e862743934 100644 --- a/scene/gui/range.cpp +++ b/scene/gui/range.cpp @@ -30,6 +30,19 @@ #include "range.h" +String Range::get_configuration_warning() const { + String warning = Control::get_configuration_warning(); + + if (shared->exp_ratio && shared->min <= 0) { + if (warning != String()) { + warning += "\n"; + } + warning += TTR("If exp_edit is true min_value must be > 0."); + } + + return warning; +} + void Range::_value_changed_notify() { _value_changed(shared->val); @@ -66,10 +79,11 @@ void Range::Shared::emit_changed(const char *p_what) { } void Range::set_value(double p_val) { + if (shared->step > 0) + p_val = Math::round(p_val / shared->step) * shared->step; - if (_rounded_values) { + if (_rounded_values) p_val = Math::round(p_val); - } if (!shared->allow_greater && p_val > shared->max - shared->page) p_val = shared->max - shared->page; @@ -90,6 +104,8 @@ void Range::set_min(double p_min) { set_value(shared->val); shared->emit_changed("min"); + + update_configuration_warning(); } void Range::set_max(double p_max) { @@ -277,6 +293,8 @@ bool Range::is_using_rounded_values() const { void Range::set_exp_ratio(bool p_enable) { shared->exp_ratio = p_enable; + + update_configuration_warning(); } bool Range::is_ratio_exp() const { diff --git a/scene/gui/range.h b/scene/gui/range.h index 125f559248..58f15c8aa8 100644 --- a/scene/gui/range.h +++ b/scene/gui/range.h @@ -97,6 +97,8 @@ public: void share(Range *p_range); void unshare(); + virtual String get_configuration_warning() const; + Range(); ~Range(); }; diff --git a/scene/gui/reference_rect.cpp b/scene/gui/reference_rect.cpp index 74e68598f4..e96e5afb2a 100644 --- a/scene/gui/reference_rect.cpp +++ b/scene/gui/reference_rect.cpp @@ -30,7 +30,7 @@ #include "reference_rect.h" -#include "engine.h" +#include "core/engine.h" void ReferenceRect::_notification(int p_what) { diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index a5f9bea1b1..9f1687262f 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -29,12 +29,13 @@ /*************************************************************************/ #include "rich_text_label.h" -#include "os/keyboard.h" -#include "os/os.h" + +#include "core/os/keyboard.h" +#include "core/os/os.h" #include "scene/scene_string_names.h" #ifdef TOOLS_ENABLED -#include "editor/editor_node.h" +#include "editor/editor_scale.h" #endif RichTextLabel::Item *RichTextLabel::_get_next_item(Item *p_item, bool p_free) { @@ -219,13 +220,14 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & case ALIGN_LEFT: l.offset_caches.push_back(0); break; \ case ALIGN_CENTER: l.offset_caches.push_back(((p_width - margin) - used) / 2); break; \ case ALIGN_RIGHT: l.offset_caches.push_back(((p_width - margin) - used)); break; \ - case ALIGN_FILL: l.offset_caches.push_back((p_width - margin) - used /*+spaces_size*/); break; \ + case ALIGN_FILL: l.offset_caches.push_back(line_wrapped ? ((p_width - margin) - used) : 0); break; \ } \ l.height_caches.push_back(line_height); \ l.ascent_caches.push_back(line_ascent); \ l.descent_caches.push_back(line_descent); \ l.space_caches.push_back(spaces); \ } \ + line_wrapped = false; \ y += line_height + get_constant(SceneStringNames::get_singleton()->line_separation); \ line_height = 0; \ line_ascent = 0; \ @@ -253,6 +255,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & l.minimum_width = MAX(l.minimum_width, m_width); \ } \ if (wofs + m_width > p_width) { \ + line_wrapped = true; \ if (p_mode == PROCESS_CACHE) { \ if (spaces > 0) \ spaces -= 1; \ @@ -297,6 +300,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & int rchar = 0; int lh = 0; bool line_is_blank = true; + bool line_wrapped = false; int fh = 0; while (it) { @@ -319,14 +323,17 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & Color color; Color font_color_shadow; bool underline = false; + bool strikethrough = false; if (p_mode == PROCESS_DRAW) { color = _find_color(text, p_base_color); font_color_shadow = _find_color(text, p_font_color_shadow); - underline = _find_underline(text); - if (_find_meta(text, &meta) && underline_meta) { + if (_find_underline(text) || (_find_meta(text, &meta) && underline_meta)) { underline = true; + } else if (_find_strikethrough(text)) { + + strikethrough = true; } } else if (p_mode == PROCESS_CACHE) { @@ -417,7 +424,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & int cw = 0; - bool visible = visible_characters < 0 || p_char_count < visible_characters && YRANGE_VISIBLE(y + lh - line_descent - line_ascent, line_ascent + line_descent); + bool visible = visible_characters < 0 || (p_char_count < visible_characters && YRANGE_VISIBLE(y + lh - line_descent - line_ascent, line_ascent + line_descent)); if (visible) line_is_blank = false; @@ -468,6 +475,15 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & underline_width *= EDSCALE; #endif VS::get_singleton()->canvas_item_add_line(ci, p_ofs + Point2(align_ofs + wofs, uy), p_ofs + Point2(align_ofs + wofs + w, uy), uc, underline_width); + } else if (strikethrough) { + Color uc = color; + uc.a *= 0.5; + int uy = y + lh / 2 - line_descent + 2; + float strikethrough_width = 1.0; +#ifdef TOOLS_ENABLED + strikethrough_width *= EDSCALE; +#endif + VS::get_singleton()->canvas_item_add_line(ci, p_ofs + Point2(align_ofs + wofs, uy), p_ofs + Point2(align_ofs + wofs + w, uy), uc, strikethrough_width); } } @@ -496,7 +512,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & ENSURE_WIDTH(img->image->get_width()); - bool visible = visible_characters < 0 || p_char_count < visible_characters && YRANGE_VISIBLE(y + lh - font->get_descent() - img->image->get_height(), img->image->get_height()); + bool visible = visible_characters < 0 || (p_char_count < visible_characters && YRANGE_VISIBLE(y + lh - font->get_descent() - img->image->get_height(), img->image->get_height())); if (visible) line_is_blank = false; @@ -834,8 +850,6 @@ void RichTextLabel::_notification(int p_what) { bool use_outline = get_constant("shadow_as_outline"); Point2 shadow_ofs(get_constant("shadow_offset_x"), get_constant("shadow_offset_y")); - float x_ofs = 0; - visible_line_count = 0; while (y < size.height && from_line < main->lines.size()) { @@ -853,7 +867,6 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item if (r_click_item) *r_click_item = NULL; - Size2 size = get_size(); Rect2 text_rect = _get_text_rect(); int ofs = vscroll->get_value(); Color font_color_shadow = get_color("font_color_shadow"); @@ -1226,6 +1239,23 @@ bool RichTextLabel::_find_underline(Item *p_item) { return false; } +bool RichTextLabel::_find_strikethrough(Item *p_item) { + + Item *item = p_item; + + while (item) { + + if (item->type == ITEM_STRIKETHROUGH) { + + return true; + } + + item = item->parent; + } + + return false; +} + bool RichTextLabel::_find_meta(Item *p_item, Variant *r_meta) { Item *item = p_item; @@ -1423,18 +1453,28 @@ bool RichTextLabel::remove_line(const int p_line) { if (p_line >= current_frame->lines.size() || p_line < 0) return false; - int lines = p_line * 2; + int i = 0; + while (i < current->subitems.size() && current->subitems[i]->line < p_line) { + i++; + } - if (current->subitems[lines]->type != ITEM_NEWLINE) - _remove_item(current->subitems[lines], current->subitems[lines]->line, lines); + bool was_newline = false; + while (i < current->subitems.size()) { + was_newline = current->subitems[i]->type == ITEM_NEWLINE; + _remove_item(current->subitems[i], current->subitems[i]->line, p_line); + if (was_newline) + break; + } - _remove_item(current->subitems[lines], current->subitems[lines]->line, lines); + if (!was_newline) { + current_frame->lines.remove(p_line); + } - if (p_line == 0) { + if (p_line == 0 && current->subitems.size() > 0) main->lines.write[0].from = main; - } main->first_invalid_line = 0; + return true; } @@ -1447,6 +1487,7 @@ void RichTextLabel::push_font(const Ref<Font> &p_font) { item->font = p_font; _add_item(item, true); } + void RichTextLabel::push_color(const Color &p_color) { ERR_FAIL_COND(current->type == ITEM_TABLE); @@ -1455,6 +1496,7 @@ void RichTextLabel::push_color(const Color &p_color) { item->color = p_color; _add_item(item, true); } + void RichTextLabel::push_underline() { ERR_FAIL_COND(current->type == ITEM_TABLE); @@ -1463,6 +1505,14 @@ void RichTextLabel::push_underline() { _add_item(item, true); } +void RichTextLabel::push_strikethrough() { + + ERR_FAIL_COND(current->type == ITEM_TABLE); + ItemStrikethrough *item = memnew(ItemStrikethrough); + + _add_item(item, true); +} + void RichTextLabel::push_align(Align p_align) { ERR_FAIL_COND(current->type == ITEM_TABLE); @@ -1672,7 +1722,7 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { } if (brk_pos == p_bbcode.length()) - break; //nothing else o add + break; //nothing else to add int brk_end = p_bbcode.find("]", brk_pos + 1); @@ -1738,7 +1788,7 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { int columns = tag.substr(6, tag.length()).to_int(); if (columns < 1) columns = 1; - //use monospace font + push_table(columns); pos = brk_end + 1; tag_stack.push_front("table"); @@ -1752,7 +1802,7 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { int ratio = tag.substr(5, tag.length()).to_int(); if (ratio < 1) ratio = 1; - //use monospace font + set_table_column_expand(get_current_table_column(), true, ratio); push_cell(); pos = brk_end + 1; @@ -1765,43 +1815,37 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { tag_stack.push_front(tag); } else if (tag == "s") { - //use strikethrough (not supported underline instead) - push_underline(); + //use strikethrough + push_strikethrough(); pos = brk_end + 1; tag_stack.push_front(tag); } else if (tag == "center") { - //use underline push_align(ALIGN_CENTER); pos = brk_end + 1; tag_stack.push_front(tag); } else if (tag == "fill") { - //use underline push_align(ALIGN_FILL); pos = brk_end + 1; tag_stack.push_front(tag); } else if (tag == "right") { - //use underline push_align(ALIGN_RIGHT); pos = brk_end + 1; tag_stack.push_front(tag); } else if (tag == "ul") { - //use underline push_list(LIST_DOTS); pos = brk_end + 1; tag_stack.push_front(tag); } else if (tag == "ol") { - //use underline push_list(LIST_NUMBERS); pos = brk_end + 1; tag_stack.push_front(tag); } else if (tag == "indent") { - //use underline indent_level++; push_indent(indent_level); pos = brk_end + 1; @@ -1809,7 +1853,6 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { } else if (tag == "url") { - //use strikethrough (not supported underline instead) int end = p_bbcode.find("[", brk_end); if (end == -1) end = p_bbcode.length(); @@ -1827,7 +1870,6 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { tag_stack.push_front("url"); } else if (tag == "img") { - //use strikethrough (not supported underline instead) int end = p_bbcode.find("[", brk_end); if (end == -1) end = p_bbcode.length(); @@ -2134,6 +2176,7 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("push_list", "type"), &RichTextLabel::push_list); ClassDB::bind_method(D_METHOD("push_meta", "data"), &RichTextLabel::push_meta); ClassDB::bind_method(D_METHOD("push_underline"), &RichTextLabel::push_underline); + ClassDB::bind_method(D_METHOD("push_strikethrough"), &RichTextLabel::push_strikethrough); ClassDB::bind_method(D_METHOD("push_table", "columns"), &RichTextLabel::push_table); ClassDB::bind_method(D_METHOD("set_table_column_expand", "column", "expand", "ratio"), &RichTextLabel::set_table_column_expand); ClassDB::bind_method(D_METHOD("push_cell"), &RichTextLabel::push_cell); @@ -2194,7 +2237,7 @@ void RichTextLabel::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "meta_underlined"), "set_meta_underline", "is_meta_underlined"); ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_size", PROPERTY_HINT_RANGE, "0,24,1"), "set_tab_size", "get_tab_size"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_active"), "set_scroll_active", "is_scroll_active"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_following"), "set_scroll_follow", "is_scroll_following"); @@ -2222,6 +2265,7 @@ void RichTextLabel::_bind_methods() { BIND_ENUM_CONSTANT(ITEM_FONT); BIND_ENUM_CONSTANT(ITEM_COLOR); BIND_ENUM_CONSTANT(ITEM_UNDERLINE); + BIND_ENUM_CONSTANT(ITEM_STRIKETHROUGH); BIND_ENUM_CONSTANT(ITEM_ALIGN); BIND_ENUM_CONSTANT(ITEM_INDENT); BIND_ENUM_CONSTANT(ITEM_LIST); @@ -2285,7 +2329,7 @@ RichTextLabel::RichTextLabel() { vscroll = memnew(VScrollBar); add_child(vscroll); - vscroll->set_drag_slave(String("..")); + vscroll->set_drag_node(String("..")); vscroll->set_step(1); vscroll->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 0); vscroll->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, 0); diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 06e9b8efe3..c2e5712b9d 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -62,6 +62,7 @@ public: ITEM_FONT, ITEM_COLOR, ITEM_UNDERLINE, + ITEM_STRIKETHROUGH, ITEM_ALIGN, ITEM_INDENT, ITEM_LIST, @@ -164,6 +165,11 @@ private: ItemUnderline() { type = ITEM_UNDERLINE; } }; + struct ItemStrikethrough : public Item { + + ItemStrikethrough() { type = ITEM_STRIKETHROUGH; } + }; + struct ItemMeta : public Item { Variant meta; @@ -277,6 +283,7 @@ private: Align _find_align(Item *p_item); Color _find_color(Item *p_item, const Color &p_default_color); bool _find_underline(Item *p_item); + bool _find_strikethrough(Item *p_item); bool _find_meta(Item *p_item, Variant *r_meta); void _update_scroll(); @@ -307,6 +314,7 @@ public: void push_font(const Ref<Font> &p_font); void push_color(const Color &p_color); void push_underline(); + void push_strikethrough(); void push_align(Align p_align); void push_indent(int p_level); void push_list(ListType p_list); diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp index e5bd1c453d..07380f45cc 100644 --- a/scene/gui/scroll_bar.cpp +++ b/scene/gui/scroll_bar.cpp @@ -30,9 +30,9 @@ #include "scroll_bar.h" -#include "os/keyboard.h" -#include "os/os.h" -#include "print_string.h" +#include "core/os/keyboard.h" +#include "core/os/os.h" +#include "core/print_string.h" bool ScrollBar::focus_by_default = false; @@ -300,24 +300,24 @@ void ScrollBar::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { - if (has_node(drag_slave_path)) { - Node *n = get_node(drag_slave_path); - drag_slave = Object::cast_to<Control>(n); + if (has_node(drag_node_path)) { + Node *n = get_node(drag_node_path); + drag_node = Object::cast_to<Control>(n); } - if (drag_slave) { - drag_slave->connect("gui_input", this, "_drag_slave_input"); - drag_slave->connect("tree_exiting", this, "_drag_slave_exit", varray(), CONNECT_ONESHOT); + if (drag_node) { + drag_node->connect("gui_input", this, "_drag_node_input"); + drag_node->connect("tree_exiting", this, "_drag_node_exit", varray(), CONNECT_ONESHOT); } } if (p_what == NOTIFICATION_EXIT_TREE) { - if (drag_slave) { - drag_slave->disconnect("gui_input", this, "_drag_slave_input"); - drag_slave->disconnect("tree_exiting", this, "_drag_slave_exit"); + if (drag_node) { + drag_node->disconnect("gui_input", this, "_drag_node_input"); + drag_node->disconnect("tree_exiting", this, "_drag_node_exit"); } - drag_slave = NULL; + drag_node = NULL; } if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) { @@ -337,12 +337,13 @@ void ScrollBar::_notification(int p_what) { scrolling = false; set_physics_process_internal(false); } - } else if (drag_slave_touching) { - if (drag_slave_touching_deaccel) { + } else if (drag_node_touching) { + + if (drag_node_touching_deaccel) { Vector2 pos = Vector2(orientation == HORIZONTAL ? get_value() : 0, orientation == VERTICAL ? get_value() : 0); - pos += drag_slave_speed * get_physics_process_delta_time(); + pos += drag_node_speed * get_physics_process_delta_time(); bool turnoff = false; @@ -360,15 +361,15 @@ void ScrollBar::_notification(int p_what) { set_value(pos.x); - float sgn_x = drag_slave_speed.x < 0 ? -1 : 1; - float val_x = Math::abs(drag_slave_speed.x); + float sgn_x = drag_node_speed.x < 0 ? -1 : 1; + float val_x = Math::abs(drag_node_speed.x); val_x -= 1000 * get_physics_process_delta_time(); if (val_x < 0) { turnoff = true; } - drag_slave_speed.x = sgn_x * val_x; + drag_node_speed.x = sgn_x * val_x; } else { @@ -384,29 +385,29 @@ void ScrollBar::_notification(int p_what) { set_value(pos.y); - float sgn_y = drag_slave_speed.y < 0 ? -1 : 1; - float val_y = Math::abs(drag_slave_speed.y); + float sgn_y = drag_node_speed.y < 0 ? -1 : 1; + float val_y = Math::abs(drag_node_speed.y); val_y -= 1000 * get_physics_process_delta_time(); if (val_y < 0) { turnoff = true; } - drag_slave_speed.y = sgn_y * val_y; + drag_node_speed.y = sgn_y * val_y; } if (turnoff) { set_physics_process_internal(false); - drag_slave_touching = false; - drag_slave_touching_deaccel = false; + drag_node_touching = false; + drag_node_touching_deaccel = false; } } else { if (time_since_motion == 0 || time_since_motion > 0.1) { - Vector2 diff = drag_slave_accum - last_drag_slave_accum; - last_drag_slave_accum = drag_slave_accum; - drag_slave_speed = diff / get_physics_process_delta_time(); + Vector2 diff = drag_node_accum - last_drag_node_accum; + last_drag_node_accum = drag_node_accum; + drag_node_speed = diff / get_physics_process_delta_time(); } time_since_motion += get_physics_process_delta_time(); @@ -544,15 +545,15 @@ float ScrollBar::get_custom_step() const { return custom_step; } -void ScrollBar::_drag_slave_exit() { +void ScrollBar::_drag_node_exit() { - if (drag_slave) { - drag_slave->disconnect("gui_input", this, "_drag_slave_input"); + if (drag_node) { + drag_node->disconnect("gui_input", this, "_drag_node_input"); } - drag_slave = NULL; + drag_node = NULL; } -void ScrollBar::_drag_slave_input(const Ref<InputEvent> &p_input) { +void ScrollBar::_drag_node_input(const Ref<InputEvent> &p_input) { Ref<InputEventMouseButton> mb = p_input; @@ -563,43 +564,30 @@ void ScrollBar::_drag_slave_input(const Ref<InputEvent> &p_input) { if (mb->is_pressed()) { - if (drag_slave_touching) { - set_physics_process_internal(false); - drag_slave_touching_deaccel = false; - drag_slave_touching = false; - drag_slave_speed = Vector2(); - drag_slave_accum = Vector2(); - last_drag_slave_accum = Vector2(); - drag_slave_from = Vector2(); - } + drag_node_speed = Vector2(); + drag_node_accum = Vector2(); + last_drag_node_accum = Vector2(); + drag_node_from = Vector2(orientation == HORIZONTAL ? get_value() : 0, orientation == VERTICAL ? get_value() : 0); - if (true) { - drag_slave_speed = Vector2(); - drag_slave_accum = Vector2(); - last_drag_slave_accum = Vector2(); - //drag_slave_from=Vector2(h_scroll->get_val(),v_scroll->get_val()); - drag_slave_from = Vector2(orientation == HORIZONTAL ? get_value() : 0, orientation == VERTICAL ? get_value() : 0); + drag_node_touching = OS::get_singleton()->has_touchscreen_ui_hint(); + drag_node_touching_deaccel = false; + time_since_motion = 0; - drag_slave_touching = OS::get_singleton()->has_touchscreen_ui_hint(); - drag_slave_touching_deaccel = false; + if (drag_node_touching) { + set_physics_process_internal(true); time_since_motion = 0; - if (drag_slave_touching) { - set_physics_process_internal(true); - time_since_motion = 0; - } } } else { - if (drag_slave_touching) { + if (drag_node_touching) { - if (drag_slave_speed == Vector2()) { - drag_slave_touching_deaccel = false; - drag_slave_touching = false; + if (drag_node_speed == Vector2()) { + drag_node_touching_deaccel = false; + drag_node_touching = false; set_physics_process_internal(false); } else { - - drag_slave_touching_deaccel = true; + drag_node_touching_deaccel = true; } } } @@ -609,60 +597,54 @@ void ScrollBar::_drag_slave_input(const Ref<InputEvent> &p_input) { if (mm.is_valid()) { - if (drag_slave_touching && !drag_slave_touching_deaccel) { + if (drag_node_touching && !drag_node_touching_deaccel) { Vector2 motion = Vector2(mm->get_relative().x, mm->get_relative().y); - drag_slave_accum -= motion; - Vector2 diff = drag_slave_from + drag_slave_accum; + drag_node_accum -= motion; + Vector2 diff = drag_node_from + drag_node_accum; if (orientation == HORIZONTAL) set_value(diff.x); - /* - else - drag_slave_accum.x=0; - */ + if (orientation == VERTICAL) set_value(diff.y); - /* - else - drag_slave_accum.y=0; - */ + time_since_motion = 0; } } } -void ScrollBar::set_drag_slave(const NodePath &p_path) { +void ScrollBar::set_drag_node(const NodePath &p_path) { if (is_inside_tree()) { - if (drag_slave) { - drag_slave->disconnect("gui_input", this, "_drag_slave_input"); - drag_slave->disconnect("tree_exiting", this, "_drag_slave_exit"); + if (drag_node) { + drag_node->disconnect("gui_input", this, "_drag_node_input"); + drag_node->disconnect("tree_exiting", this, "_drag_node_exit"); } } - drag_slave = NULL; - drag_slave_path = p_path; + drag_node = NULL; + drag_node_path = p_path; if (is_inside_tree()) { if (has_node(p_path)) { Node *n = get_node(p_path); - drag_slave = Object::cast_to<Control>(n); + drag_node = Object::cast_to<Control>(n); } - if (drag_slave) { - drag_slave->connect("gui_input", this, "_drag_slave_input"); - drag_slave->connect("tree_exiting", this, "_drag_slave_exit", varray(), CONNECT_ONESHOT); + if (drag_node) { + drag_node->connect("gui_input", this, "_drag_node_input"); + drag_node->connect("tree_exiting", this, "_drag_node_exit", varray(), CONNECT_ONESHOT); } } } -NodePath ScrollBar::get_drag_slave() const { +NodePath ScrollBar::get_drag_node() const { - return drag_slave_path; + return drag_node_path; } void ScrollBar::set_smooth_scroll_enabled(bool p_enable) { @@ -678,8 +660,8 @@ void ScrollBar::_bind_methods() { ClassDB::bind_method(D_METHOD("_gui_input"), &ScrollBar::_gui_input); ClassDB::bind_method(D_METHOD("set_custom_step", "step"), &ScrollBar::set_custom_step); ClassDB::bind_method(D_METHOD("get_custom_step"), &ScrollBar::get_custom_step); - ClassDB::bind_method(D_METHOD("_drag_slave_input"), &ScrollBar::_drag_slave_input); - ClassDB::bind_method(D_METHOD("_drag_slave_exit"), &ScrollBar::_drag_slave_exit); + ClassDB::bind_method(D_METHOD("_drag_node_input"), &ScrollBar::_drag_node_input); + ClassDB::bind_method(D_METHOD("_drag_node_exit"), &ScrollBar::_drag_node_exit); ADD_SIGNAL(MethodInfo("scrolling")); @@ -691,13 +673,13 @@ ScrollBar::ScrollBar(Orientation p_orientation) { orientation = p_orientation; highlight = HIGHLIGHT_NONE; custom_step = -1; - drag_slave = NULL; + drag_node = NULL; drag.active = false; - drag_slave_speed = Vector2(); - drag_slave_touching = false; - drag_slave_touching_deaccel = false; + drag_node_speed = Vector2(); + drag_node_touching = false; + drag_node_touching_deaccel = false; scrolling = false; target_scroll = 0; diff --git a/scene/gui/scroll_bar.h b/scene/gui/scroll_bar.h index 15e037f8bb..cde4120cdb 100644 --- a/scene/gui/scroll_bar.h +++ b/scene/gui/scroll_bar.h @@ -36,6 +36,7 @@ /** @author Juan Linietsky <reduzio@gmail.com> */ + class ScrollBar : public Range { GDCLASS(ScrollBar, Range); @@ -71,25 +72,25 @@ class ScrollBar : public Range { static void set_can_focus_by_default(bool p_can_focus); - Node *drag_slave; - NodePath drag_slave_path; + Node *drag_node; + NodePath drag_node_path; - Vector2 drag_slave_speed; - Vector2 drag_slave_accum; - Vector2 drag_slave_from; - Vector2 last_drag_slave_accum; - float last_drag_slave_time; + Vector2 drag_node_speed; + Vector2 drag_node_accum; + Vector2 drag_node_from; + Vector2 last_drag_node_accum; + float last_drag_node_time; float time_since_motion; - bool drag_slave_touching; - bool drag_slave_touching_deaccel; + bool drag_node_touching; + bool drag_node_touching_deaccel; bool click_handled; bool scrolling; double target_scroll; bool smooth_scroll_enabled; - void _drag_slave_exit(); - void _drag_slave_input(const Ref<InputEvent> &p_input); + void _drag_node_exit(); + void _drag_node_input(const Ref<InputEvent> &p_input); void _gui_input(Ref<InputEvent> p_event); @@ -102,8 +103,8 @@ public: void set_custom_step(float p_custom_step); float get_custom_step() const; - void set_drag_slave(const NodePath &p_path); - NodePath get_drag_slave() const; + void set_drag_node(const NodePath &p_path); + NodePath get_drag_node() const; void set_smooth_scroll_enabled(bool p_enable); bool is_smooth_scroll_enabled() const; diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index 495d618930..26da16569a 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -29,7 +29,8 @@ /*************************************************************************/ #include "scroll_container.h" -#include "os/os.h" +#include "core/os/os.h" + bool ScrollContainer::clips_input() const { return true; @@ -170,7 +171,7 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) { Vector2 motion = Vector2(mm->get_relative().x, mm->get_relative().y); drag_accum -= motion; - if (beyond_deadzone || scroll_h && Math::abs(drag_accum.x) > deadzone || scroll_v && Math::abs(drag_accum.y) > deadzone) { + if (beyond_deadzone || (scroll_h && Math::abs(drag_accum.x) > deadzone) || (scroll_v && Math::abs(drag_accum.y) > deadzone)) { if (!beyond_deadzone) { propagate_notification(NOTIFICATION_SCROLL_BEGIN); emit_signal("scroll_started"); diff --git a/scene/gui/shortcut.cpp b/scene/gui/shortcut.cpp index 36490cf254..6fcf96f611 100644 --- a/scene/gui/shortcut.cpp +++ b/scene/gui/shortcut.cpp @@ -30,7 +30,7 @@ #include "shortcut.h" -#include "os/keyboard.h" +#include "core/os/keyboard.h" void ShortCut::set_shortcut(const Ref<InputEvent> &p_shortcut) { diff --git a/scene/gui/shortcut.h b/scene/gui/shortcut.h index f9240642bf..7613a24e43 100644 --- a/scene/gui/shortcut.h +++ b/scene/gui/shortcut.h @@ -31,8 +31,8 @@ #ifndef SHORTCUT_H #define SHORTCUT_H -#include "os/input_event.h" -#include "resource.h" +#include "core/os/input_event.h" +#include "core/resource.h" class ShortCut : public Resource { diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp index b820e2eafd..147c0518ec 100644 --- a/scene/gui/slider.cpp +++ b/scene/gui/slider.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "slider.h" -#include "os/keyboard.h" +#include "core/os/keyboard.h" Size2 Slider::get_minimum_size() const { diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp index 145981d498..f766c0722d 100644 --- a/scene/gui/spin_box.cpp +++ b/scene/gui/spin_box.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "spin_box.h" -#include "os/input.h" +#include "core/os/input.h" Size2 SpinBox::get_minimum_size() const { @@ -217,6 +217,16 @@ void SpinBox::_notification(int p_what) { } } +void SpinBox::set_align(LineEdit::Align p_align) { + + line_edit->set_align(p_align); +} + +LineEdit::Align SpinBox::get_align() const { + + return line_edit->get_align(); +} + void SpinBox::set_suffix(const String &p_suffix) { suffix = p_suffix; @@ -253,6 +263,8 @@ void SpinBox::_bind_methods() { //ClassDB::bind_method(D_METHOD("_value_changed"),&SpinBox::_value_changed); ClassDB::bind_method(D_METHOD("_gui_input"), &SpinBox::_gui_input); ClassDB::bind_method(D_METHOD("_text_entered"), &SpinBox::_text_entered); + ClassDB::bind_method(D_METHOD("set_align", "align"), &SpinBox::set_align); + ClassDB::bind_method(D_METHOD("get_align"), &SpinBox::get_align); ClassDB::bind_method(D_METHOD("set_suffix", "suffix"), &SpinBox::set_suffix); ClassDB::bind_method(D_METHOD("get_suffix"), &SpinBox::get_suffix); ClassDB::bind_method(D_METHOD("set_prefix", "prefix"), &SpinBox::set_prefix); @@ -264,6 +276,7 @@ void SpinBox::_bind_methods() { ClassDB::bind_method(D_METHOD("_line_edit_input"), &SpinBox::_line_edit_input); ClassDB::bind_method(D_METHOD("_range_click_timeout"), &SpinBox::_range_click_timeout); + ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_align", "get_align"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "prefix"), "set_prefix", "get_prefix"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "suffix"), "set_suffix", "get_suffix"); diff --git a/scene/gui/spin_box.h b/scene/gui/spin_box.h index 8863f44bef..f1ee26d9f3 100644 --- a/scene/gui/spin_box.h +++ b/scene/gui/spin_box.h @@ -76,6 +76,9 @@ public: virtual Size2 get_minimum_size() const; + void set_align(LineEdit::Align p_align); + LineEdit::Align get_align() const; + void set_editable(bool p_editable); bool is_editable() const; diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index c30fa96327..4c354768fe 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -30,7 +30,7 @@ #include "tab_container.h" -#include "message_queue.h" +#include "core/message_queue.h" #include "scene/gui/box_container.h" #include "scene/gui/label.h" #include "scene/gui/texture_rect.h" diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp index 2075f7ce70..b7f8c74046 100644 --- a/scene/gui/tabs.cpp +++ b/scene/gui/tabs.cpp @@ -30,7 +30,7 @@ #include "tabs.h" -#include "message_queue.h" +#include "core/message_queue.h" #include "scene/gui/box_container.h" #include "scene/gui/label.h" #include "scene/gui/texture_rect.h" @@ -106,41 +106,8 @@ void Tabs::_gui_input(const Ref<InputEvent> &p_event) { } } - // test hovering to display right or close button - int hover_now = -1; - int hover_buttons = -1; - for (int i = 0; i < tabs.size(); i++) { - - if (i < offset) - continue; - - Rect2 rect = get_tab_rect(i); - if (rect.has_point(pos)) { - hover_now = i; - } - if (tabs[i].rb_rect.has_point(pos)) { - rb_hover = i; - cb_hover = -1; - hover_buttons = i; - break; - } else if (!tabs[i].disabled && tabs[i].cb_rect.has_point(pos)) { - cb_hover = i; - rb_hover = -1; - hover_buttons = i; - break; - } - } - if (hover != hover_now) { - hover = hover_now; - emit_signal("tab_hover", hover); - } - - if (hover_buttons == -1) { // no hover - rb_hover = hover_buttons; - cb_hover = hover_buttons; - } + _update_hover(); update(); - return; } @@ -522,6 +489,48 @@ Ref<Texture> Tabs::get_tab_right_button(int p_tab) const { return tabs[p_tab].right_button; } +void Tabs::_update_hover() { + + if (!is_inside_tree()) { + return; + } + + const Point2 &pos = get_local_mouse_position(); + // test hovering to display right or close button + int hover_now = -1; + int hover_buttons = -1; + for (int i = 0; i < tabs.size(); i++) { + + if (i < offset) + continue; + + Rect2 rect = get_tab_rect(i); + if (rect.has_point(pos)) { + hover_now = i; + } + if (tabs[i].rb_rect.has_point(pos)) { + rb_hover = i; + cb_hover = -1; + hover_buttons = i; + break; + } else if (!tabs[i].disabled && tabs[i].cb_rect.has_point(pos)) { + cb_hover = i; + rb_hover = -1; + hover_buttons = i; + break; + } + } + if (hover != hover_now) { + hover = hover_now; + emit_signal("tab_hover", hover); + } + + if (hover_buttons == -1) { // no hover + rb_hover = hover_buttons; + cb_hover = hover_buttons; + } +} + void Tabs::_update_cache() { Ref<StyleBox> tab_disabled = get_stylebox("tab_disabled"); Ref<StyleBox> tab_bg = get_stylebox("tab_bg"); @@ -597,6 +606,7 @@ void Tabs::add_tab(const String &p_str, const Ref<Texture> &p_icon) { tabs.push_back(t); _update_cache(); + call_deferred("_update_hover"); update(); minimum_size_changed(); } @@ -604,6 +614,7 @@ void Tabs::add_tab(const String &p_str, const Ref<Texture> &p_icon) { void Tabs::clear_tabs() { tabs.clear(); current = 0; + call_deferred("_update_hover"); update(); } @@ -614,6 +625,7 @@ void Tabs::remove_tab(int p_idx) { if (current >= p_idx) current--; _update_cache(); + call_deferred("_update_hover"); update(); minimum_size_changed(); @@ -931,6 +943,7 @@ bool Tabs::get_select_with_rmb() const { void Tabs::_bind_methods() { ClassDB::bind_method(D_METHOD("_gui_input"), &Tabs::_gui_input); + ClassDB::bind_method(D_METHOD("_update_hover"), &Tabs::_update_hover); ClassDB::bind_method(D_METHOD("get_tab_count"), &Tabs::get_tab_count); ClassDB::bind_method(D_METHOD("set_current_tab", "tab_idx"), &Tabs::set_current_tab); ClassDB::bind_method(D_METHOD("get_current_tab"), &Tabs::get_current_tab); diff --git a/scene/gui/tabs.h b/scene/gui/tabs.h index e204f4364b..a98744b804 100644 --- a/scene/gui/tabs.h +++ b/scene/gui/tabs.h @@ -97,6 +97,8 @@ private: int get_tab_width(int p_idx) const; void _ensure_no_over_offset(); + + void _update_hover(); void _update_cache(); protected: diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 9616caa811..d996132960 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -30,11 +30,11 @@ #include "text_edit.h" -#include "message_queue.h" -#include "os/input.h" -#include "os/keyboard.h" -#include "os/os.h" -#include "project_settings.h" +#include "core/message_queue.h" +#include "core/os/input.h" +#include "core/os/keyboard.h" +#include "core/os/os.h" +#include "core/project_settings.h" #include "scene/main/viewport.h" #ifdef TOOLS_ENABLED @@ -334,15 +334,12 @@ void TextEdit::_update_scrollbars() { h_scroll->set_begin(Point2(0, size.height - hmin.height)); h_scroll->set_end(Point2(size.width - vmin.width, size.height)); - int hscroll_rows = ((hmin.height - 1) / get_row_height()) + 1; int visible_rows = get_visible_rows(); - int total_rows = get_total_visible_rows(); if (scroll_past_end_of_file_enabled) { total_rows += visible_rows - 1; } - int vscroll_pixels = v_scroll->get_combined_minimum_size().width; int visible_width = size.width - cache.style_normal->get_minimum_size().width; int total_width = text.get_max_width(true) + vmin.x; @@ -367,12 +364,12 @@ void TextEdit::_update_scrollbars() { } else { - if (total_rows > visible_rows && total_width <= visible_width - vscroll_pixels) { + if (total_rows > visible_rows && total_width <= visible_width) { //thanks yessopie for this clever bit of logic use_hscroll = false; } - if (total_rows <= visible_rows - hscroll_rows && total_width > visible_width) { + if (total_rows <= visible_rows && total_width > visible_width) { //thanks yessopie for this clever bit of logic use_vscroll = false; } @@ -556,7 +553,6 @@ void TextEdit::_notification(int p_what) { } break; case NOTIFICATION_RESIZED: { - cache.size = get_size(); _update_scrollbars(); update_wrap_at(); } break; @@ -593,6 +589,7 @@ void TextEdit::_notification(int p_what) { } } break; case NOTIFICATION_DRAW: { + Size2 size = get_size(); if ((!has_focus() && !menu->has_focus()) || !window_has_focus) { draw_caret = false; } @@ -634,24 +631,22 @@ void TextEdit::_notification(int p_what) { RID ci = get_canvas_item(); VisualServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), true); int xmargin_beg = cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width; - int xmargin_end = cache.size.width - cache.style_normal->get_margin(MARGIN_RIGHT); + int xmargin_end = size.width - cache.style_normal->get_margin(MARGIN_RIGHT); //let's do it easy for now: - cache.style_normal->draw(ci, Rect2(Point2(), cache.size)); + cache.style_normal->draw(ci, Rect2(Point2(), size)); float readonly_alpha = 1.0; // used to set the input text color when in read-only mode if (readonly) { - cache.style_readonly->draw(ci, Rect2(Point2(), cache.size)); + cache.style_readonly->draw(ci, Rect2(Point2(), size)); readonly_alpha = .5; draw_caret = false; } if (has_focus()) - cache.style_focus->draw(ci, Rect2(Point2(), cache.size)); + cache.style_focus->draw(ci, Rect2(Point2(), size)); int ascent = cache.font->get_ascent(); int visible_rows = get_visible_rows() + 1; - int tab_w = cache.font->get_char_size(' ').width * indent_size; - Color color = cache.font_color; color.a *= readonly_alpha; @@ -1261,7 +1256,7 @@ void TextEdit::_notification(int p_what) { if (line_length_guideline) { int x = xmargin_beg + cache.font->get_char_size('0').width * line_length_guideline_col - cursor.x_ofs; if (x > xmargin_beg && x < xmargin_end) { - VisualServer::get_singleton()->canvas_item_add_line(ci, Point2(x, 0), Point2(x, cache.size.height), cache.line_length_guideline_color); + VisualServer::get_singleton()->canvas_item_add_line(ci, Point2(x, 0), Point2(x, size.height), cache.line_length_guideline_color); } } @@ -1434,9 +1429,6 @@ void TextEdit::_notification(int p_what) { if (OS::get_singleton()->has_virtual_keyboard()) OS::get_singleton()->show_virtual_keyboard(get_text(), get_global_rect()); - if (raised_from_completion) { - VisualServer::get_singleton()->canvas_item_set_z_index(get_canvas_item(), 1); - } } break; case NOTIFICATION_FOCUS_EXIT: { @@ -1448,9 +1440,6 @@ void TextEdit::_notification(int p_what) { if (OS::get_singleton()->has_virtual_keyboard()) OS::get_singleton()->hide_virtual_keyboard(); - if (raised_from_completion) { - VisualServer::get_singleton()->canvas_item_set_z_index(get_canvas_item(), 0); - } } break; } } @@ -1942,7 +1931,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { int to_column = 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 + // Right click is outside the selected text deselect(); } } @@ -2336,9 +2325,9 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { // no need to indent if we are going upwards. if (auto_indent && !(k->get_command() && k->get_shift())) { - // indent once again if previous line will end with ':' or '{' + // indent once again if previous line will end with ':' or '{' and the line is not a comment // (i.e. colon/brace precedes current cursor position) - if (cursor.column > 0 && (text[cursor.line][cursor.column - 1] == ':' || text[cursor.line][cursor.column - 1] == '{')) { + if (cursor.column > 0 && (text[cursor.line][cursor.column - 1] == ':' || text[cursor.line][cursor.column - 1] == '{') && !is_line_comment(cursor.line)) { if (indent_using_spaces) { ins += space_indent; } else { @@ -3119,16 +3108,14 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { if (selection.active) { int ini = selection.from_line; int end = selection.to_line; + for (int i = ini; i <= end; i++) { - if (get_line(i).begins_with("#")) - _remove_text(i, 0, i, 1); + _uncomment_line(i); } } else { - if (get_line(cursor.line).begins_with("#")) { - _remove_text(cursor.line, 0, cursor.line, 1); - if (cursor.column >= get_line(cursor.line).length()) { - cursor.column = MAX(0, get_line(cursor.line).length() - 1); - } + _uncomment_line(cursor.line); + if (cursor.column >= get_line(cursor.line).length()) { + cursor.column = MAX(0, get_line(cursor.line).length() - 1); } } update(); @@ -3208,6 +3195,24 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } } +void TextEdit::_uncomment_line(int p_line) { + String line_text = get_line(p_line); + for (int i = 0; i < line_text.length(); i++) { + if (line_text[i] == '#') { + _remove_text(p_line, i, p_line, i + 1); + if (p_line == selection.to_line && selection.to_column > line_text.length() - 1) { + selection.to_column -= 1; + if (selection.to_column >= selection.from_column) { + selection.active = false; + } + } + return; + } else if (line_text[i] != '\t' && line_text[i] != ' ') { + return; + } + } +} + void TextEdit::_scroll_up(real_t p_delta) { if (scrolling && smooth_scroll_enabled && SGN(target_v_scroll - v_scroll->get_value()) != SGN(-p_delta)) @@ -3246,7 +3251,7 @@ void TextEdit::_scroll_down(real_t p_delta) { } if (smooth_scroll_enabled) { - int max_v_scroll = v_scroll->get_max() - v_scroll->get_page(); + int max_v_scroll = round(v_scroll->get_max() - v_scroll->get_page()); if (target_v_scroll > max_v_scroll) { target_v_scroll = max_v_scroll; v_scroll->set_value(target_v_scroll); @@ -3609,7 +3614,7 @@ Size2 TextEdit::get_minimum_size() const { int TextEdit::get_visible_rows() const { - int total = cache.size.height; + int total = get_size().height; total -= cache.style_normal->get_minimum_size().height; if (h_scroll->is_visible_in_tree()) total -= h_scroll->get_size().height; @@ -3636,7 +3641,7 @@ int TextEdit::get_total_visible_rows() const { void TextEdit::update_wrap_at() { - wrap_at = cache.size.width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width - wrap_right_offset; + wrap_at = get_size().width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width - wrap_right_offset; update_cursor_wrap_offset(); text.clear_wrap_cache(); @@ -3670,7 +3675,7 @@ void TextEdit::adjust_viewport_to_cursor() { set_line_as_last_visible(cur_line, cur_wrap); } - int visible_width = cache.size.width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width; + int visible_width = get_size().width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width; if (v_scroll->is_visible_in_tree()) visible_width -= v_scroll->get_combined_minimum_size().width; visible_width -= 20; // give it a little more space @@ -3701,7 +3706,7 @@ void TextEdit::center_viewport_to_cursor() { unfold_line(cursor.line); set_line_as_center_visible(cursor.line, get_cursor_wrap_index()); - int visible_width = cache.size.width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width; + int visible_width = get_size().width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width; if (v_scroll->is_visible_in_tree()) visible_width -= v_scroll->get_combined_minimum_size().width; visible_width -= 20; // give it a little more space @@ -3780,7 +3785,7 @@ Vector<String> TextEdit::get_wrap_rows_text(int p_line) const { int tab_offset_px = get_indent_level(p_line) * cache.font->get_char_size(' ').width; while (col < line_text.length()) { - char c = line_text[col]; + CharType c = line_text[col]; int w = text.get_char_width(c, line_text[col + 1], px + word_px); int indent_ofs = (cur_wrap_index != 0 ? tab_offset_px : 0); @@ -4158,7 +4163,7 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const { } } - return CURSOR_IBEAM; + return get_default_cursor_shape(); } void TextEdit::set_text(String p_text) { @@ -4386,7 +4391,7 @@ int TextEdit::_is_line_in_region(int p_line) { // if not find the closest line we have int previous_line = p_line - 1; - for (previous_line; previous_line > -1; previous_line--) { + for (; previous_line > -1; previous_line--) { if (color_region_cache.has(p_line)) { break; } @@ -4531,9 +4536,13 @@ void TextEdit::cut() { void TextEdit::copy() { if (!selection.active) { - String clipboard = _base_get_text(cursor.line, 0, cursor.line, text[cursor.line].length()); - OS::get_singleton()->set_clipboard(clipboard); - cut_copy_line = clipboard; + + if (text[cursor.line].length() != 0) { + + String clipboard = _base_get_text(cursor.line, 0, cursor.line, text[cursor.line].length()); + OS::get_singleton()->set_clipboard(clipboard); + cut_copy_line = clipboard; + } } else { String clipboard = _base_get_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column); OS::get_singleton()->set_clipboard(clipboard); @@ -4822,28 +4831,27 @@ bool TextEdit::search(const String &p_key, uint32_t p_search_flags, int p_from_l pos = -1; - int pos_from = 0; + int pos_from = (p_search_flags & SEARCH_BACKWARDS) ? text_line.length() : 0; int last_pos = -1; while (true) { - while ((last_pos = (p_search_flags & SEARCH_MATCH_CASE) ? text_line.find(p_key, pos_from) : text_line.findn(p_key, pos_from)) != -1) { - - if (p_search_flags & SEARCH_BACKWARDS) { - - if (last_pos > from_column) + if (p_search_flags & SEARCH_BACKWARDS) { + while ((last_pos = (p_search_flags & SEARCH_MATCH_CASE) ? text_line.rfind(p_key, pos_from) : text_line.rfindn(p_key, pos_from)) != -1) { + if (last_pos <= from_column) { + pos = last_pos; break; - pos = last_pos; - - } else { - + } + pos_from = last_pos - p_key.length(); + } + } else { + while ((last_pos = (p_search_flags & SEARCH_MATCH_CASE) ? text_line.find(p_key, pos_from) : text_line.findn(p_key, pos_from)) != -1) { if (last_pos >= from_column) { pos = last_pos; break; } + pos_from = last_pos + p_key.length(); } - - pos_from = last_pos + p_key.length(); } bool is_match = true; @@ -4856,11 +4864,15 @@ bool TextEdit::search(const String &p_key, uint32_t p_search_flags, int p_from_l is_match = false; } + if (pos_from == -1) { + pos = -1; + } + if (is_match || last_pos == -1 || pos == -1) { break; } - pos_from = pos + 1; + pos_from = (p_search_flags & SEARCH_BACKWARDS) ? pos - 1 : pos + 1; pos = -1; } @@ -5133,7 +5145,7 @@ bool TextEdit::can_fold(int p_line) const { return false; if (p_line + 1 >= text.size()) return false; - if (text[p_line].size() == 0) + if (text[p_line].strip_edges().size() == 0) return false; if (is_folded(p_line)) return false; @@ -5145,7 +5157,7 @@ bool TextEdit::can_fold(int p_line) const { int start_indent = get_indent_level(p_line); for (int i = p_line + 1; i < text.size(); i++) { - if (text[i].size() == 0) + if (text[i].strip_edges().size() == 0) continue; int next_indent = get_indent_level(i); if (is_line_comment(i)) { @@ -5543,7 +5555,7 @@ int TextEdit::get_last_visible_line_wrap_index() const { double TextEdit::get_visible_rows_offset() const { - double total = cache.size.height; + double total = get_size().height; total -= cache.style_normal->get_minimum_size().height; if (h_scroll->is_visible_in_tree()) total -= h_scroll->get_size().height; @@ -5640,20 +5652,20 @@ void TextEdit::_confirm_completion() { end_complex_operation(); _cancel_completion(); + + if (last_completion_char == '(') { + query_code_comple(); + } } void TextEdit::_cancel_code_hint() { - VisualServer::get_singleton()->canvas_item_set_z_index(get_canvas_item(), 0); - raised_from_completion = false; completion_hint = ""; update(); } void TextEdit::_cancel_completion() { - VisualServer::get_singleton()->canvas_item_set_z_index(get_canvas_item(), 0); - raised_from_completion = false; if (!completion_active) return; @@ -5751,43 +5763,23 @@ void TextEdit::_update_completion_candidates() { completion_strings.write[i] = completion_strings[i].unquote().quote("'"); } - if (s == completion_strings[i]) { - // A perfect match, stop completion - _cancel_completion(); - return; + if (completion_strings[i].begins_with(s)) { + completion_options.push_back(completion_strings[i]); } + } - if (s.is_subsequence_ofi(completion_strings[i])) { - // don't remove duplicates if no input is provided - if (s != "" && completion_options.find(completion_strings[i]) != -1) { - continue; - } - // Calculate the similarity to keep completions in good order - float similarity; - if (completion_strings[i].begins_with(s)) { - // Substrings (same case) are the best candidates - similarity = 1.2; - } else if (completion_strings[i].to_lower().begins_with(s.to_lower())) { - // then any substrings - similarity = 1.1; - } else { - // Otherwise compute the similarity - similarity = s.to_lower().similarity(completion_strings[i].to_lower()); + if (completion_options.size() == 0) { + for (int i = 0; i < completion_strings.size(); i++) { + if (s.is_subsequence_of(completion_strings[i])) { + completion_options.push_back(completion_strings[i]); } + } + } - int comp_size = completion_options.size(); - if (comp_size == 0) { + if (completion_options.size() == 0) { + for (int i = 0; i < completion_strings.size(); i++) { + if (s.is_subsequence_ofi(completion_strings[i])) { completion_options.push_back(completion_strings[i]); - sim_cache.push_back(similarity); - } else { - float comp_sim; - int pos = 0; - do { - comp_sim = sim_cache[pos++]; - } while (pos < comp_size && similarity < comp_sim); - pos = similarity > comp_sim ? pos - 1 : pos; // Pos will be off by one - completion_options.insert(pos, completion_strings[i]); - sim_cache.insert(pos, similarity); } } } @@ -5795,7 +5787,12 @@ void TextEdit::_update_completion_candidates() { if (completion_options.size() == 0) { //no options to complete, cancel _cancel_completion(); + return; + } + if (completion_options.size() == 1 && s == completion_options[0]) { + // A perfect match, stop completion + _cancel_completion(); return; } @@ -5826,8 +5823,6 @@ void TextEdit::query_code_comple() { void TextEdit::set_code_hint(const String &p_hint) { - VisualServer::get_singleton()->canvas_item_set_z_index(get_canvas_item(), 1); - raised_from_completion = true; completion_hint = p_hint; completion_hint_offset = -0xFFFF; update(); @@ -5835,8 +5830,6 @@ void TextEdit::set_code_hint(const String &p_hint) { void TextEdit::code_complete(const Vector<String> &p_strings, bool p_forced) { - VisualServer::get_singleton()->canvas_item_set_z_index(get_canvas_item(), 1); - raised_from_completion = true; completion_strings = p_strings; completion_active = true; completion_forced = p_forced; @@ -5858,7 +5851,7 @@ String TextEdit::get_word_at_pos(const Vector2 &p_pos) const { if (select_word(s, col, beg, end)) { bool inside_quotes = false; - char selected_quote = '\0'; + CharType selected_quote = '\0'; int qbegin = 0, qend = 0; for (int i = 0; i < s.length(); i++) { if (s[i] == '"' || s[i] == '\'') { @@ -5921,6 +5914,9 @@ void TextEdit::set_line(int line, String new_text) { if (cursor.line == line) { cursor.column = MIN(cursor.column, new_text.length()); } + if (is_selection_active() && line == selection.to_line && selection.to_column > text[line].length()) { + selection.to_column = text[line].length(); + } } void TextEdit::insert_at(const String &p_text, int at) { @@ -6222,6 +6218,7 @@ void TextEdit::_bind_methods() { BIND_ENUM_CONSTANT(MENU_MAX); GLOBAL_DEF("gui/timers/text_edit_idle_detect_sec", 3); + ProjectSettings::get_singleton()->set_custom_property_info("gui/timers/text_edit_idle_detect_sec", PropertyInfo(Variant::REAL, "gui/timers/text_edit_idle_detect_sec", PROPERTY_HINT_RANGE, "0,10,0.01,or_greater")); // No negative numbers } TextEdit::TextEdit() { @@ -6238,7 +6235,6 @@ TextEdit::TextEdit() { set_focus_mode(FOCUS_ALL); syntax_highlighter = NULL; _update_caches(); - cache.size = Size2(1, 1); cache.row_height = 1; cache.line_spacing = 1; cache.line_number_w = 1; @@ -6246,6 +6242,7 @@ TextEdit::TextEdit() { breakpoint_gutter_width = 0; cache.fold_gutter_width = 0; fold_gutter_width = 0; + set_default_cursor_shape(CURSOR_IBEAM); indent_size = 4; text.set_indent_size(indent_size); @@ -6335,8 +6332,6 @@ TextEdit::TextEdit() { target_v_scroll = 0; v_scroll_speed = 80; - raised_from_completion = false; - context_menu_enabled = true; menu = memnew(PopupMenu); add_child(menu); @@ -6411,8 +6406,8 @@ Map<int, TextEdit::HighlighterInfo> TextEdit::_get_line_syntax_highlighting(int is_hex_notation = false; } - // check for dot or underscore or 'x' for hex notation in floating point number - if ((str[j] == '.' || str[j] == 'x' || str[j] == '_' || str[j] == 'f') && !in_word && prev_is_number && !is_number) { + // check for dot or underscore or 'x' for hex notation in floating point number or 'e' for scientific notation + if ((str[j] == '.' || str[j] == 'x' || str[j] == '_' || str[j] == 'f' || str[j] == 'e') && !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 19b5d574c6..8a508a8738 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -193,7 +193,6 @@ private: int line_number_w; int breakpoint_gutter_width; int fold_gutter_width; - Size2 size; } cache; Map<int, int> color_region_cache; @@ -307,8 +306,6 @@ private: float target_v_scroll; float v_scroll_speed; - bool raised_from_completion; - String highlighted_word; uint64_t last_dblclk; @@ -373,6 +370,7 @@ private: void _update_selection_mode_word(); void _update_selection_mode_line(); + void _uncomment_line(int p_line); void _scroll_up(real_t p_delta); void _scroll_down(real_t p_delta); diff --git a/scene/gui/texture_button.cpp b/scene/gui/texture_button.cpp index 6bd3b26280..1940bbfa98 100644 --- a/scene/gui/texture_button.cpp +++ b/scene/gui/texture_button.cpp @@ -88,6 +88,9 @@ bool TextureButton::has_point(const Point2 &p_point) const { scale.y = min; ofs -= _texture_region.position / min; } break; + default: { + // FIXME: Why a switch if we only handle one enum value? + } } // offset and scale the new point position to adjust it to the bitmask size @@ -125,6 +128,7 @@ void TextureButton::_notification(int p_what) { if (normal.is_valid()) texdraw = normal; } break; + case DRAW_HOVER_PRESSED: case DRAW_PRESSED: { if (pressed.is_null()) { diff --git a/scene/gui/texture_progress.cpp b/scene/gui/texture_progress.cpp index ff90576c1b..75f88dc4e6 100644 --- a/scene/gui/texture_progress.cpp +++ b/scene/gui/texture_progress.cpp @@ -30,7 +30,7 @@ #include "texture_progress.h" -#include "engine.h" +#include "core/engine.h" void TextureProgress::set_under_texture(const Ref<Texture> &p_texture) { @@ -59,14 +59,14 @@ Ref<Texture> TextureProgress::get_over_texture() const { } void TextureProgress::set_stretch_margin(Margin p_margin, int p_size) { - ERR_FAIL_INDEX(p_margin, 4); + ERR_FAIL_INDEX((int)p_margin, 4); stretch_margin[p_margin] = p_size; update(); minimum_size_changed(); } int TextureProgress::get_stretch_margin(Margin p_margin) const { - ERR_FAIL_INDEX_V(p_margin, 4, 0); + ERR_FAIL_INDEX_V((int)p_margin, 4, 0); return stretch_margin[p_margin]; } @@ -148,9 +148,9 @@ Point2 TextureProgress::unit_val_to_uv(float val) { float angle = (val * Math_TAU) - Math_PI * 0.5; Point2 dir = Vector2(Math::cos(angle), Math::sin(angle)); float t1 = 1.0; - float cp; - float cq; - float cr; + float cp = 0; + float cq = 0; + float cr = 0; float edgeLeft = 0.0; float edgeRight = 1.0; float edgeBottom = 0.0; @@ -229,6 +229,17 @@ void TextureProgress::draw_nine_patch_stretched(const Ref<Texture> &p_texture, F first_section_size = topleft.y; last_section_size = bottomright.y; } break; + case FILL_BILINEAR_LEFT_AND_RIGHT: { + // TODO: Implement + } break; + case FILL_BILINEAR_TOP_AND_BOTTOM: { + // TODO: Implement + } break; + case FILL_CLOCKWISE: + case FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE: + case FILL_COUNTER_CLOCKWISE: { + // Those modes are circular, not relevant for nine patch + } break; } double width_filled = width_total * p_ratio; @@ -263,6 +274,17 @@ void TextureProgress::draw_nine_patch_stretched(const Ref<Texture> &p_texture, F dst_rect.size.y = width_filled; topleft.y = last_section_size; } break; + case FILL_BILINEAR_LEFT_AND_RIGHT: { + // TODO: Implement + } break; + case FILL_BILINEAR_TOP_AND_BOTTOM: { + // TODO: Implement + } break; + case FILL_CLOCKWISE: + case FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE: + case FILL_COUNTER_CLOCKWISE: { + // Those modes are circular, not relevant for nine patch + } break; } } @@ -466,9 +488,9 @@ void TextureProgress::_bind_methods() { 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,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"); - ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_progress", PROPERTY_HINT_COLOR_NO_ALPHA), "set_tint_progress", "get_tint_progress"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_under"), "set_tint_under", "get_tint_under"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_over"), "set_tint_over", "get_tint_over"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_progress"), "set_tint_progress", "get_tint_progress"); ADD_GROUP("Radial Fill", "radial_"); ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "radial_initial_angle", PROPERTY_HINT_RANGE, "0.0,360.0,0.1,slider"), "set_radial_initial_angle", "get_radial_initial_angle"); ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "radial_fill_degrees", PROPERTY_HINT_RANGE, "0.0,360.0,0.1,slider"), "set_fill_degrees", "get_fill_degrees"); diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 6f09488b64..31f7a21114 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -31,12 +31,12 @@ #include "tree.h" #include <limits.h> -#include "math_funcs.h" -#include "os/input.h" -#include "os/keyboard.h" -#include "os/os.h" -#include "print_string.h" -#include "project_settings.h" +#include "core/math/math_funcs.h" +#include "core/os/input.h" +#include "core/os/keyboard.h" +#include "core/os/os.h" +#include "core/print_string.h" +#include "core/project_settings.h" #include "scene/main/viewport.h" #ifdef TOOLS_ENABLED @@ -159,7 +159,7 @@ void TreeItem::set_text(int p_column, String p_text) { ERR_FAIL_INDEX(p_column, cells.size()); 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) { + if (cells[p_column].mode == TreeItem::CELL_MODE_RANGE) { Vector<String> strings = p_text.split(","); cells.write[p_column].min = INT_MAX; @@ -791,7 +791,6 @@ void TreeItem::_bind_methods() { BIND_ENUM_CONSTANT(CELL_MODE_STRING); BIND_ENUM_CONSTANT(CELL_MODE_CHECK); BIND_ENUM_CONSTANT(CELL_MODE_RANGE); - BIND_ENUM_CONSTANT(CELL_MODE_RANGE_EXPRESSION); BIND_ENUM_CONSTANT(CELL_MODE_ICON); BIND_ENUM_CONSTANT(CELL_MODE_CUSTOM); @@ -1245,9 +1244,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 //font->draw( ci, text_pos, p_item->cells[i].text, col,item_rect.size.x-check_w ); } break; - case TreeItem::CELL_MODE_RANGE: - case TreeItem::CELL_MODE_RANGE_EXPRESSION: { - + case TreeItem::CELL_MODE_RANGE: { if (p_item->cells[i].text != "") { if (!p_item->cells[i].editable) @@ -1257,13 +1254,13 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 String s = RTR("(Other)"); Vector<String> strings = p_item->cells[i].text.split(","); - 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(); + for (int j = 0; j < strings.size(); j++) { + int value = j; + if (!strings[j].get_slicec(':', 1).empty()) { + value = strings[j].get_slicec(':', 1).to_int(); } if (option == value) { - s = strings[i].get_slicec(':', 0); + s = strings[j].get_slicec(':', 0); break; } } @@ -1419,7 +1416,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 while (c) { - if (cache.draw_relationship_lines == 1) { + if (cache.draw_relationship_lines == 1 && (c->get_parent() != root || !hide_root)) { int root_ofs = children_pos.x + ((p_item->disable_folding || hide_folding) ? cache.hseparation : cache.item_margin); int parent_ofs = p_pos.x + ((p_item->disable_folding || hide_folding) ? cache.hseparation : cache.item_margin); Point2i root_pos = Point2i(root_ofs, children_pos.y + label_h / 2) - cache.offset + p_draw_ofs; @@ -1721,7 +1718,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool if (p_button == BUTTON_LEFT || (p_button == BUTTON_RIGHT && allow_rmb_select)) { /* process selection */ - if (p_doubleclick && (!c.editable || c.mode == TreeItem::CELL_MODE_CUSTOM || c.mode == TreeItem::CELL_MODE_ICON /*|| c.mode==TreeItem::CELL_MODE_CHECK*/)) { //it' s confusing for check + if (p_doubleclick && (!c.editable || c.mode == TreeItem::CELL_MODE_CUSTOM || c.mode == TreeItem::CELL_MODE_ICON /*|| c.mode==TreeItem::CELL_MODE_CHECK*/)) { //it's confusing for check emit_signal("item_activated"); incr_search.clear(); @@ -1821,9 +1818,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool //p_item->edited_signal.call(col); } break; - case TreeItem::CELL_MODE_RANGE: - case TreeItem::CELL_MODE_RANGE_EXPRESSION: { - + case TreeItem::CELL_MODE_RANGE: { if (c.text != "") { //if (x >= (get_column_width(col)-item_h/2)) { @@ -2010,21 +2005,6 @@ void Tree::text_editor_enter(String p_text) { //popup_edited_item->edited_signal.call( popup_edited_item_col ); } break; - case TreeItem::CELL_MODE_RANGE_EXPRESSION: { - - if (evaluator) - c.val = evaluator->eval(p_text); - else - c.val = p_text.to_double(); - - if (c.step > 0) - c.val = Math::stepify(c.val, c.step); - if (c.val < c.min) - c.val = c.min; - else if (c.val > c.max) - c.val = c.max; - - } break; default: { ERR_FAIL(); } } @@ -2203,6 +2183,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { Ref<InputEventKey> k = p_event; + bool is_command = k.is_valid() && k->get_command(); if (p_event->is_action("ui_right") && p_event->is_pressed()) { if (!cursor_can_exit_tree) accept_event(); @@ -2239,13 +2220,13 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { _go_left(); } - } else if (p_event->is_action("ui_up") && p_event->is_pressed()) { + } else if (p_event->is_action("ui_up") && p_event->is_pressed() && !is_command) { if (!cursor_can_exit_tree) accept_event(); _go_up(); - } else if (p_event->is_action("ui_down") && p_event->is_pressed()) { + } else if (p_event->is_action("ui_down") && p_event->is_pressed() && !is_command) { if (!cursor_can_exit_tree) accept_event(); @@ -2453,7 +2434,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { update(); } - if (pressing_for_editor && popup_edited_item && (popup_edited_item->get_cell_mode(popup_edited_item_col) == TreeItem::CELL_MODE_RANGE || popup_edited_item->get_cell_mode(popup_edited_item_col) == TreeItem::CELL_MODE_RANGE_EXPRESSION)) { + if (pressing_for_editor && popup_edited_item && (popup_edited_item->get_cell_mode(popup_edited_item_col) == TreeItem::CELL_MODE_RANGE)) { //range drag if (!range_drag_enabled) { @@ -2538,7 +2519,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { pressing_for_editor = false; } - if (cache.click_type == Cache::CLICK_BUTTON) { + if (cache.click_type == Cache::CLICK_BUTTON && cache.click_item != NULL) { // make sure in case of wrong reference after reconstructing whole TreeItems cache.click_item = get_item_at_position(cache.click_pos); emit_signal("button_pressed", cache.click_item, cache.click_column, cache.click_id); @@ -2697,7 +2678,7 @@ bool Tree::edit_selected() { item_edited(col, s); return true; - } else if ((c.mode == TreeItem::CELL_MODE_RANGE || c.mode == TreeItem::CELL_MODE_RANGE_EXPRESSION) && c.text != "") { + } else if (c.mode == TreeItem::CELL_MODE_RANGE && c.text != "") { popup_menu->clear(); for (int i = 0; i < c.text.get_slice_count(","); i++) { @@ -2713,7 +2694,7 @@ bool Tree::edit_selected() { popup_edited_item_col = col; return true; - } else if (c.mode == TreeItem::CELL_MODE_STRING || c.mode == TreeItem::CELL_MODE_RANGE || c.mode == TreeItem::CELL_MODE_RANGE_EXPRESSION) { + } else if (c.mode == TreeItem::CELL_MODE_STRING || c.mode == TreeItem::CELL_MODE_RANGE) { Vector2 ofs(0, (text_editor->get_size().height - rect.size.height) / 2); Point2i textedpos = get_global_position() + rect.position - ofs; @@ -2723,7 +2704,7 @@ bool Tree::edit_selected() { text_editor->set_text(c.mode == TreeItem::CELL_MODE_STRING ? c.text : String::num(c.val, Math::step_decimals(c.step))); text_editor->select_all(); - if (c.mode == TreeItem::CELL_MODE_RANGE || c.mode == TreeItem::CELL_MODE_RANGE_EXPRESSION) { + if (c.mode == TreeItem::CELL_MODE_RANGE) { value_editor->set_position(textedpos + Point2i(0, text_editor->get_size().height)); value_editor->set_size(Size2(rect.size.width, 1)); @@ -2953,7 +2934,7 @@ void Tree::_notification(int p_what) { if (show_column_titles) { - //title butons + //title buttons int ofs = cache.bg->get_margin(MARGIN_LEFT); for (int i = 0; i < columns.size(); i++) { @@ -3713,10 +3694,6 @@ bool Tree::is_folding_hidden() const { return hide_folding; } -void Tree::set_value_evaluator(ValueEvaluator *p_evaluator) { - evaluator = p_evaluator; -} - void Tree::set_drop_mode_flags(int p_flags) { if (drop_mode_flags == p_flags) return; @@ -3831,16 +3808,16 @@ void Tree::_bind_methods() { ADD_SIGNAL(MethodInfo("item_selected")); ADD_SIGNAL(MethodInfo("cell_selected")); - ADD_SIGNAL(MethodInfo("multi_selected", PropertyInfo(Variant::OBJECT, "item"), PropertyInfo(Variant::INT, "column"), PropertyInfo(Variant::BOOL, "selected"))); + ADD_SIGNAL(MethodInfo("multi_selected", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem"), PropertyInfo(Variant::INT, "column"), PropertyInfo(Variant::BOOL, "selected"))); ADD_SIGNAL(MethodInfo("item_rmb_selected", PropertyInfo(Variant::VECTOR2, "position"))); ADD_SIGNAL(MethodInfo("empty_tree_rmb_selected", PropertyInfo(Variant::VECTOR2, "position"))); ADD_SIGNAL(MethodInfo("item_edited")); ADD_SIGNAL(MethodInfo("item_rmb_edited")); ADD_SIGNAL(MethodInfo("item_custom_button_pressed")); ADD_SIGNAL(MethodInfo("item_double_clicked")); - ADD_SIGNAL(MethodInfo("item_collapsed", PropertyInfo(Variant::OBJECT, "item"))); + ADD_SIGNAL(MethodInfo("item_collapsed", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem"))); //ADD_SIGNAL( MethodInfo("item_doubleclicked" ) ); - ADD_SIGNAL(MethodInfo("button_pressed", PropertyInfo(Variant::OBJECT, "item"), PropertyInfo(Variant::INT, "column"), PropertyInfo(Variant::INT, "id"))); + ADD_SIGNAL(MethodInfo("button_pressed", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem"), PropertyInfo(Variant::INT, "column"), PropertyInfo(Variant::INT, "id"))); ADD_SIGNAL(MethodInfo("custom_popup_edited", PropertyInfo(Variant::BOOL, "arrow_clicked"))); ADD_SIGNAL(MethodInfo("item_activated")); ADD_SIGNAL(MethodInfo("column_title_pressed", PropertyInfo(Variant::INT, "column"))); @@ -3934,8 +3911,6 @@ Tree::Tree() { hide_folding = false; - evaluator = NULL; - drop_mode_flags = 0; drop_mode_over = NULL; drop_mode_section = 0; diff --git a/scene/gui/tree.h b/scene/gui/tree.h index 5af66c5faa..34138acb85 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -31,7 +31,6 @@ #ifndef TREE_H #define TREE_H -#include "core/helper/value_evaluator.h" #include "scene/gui/control.h" #include "scene/gui/line_edit.h" #include "scene/gui/popup_menu.h" @@ -54,7 +53,6 @@ public: CELL_MODE_STRING, ///< just a string CELL_MODE_CHECK, ///< string + check CELL_MODE_RANGE, ///< Contains a range - CELL_MODE_RANGE_EXPRESSION, ///< Contains a range CELL_MODE_ICON, ///< Contains an icon, not editable CELL_MODE_CUSTOM, ///< Contains a custom value, show a string, and an edit button }; @@ -504,8 +502,6 @@ private: bool hide_folding; - ValueEvaluator *evaluator; - int _count_selected_items(TreeItem *p_from) const; void _go_left(); void _go_right(); @@ -516,7 +512,7 @@ protected: static void _bind_methods(); //bind helpers - Object *_create_item(Object *p_parent, int p_idx = -1) { + TreeItem *_create_item(Object *p_parent, int p_idx = -1) { return create_item(Object::cast_to<TreeItem>(p_parent), p_idx); } @@ -601,8 +597,6 @@ public: void set_allow_reselect(bool p_allow); bool get_allow_reselect() const; - void set_value_evaluator(ValueEvaluator *p_evaluator); - Tree(); ~Tree(); }; diff --git a/scene/gui/video_player.cpp b/scene/gui/video_player.cpp index 88e1847533..812ca0042b 100644 --- a/scene/gui/video_player.cpp +++ b/scene/gui/video_player.cpp @@ -31,7 +31,7 @@ #include "video_player.h" #include "scene/scene_string_names.h" -#include "os/os.h" +#include "core/os/os.h" #include "servers/audio_server.h" int VideoPlayer::sp_get_channel_count() const { @@ -90,49 +90,32 @@ void VideoPlayer::_mix_audio() { AudioFrame vol = AudioFrame(volume, volume); - // Copy to server's audio buffer - switch (AudioServer::get_singleton()->get_speaker_mode()) { + int cc = AudioServer::get_singleton()->get_channel_count(); - case AudioServer::SPEAKER_MODE_STEREO: { - AudioFrame *target = AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 0); + if (cc == 1) { + AudioFrame *target = AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 0); + ERR_FAIL_COND(!target); - for (int j = 0; j < buffer_size; j++) { + for (int j = 0; j < buffer_size; j++) { - target[j] += buffer[j] * vol; - } - - } break; - case AudioServer::SPEAKER_SURROUND_51: { - - AudioFrame *targets[2] = { - AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 1), - AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 2), - }; - - for (int j = 0; j < buffer_size; j++) { + target[j] += buffer[j] * vol; + } - AudioFrame frame = buffer[j] * vol; - targets[0][j] = frame; - targets[1][j] = frame; - } - } break; - case AudioServer::SPEAKER_SURROUND_71: { + } else { + AudioFrame *targets[4]; - AudioFrame *targets[3] = { - AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 1), - AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 2), - AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 3) - }; + for (int k = 0; k < cc; k++) { + targets[k] = AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, k); + ERR_FAIL_COND(!targets[k]); + } - for (int j = 0; j < buffer_size; j++) { + for (int j = 0; j < buffer_size; j++) { - AudioFrame frame = buffer[j] * vol; - targets[0][j] += frame; - targets[1][j] += frame; - targets[2][j] += frame; + AudioFrame frame = buffer[j] * vol; + for (int k = 0; k < cc; k++) { + targets[k][j] += frame; } - - } break; + } } } diff --git a/scene/main/SCsub b/scene/main/SCsub index bf9125be7f..b01e2fd54d 100644 --- a/scene/main/SCsub +++ b/scene/main/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.scene_sources, "*.cpp") - -Export('env') diff --git a/scene/main/canvas_layer.cpp b/scene/main/canvas_layer.cpp index 7a53de86a1..93f51a44f4 100644 --- a/scene/main/canvas_layer.cpp +++ b/scene/main/canvas_layer.cpp @@ -35,7 +35,7 @@ void CanvasLayer::set_layer(int p_xform) { layer = p_xform; if (viewport.is_valid()) - VisualServer::get_singleton()->viewport_set_canvas_layer(viewport, canvas, layer); + VisualServer::get_singleton()->viewport_set_canvas_stacking(viewport, canvas, layer, get_position_in_parent()); } int CanvasLayer::get_layer() const { @@ -151,7 +151,7 @@ void CanvasLayer::_notification(int p_what) { viewport = vp->get_viewport_rid(); VisualServer::get_singleton()->viewport_attach_canvas(viewport, canvas); - VisualServer::get_singleton()->viewport_set_canvas_layer(viewport, canvas, layer); + VisualServer::get_singleton()->viewport_set_canvas_stacking(viewport, canvas, layer, get_position_in_parent()); VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas, transform); } break; @@ -162,6 +162,10 @@ void CanvasLayer::_notification(int p_what) { viewport = RID(); } break; + case NOTIFICATION_MOVED_IN_PARENT: { + + VisualServer::get_singleton()->viewport_set_canvas_stacking(viewport, canvas, layer, get_position_in_parent()); + } break; } } @@ -206,7 +210,7 @@ void CanvasLayer::set_custom_viewport(Node *p_viewport) { viewport = vp->get_viewport_rid(); VisualServer::get_singleton()->viewport_attach_canvas(viewport, canvas); - VisualServer::get_singleton()->viewport_set_canvas_layer(viewport, canvas, layer); + VisualServer::get_singleton()->viewport_set_canvas_stacking(viewport, canvas, layer, get_position_in_parent()); VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas, transform); } } diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp index 4750e05633..a9b7fba9c7 100644 --- a/scene/main/http_request.cpp +++ b/scene/main/http_request.cpp @@ -330,15 +330,13 @@ bool HTTPRequest::_update_connection() { return true; } - if (client->is_response_chunked()) { - body_len = -1; // No body len because chunked, change your webserver configuration if you want body len - } else { - body_len = client->get_response_body_length(); + // No body len (-1) if chunked or no content-length header was provided. + // Change your webserver configuration if you want body len. + body_len = client->get_response_body_length(); - if (body_size_limit >= 0 && body_len > body_size_limit) { - call_deferred("_request_done", RESULT_BODY_SIZE_LIMIT_EXCEEDED, response_code, response_headers, PoolByteArray()); - return true; - } + if (body_size_limit >= 0 && body_len > body_size_limit) { + call_deferred("_request_done", RESULT_BODY_SIZE_LIMIT_EXCEEDED, response_code, response_headers, PoolByteArray()); + return true; } if (download_to_file != String()) { @@ -378,6 +376,9 @@ bool HTTPRequest::_update_connection() { call_deferred("_request_done", RESULT_SUCCESS, response_code, response_headers, body); return true; } + } else if (client->get_status() == HTTPClient::STATUS_DISCONNECTED) { + // We read till EOF, with no errors. Request is done. + call_deferred("_request_done", RESULT_SUCCESS, response_code, response_headers, body); } return false; diff --git a/scene/main/http_request.h b/scene/main/http_request.h index eb5d020bc5..de09d2afda 100644 --- a/scene/main/http_request.h +++ b/scene/main/http_request.h @@ -31,10 +31,10 @@ #ifndef HTTPREQUEST_H #define HTTPREQUEST_H -#include "io/http_client.h" +#include "core/io/http_client.h" +#include "core/os/file_access.h" +#include "core/os/thread.h" #include "node.h" -#include "os/file_access.h" -#include "os/thread.h" class HTTPRequest : public Node { diff --git a/scene/main/instance_placeholder.cpp b/scene/main/instance_placeholder.cpp index 1443d5efbf..0ee5648de2 100644 --- a/scene/main/instance_placeholder.cpp +++ b/scene/main/instance_placeholder.cpp @@ -30,7 +30,7 @@ #include "instance_placeholder.h" -#include "io/resource_loader.h" +#include "core/io/resource_loader.h" #include "scene/resources/packed_scene.h" bool InstancePlaceholder::_set(const StringName &p_name, const Variant &p_value) { diff --git a/scene/main/node.cpp b/scene/main/node.cpp index e30f58e012..9f234f832d 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -31,10 +31,10 @@ #include "node.h" #include "core/core_string_names.h" +#include "core/io/resource_loader.h" +#include "core/message_queue.h" +#include "core/print_string.h" #include "instance_placeholder.h" -#include "io/resource_loader.h" -#include "message_queue.h" -#include "print_string.h" #include "scene/resources/packed_scene.h" #include "scene/scene_string_names.h" #include "viewport.h" @@ -238,6 +238,16 @@ void Node::_propagate_enter_tree() { // enter groups } +void Node::_propagate_after_exit_tree() { + + data.blocked++; + for (int i = 0; i < data.children.size(); i++) { + data.children[i]->_propagate_after_exit_tree(); + } + data.blocked--; + emit_signal(SceneStringNames::get_singleton()->tree_exited); +} + void Node::_propagate_exit_tree() { //block while removing children @@ -299,8 +309,6 @@ void Node::_propagate_exit_tree() { data.ready_notified = false; data.tree = NULL; data.depth = -1; - - emit_signal(SceneStringNames::get_singleton()->tree_exited); } void Node::move_child(Node *p_child, int p_pos) { @@ -1207,6 +1215,10 @@ void Node::remove_child(Node *p_child) { // validate owner p_child->_propagate_validate_owner(); + + if (data.inside_tree) { + p_child->_propagate_after_exit_tree(); + } } int Node::get_child_count() const { @@ -1336,6 +1348,19 @@ Node *Node::get_parent() const { return data.parent; } +Node *Node::find_parent(const String &p_mask) const { + + Node *p = data.parent; + while (p) { + + if (p->data.name.operator String().match(p_mask)) + return p; + p = p->data.parent; + } + + return NULL; +} + bool Node::is_a_parent_of(const Node *p_node) const { ERR_FAIL_NULL_V(p_node, false); @@ -1810,7 +1835,7 @@ void Node::set_editable_instance(Node *p_node, bool p_editable) { } } -bool Node::is_editable_instance(Node *p_node) const { +bool Node::is_editable_instance(const Node *p_node) const { if (!p_node) return false; //easier, null is never editable :) @@ -2104,6 +2129,12 @@ void Node::_duplicate_and_reown(Node *p_new_parent, const Map<Node *, Node *> &p node->set(name, value); } + List<GroupInfo> groups; + get_groups(&groups); + + for (List<GroupInfo>::Element *E = groups.front(); E; E = E->next()) + node->add_to_group(E->get().name, E->get().persistent); + node->set_name(get_name()); p_new_parent->add_child(node); @@ -2151,15 +2182,17 @@ void Node::_duplicate_signals(const Node *p_original, Node *p_copy) const { continue; } NodePath ptarget = p_original->get_path_to(target); - Node *copytarget = p_copy->get_node(ptarget); - // Cannot find a path to the duplicate target, so it seems it's not part - // of the duplicated and not yet parented hierarchy, so at least try to connect + Node *copytarget = target; + + // Atempt to find a path to the duplicate target, if it seems it's not part + // of the duplicated and not yet parented hierarchy then at least try to connect // to the same target as the original - if (!copytarget) - copytarget = target; - if (copy && copytarget) { + if (p_copy->has_node(ptarget)) + copytarget = p_copy->get_node(ptarget); + + if (copy && copytarget && !copy->is_connected(E->get().signal, copytarget, E->get().method)) { copy->connect(E->get().signal, copytarget, E->get().method, E->get().binds, E->get().flags); } } @@ -2198,6 +2231,12 @@ Node *Node::duplicate_and_reown(const Map<Node *, Node *> &p_reown_map) const { node->set(name, get(name)); } + List<GroupInfo> groups; + get_groups(&groups); + + for (List<GroupInfo>::Element *E = groups.front(); E; E = E->next()) + node->add_to_group(E->get().name, E->get().persistent); + for (int i = 0; i < get_child_count(); i++) { get_child(i)->_duplicate_and_reown(node, p_reown_map); @@ -2449,6 +2488,7 @@ void Node::_set_tree(SceneTree *p_tree) { tree_changed_b->tree_changed(); } +#ifdef DEBUG_ENABLED static void _Node_debug_sn(Object *p_obj) { Node *n = Object::cast_to<Node>(p_obj); @@ -2470,6 +2510,7 @@ static void _Node_debug_sn(Object *p_obj) { path = String(p->get_name()) + "/" + p->get_path_to(n); print_line(itos(p_obj->get_instance_id()) + " - Stray Node: " + path + " (Type: " + n->get_class() + ")"); } +#endif // DEBUG_ENABLED void Node::_print_stray_nodes() { @@ -2479,7 +2520,6 @@ void Node::_print_stray_nodes() { void Node::print_stray_nodes() { #ifdef DEBUG_ENABLED - ObjectDB::debug_objects(_Node_debug_sn); #endif } @@ -2605,6 +2645,7 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("get_node", "path"), &Node::get_node); ClassDB::bind_method(D_METHOD("get_parent"), &Node::get_parent); ClassDB::bind_method(D_METHOD("find_node", "mask", "recursive", "owned"), &Node::find_node, DEFVAL(true), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("find_parent", "mask"), &Node::find_parent); ClassDB::bind_method(D_METHOD("has_node_and_resource", "path"), &Node::has_node_and_resource); ClassDB::bind_method(D_METHOD("get_node_and_resource", "path"), &Node::_get_node_and_resource); diff --git a/scene/main/node.h b/scene/main/node.h index f3422618ce..78db12dda9 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -31,13 +31,13 @@ #ifndef NODE_H #define NODE_H -#include "class_db.h" -#include "map.h" -#include "node_path.h" -#include "object.h" -#include "project_settings.h" +#include "core/class_db.h" +#include "core/map.h" +#include "core/node_path.h" +#include "core/object.h" +#include "core/project_settings.h" +#include "core/script_language.h" #include "scene/main/scene_tree.h" -#include "script_language.h" class Viewport; class SceneState; @@ -166,6 +166,7 @@ private: void _propagate_enter_tree(); void _propagate_ready(); void _propagate_exit_tree(); + void _propagate_after_exit_tree(); void _propagate_validate_owner(); void _print_stray_nodes(); void _propagate_pause_owner(Node *p_owner); @@ -256,6 +257,8 @@ public: Node *get_node_and_resource(const NodePath &p_path, RES &r_res, Vector<StringName> &r_leftover_subpath, bool p_last_is_property = true) const; Node *get_parent() const; + Node *find_parent(const String &p_mask) const; + _FORCE_INLINE_ SceneTree *get_tree() const { ERR_FAIL_COND_V(!data.tree, NULL); return data.tree; @@ -300,7 +303,7 @@ public: String get_filename() const; void set_editable_instance(Node *p_node, bool p_editable); - bool is_editable_instance(Node *p_node) const; + bool is_editable_instance(const Node *p_node) const; void set_editable_instances(const HashMap<NodePath, int> &p_editable_instances); HashMap<NodePath, int> get_editable_instances() const; diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 1d23650a1e..f7dec77ce4 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -30,16 +30,16 @@ #include "scene_tree.h" +#include "core/io/marshalls.h" +#include "core/io/resource_loader.h" +#include "core/message_queue.h" +#include "core/os/keyboard.h" +#include "core/os/os.h" +#include "core/print_string.h" +#include "core/project_settings.h" #include "editor/editor_node.h" -#include "io/marshalls.h" -#include "io/resource_loader.h" #include "main/input_default.h" -#include "message_queue.h" #include "node.h" -#include "os/keyboard.h" -#include "os/os.h" -#include "print_string.h" -#include "project_settings.h" #include "scene/resources/dynamic_font.h" #include "scene/resources/material.h" #include "scene/resources/mesh.h" @@ -598,6 +598,7 @@ void SceneTree::finish() { if (root) { root->_set_tree(NULL); + root->_propagate_after_exit_tree(); memdelete(root); //delete root } } @@ -1196,6 +1197,9 @@ void SceneTree::_update_root_rect() { } switch (stretch_mode) { + case STRETCH_MODE_DISABLED: { + // Already handled above + } break; case STRETCH_MODE_2D: { root->set_size((screen_size / stretch_shrink).floor()); @@ -1601,7 +1605,7 @@ void SceneTree::_live_edit_duplicate_node_func(const NodePath &p_at, const Strin continue; Node *n2 = n->get_node(p_at); - Node *dup = n2->duplicate(true); + Node *dup = n2->duplicate(Node::DUPLICATE_SIGNALS | Node::DUPLICATE_GROUPS | Node::DUPLICATE_SCRIPTS); if (!dup) continue; @@ -1862,10 +1866,10 @@ void SceneTree::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "multiplayer_poll"), "set_multiplayer_poll_enabled", "is_multiplayer_poll_enabled"); ADD_SIGNAL(MethodInfo("tree_changed")); - ADD_SIGNAL(MethodInfo("node_added", PropertyInfo(Variant::OBJECT, "node"))); - ADD_SIGNAL(MethodInfo("node_removed", PropertyInfo(Variant::OBJECT, "node"))); + ADD_SIGNAL(MethodInfo("node_added", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); + ADD_SIGNAL(MethodInfo("node_removed", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); ADD_SIGNAL(MethodInfo("screen_resized")); - ADD_SIGNAL(MethodInfo("node_configuration_warning_changed", PropertyInfo(Variant::OBJECT, "node"))); + ADD_SIGNAL(MethodInfo("node_configuration_warning_changed", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); ADD_SIGNAL(MethodInfo("idle_frame")); ADD_SIGNAL(MethodInfo("physics_frame")); @@ -1940,6 +1944,7 @@ SceneTree::SceneTree() { debug_navigation_color = GLOBAL_DEF("debug/shapes/navigation/geometry_color", Color(0.1, 1.0, 0.7, 0.4)); debug_navigation_disabled_color = GLOBAL_DEF("debug/shapes/navigation/disabled_geometry_color", Color(1.0, 0.7, 0.1, 0.4)); collision_debug_contacts = GLOBAL_DEF("debug/shapes/collision/max_contacts_displayed", 10000); + ProjectSettings::get_singleton()->set_custom_property_info("debug/shapes/collision/max_contacts_displayed", PropertyInfo(Variant::INT, "debug/shapes/collision/max_contacts_displayed", PROPERTY_HINT_RANGE, "0,20000,1")); // No negative tree_version = 1; physics_process_time = 1; @@ -1973,7 +1978,9 @@ SceneTree::SceneTree() { current_scene = NULL; int ref_atlas_size = GLOBAL_DEF("rendering/quality/reflections/atlas_size", 2048); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/reflections/atlas_size", PropertyInfo(Variant::INT, "rendering/quality/reflections/atlas_size", PROPERTY_HINT_RANGE, "0,8192,or_greater")); //next_power_of_2 will return a 0 as min value int ref_atlas_subdiv = GLOBAL_DEF("rendering/quality/reflections/atlas_subdiv", 8); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/reflections/atlas_subdiv", PropertyInfo(Variant::INT, "rendering/quality/reflections/atlas_subdiv", PROPERTY_HINT_RANGE, "0,32,or_greater")); //next_power_of_2 will return a 0 as min value int msaa_mode = GLOBAL_DEF("rendering/quality/filters/msaa", 0); ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/filters/msaa", PropertyInfo(Variant::INT, "rendering/quality/filters/msaa", PROPERTY_HINT_ENUM, "Disabled,2x,4x,8x,16x")); root->set_msaa(Viewport::MSAA(msaa_mode)); diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index 11201097d4..d59cbe05fb 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -31,13 +31,13 @@ #ifndef SCENE_MAIN_LOOP_H #define SCENE_MAIN_LOOP_H -#include "io/multiplayer_api.h" -#include "os/main_loop.h" -#include "os/thread_safe.h" +#include "core/io/multiplayer_api.h" +#include "core/os/main_loop.h" +#include "core/os/thread_safe.h" +#include "core/self_list.h" #include "scene/resources/mesh.h" #include "scene/resources/world.h" #include "scene/resources/world_2d.h" -#include "self_list.h" /** @author Juan Linietsky <reduzio@gmail.com> diff --git a/scene/main/timer.cpp b/scene/main/timer.cpp index c285694dfa..227840531e 100755 --- a/scene/main/timer.cpp +++ b/scene/main/timer.cpp @@ -30,7 +30,7 @@ #include "timer.h" -#include "engine.h" +#include "core/engine.h" void Timer::_notification(int p_what) { diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 7c17e250ae..f8cc1bdd77 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -30,9 +30,9 @@ #include "viewport.h" -#include "os/input.h" -#include "os/os.h" -#include "project_settings.h" +#include "core/os/input.h" +#include "core/os/os.h" +#include "core/project_settings.h" #include "scene/2d/collision_object_2d.h" #include "scene/3d/camera.h" #include "scene/3d/collision_object.h" @@ -645,10 +645,8 @@ Rect2 Viewport::get_visible_rect() const { Rect2 r; if (size == Size2()) { - - r = Rect2(Point2(), Size2(OS::get_singleton()->get_window_size().width, OS::get_singleton()->get_window_size().height)); + r = Rect2(Point2(), OS::get_singleton()->get_window_size()); } else { - r = Rect2(Point2(), size); } @@ -717,15 +715,6 @@ void Viewport::set_canvas_transform(const Transform2D &p_transform) { canvas_transform = p_transform; VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, find_world_2d()->get_canvas(), canvas_transform); - - Transform2D xform = (global_canvas_transform * canvas_transform).affine_inverse(); - Size2 ss = get_visible_rect().size; - /*SpatialSound2DServer::get_singleton()->listener_set_transform(internal_listener_2d, Transform2D(0, xform.xform(ss*0.5))); - Vector2 ss2 = ss*xform.get_scale(); - float panrange = MAX(ss2.x,ss2.y); - - SpatialSound2DServer::get_singleton()->listener_set_param(internal_listener_2d, SpatialSound2DServer::LISTENER_PARAM_PAN_RANGE, panrange); -*/ } Transform2D Viewport::get_canvas_transform() const { @@ -738,15 +727,6 @@ void Viewport::_update_global_transform() { Transform2D sxform = stretch_transform * global_canvas_transform; VisualServer::get_singleton()->viewport_set_global_canvas_transform(viewport, sxform); - - Transform2D xform = (sxform * canvas_transform).affine_inverse(); - Size2 ss = get_visible_rect().size; - /*SpatialSound2DServer::get_singleton()->listener_set_transform(internal_listener_2d, Transform2D(0, xform.xform(ss*0.5))); - Vector2 ss2 = ss*xform.get_scale(); - float panrange = MAX(ss2.x,ss2.y); - - SpatialSound2DServer::get_singleton()->listener_set_param(internal_listener_2d, SpatialSound2DServer::LISTENER_PARAM_PAN_RANGE, panrange); -*/ } void Viewport::set_global_canvas_transform(const Transform2D &p_transform) { @@ -1885,8 +1865,16 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { MenuButton *popup_menu_parent = NULL; MenuButton *menu_button = Object::cast_to<MenuButton>(over); - if (popup_menu) + if (popup_menu) { popup_menu_parent = Object::cast_to<MenuButton>(popup_menu->get_parent()); + if (!popup_menu_parent) { + // Go through the parents to see if there's a MenuButton at the end. + while (Object::cast_to<PopupMenu>(popup_menu->get_parent())) { + popup_menu = Object::cast_to<PopupMenu>(popup_menu->get_parent()); + } + 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. @@ -2815,6 +2803,8 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("gui_get_drag_data"), &Viewport::gui_get_drag_data); ClassDB::bind_method(D_METHOD("gui_is_dragging"), &Viewport::gui_is_dragging); + ClassDB::bind_method(D_METHOD("get_modal_stack_top"), &Viewport::get_modal_stack_top); + ClassDB::bind_method(D_METHOD("set_disable_input", "disable"), &Viewport::set_disable_input); ClassDB::bind_method(D_METHOD("is_input_disabled"), &Viewport::is_input_disabled); @@ -2988,6 +2978,7 @@ Viewport::Viewport() { //gui.tooltip_timer->force_parent_owned(); gui.tooltip_delay = GLOBAL_DEF("gui/timers/tooltip_delay_sec", 0.7); + ProjectSettings::get_singleton()->set_custom_property_info("gui/timers/tooltip_delay_sec", PropertyInfo(Variant::REAL, "gui/timers/tooltip_delay_sec", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater")); // No negative numbers gui.tooltip = NULL; gui.tooltip_label = NULL; diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 694cf70fe1..08c1ac1d12 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -31,11 +31,11 @@ #ifndef VIEWPORT_H #define VIEWPORT_H +#include "core/math/transform_2d.h" #include "scene/main/node.h" #include "scene/resources/texture.h" #include "scene/resources/world_2d.h" #include "servers/visual_server.h" -#include "transform_2d.h" /** @author Juan Linietsky <reduzio@gmail.com> */ diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 3d837d4468..97230d422b 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -42,6 +42,7 @@ #include "scene/2d/canvas_modulate.h" #include "scene/2d/collision_polygon_2d.h" #include "scene/2d/collision_shape_2d.h" +#include "scene/2d/cpu_particles_2d.h" #include "scene/2d/joints_2d.h" #include "scene/2d/light_2d.h" #include "scene/2d/light_occluder_2d.h" @@ -143,6 +144,7 @@ #include "scene/resources/mesh_data_tool.h" #include "scene/resources/mesh_library.h" #include "scene/resources/packed_scene.h" +#include "scene/resources/particles_material.h" #include "scene/resources/plane_shape.h" #include "scene/resources/polygon_path_finder.h" #include "scene/resources/primitive_meshes.h" @@ -164,8 +166,6 @@ #include "scene/resources/world_2d.h" #include "scene/scene_string_names.h" -#include "scene/3d/cpu_particles.h" -#include "scene/3d/particles.h" #include "scene/3d/scenario_fx.h" #include "scene/3d/spatial.h" @@ -178,6 +178,7 @@ #include "scene/3d/camera.h" #include "scene/3d/collision_polygon.h" #include "scene/3d/collision_shape.h" +#include "scene/3d/cpu_particles.h" #include "scene/3d/gi_probe.h" #include "scene/3d/immediate_geometry.h" #include "scene/3d/interpolated_camera.h" @@ -187,6 +188,7 @@ #include "scene/3d/multimesh_instance.h" #include "scene/3d/navigation.h" #include "scene/3d/navigation_mesh.h" +#include "scene/3d/particles.h" #include "scene/3d/path.h" #include "scene/3d/physics_body.h" #include "scene/3d/physics_joint.h" @@ -514,6 +516,7 @@ void register_scene_types() { SceneTree::add_idle_callback(CanvasItemMaterial::flush_changes); CanvasItemMaterial::init_shaders(); ClassDB::register_class<Node2D>(); + ClassDB::register_class<CPUParticles2D>(); ClassDB::register_class<Particles2D>(); //ClassDB::register_class<ParticleAttractor2D>(); ClassDB::register_class<Sprite>(); @@ -563,6 +566,9 @@ void register_scene_types() { /* REGISTER RESOURCES */ ClassDB::register_virtual_class<Shader>(); + ClassDB::register_class<ParticlesMaterial>(); + SceneTree::add_idle_callback(ParticlesMaterial::flush_changes); + ParticlesMaterial::init_shaders(); #ifndef _3D_DISABLED ClassDB::register_virtual_class<Mesh>(); @@ -580,10 +586,6 @@ void register_scene_types() { SceneTree::add_idle_callback(SpatialMaterial::flush_changes); SpatialMaterial::init_shaders(); - ClassDB::register_class<ParticlesMaterial>(); - SceneTree::add_idle_callback(ParticlesMaterial::flush_changes); - ParticlesMaterial::init_shaders(); - ClassDB::register_class<MultiMesh>(); ClassDB::register_class<MeshLibrary>(); diff --git a/scene/resources/SCsub b/scene/resources/SCsub index 2ad90247a7..5e5b6f8fd5 100644 --- a/scene/resources/SCsub +++ b/scene/resources/SCsub @@ -4,6 +4,4 @@ Import('env') env.add_source_files(env.scene_sources, "*.cpp") -Export('env') - SConscript("default_theme/SCsub") diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index 58e6db3f5e..5c01cadcd5 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -30,7 +30,7 @@ #include "animation.h" -#include "geometry.h" +#include "core/math/geometry.h" #define ANIM_MIN_LENGTH 0.001 @@ -1442,7 +1442,7 @@ void Animation::track_set_key_transition(int p_track, int p_key_idx, float p_tra case TYPE_BEZIER: case TYPE_AUDIO: case TYPE_ANIMATION: { - // they dont use transition + // they don't use transition } break; } } @@ -1901,7 +1901,7 @@ void Animation::value_track_set_update_mode(int p_track, UpdateMode p_mode) { ERR_FAIL_INDEX(p_track, tracks.size()); Track *t = tracks[p_track]; ERR_FAIL_COND(t->type != TYPE_VALUE); - ERR_FAIL_INDEX(p_mode, 4); + ERR_FAIL_INDEX((int)p_mode, 4); ValueTrack *vt = static_cast<ValueTrack *>(t); vt->update_mode = p_mode; diff --git a/scene/resources/animation.h b/scene/resources/animation.h index a41e6ea5d7..6eec2c8bc7 100644 --- a/scene/resources/animation.h +++ b/scene/resources/animation.h @@ -31,7 +31,7 @@ #ifndef ANIMATION_H #define ANIMATION_H -#include "resource.h" +#include "core/resource.h" /** @author Juan Linietsky <reduzio@gmail.com> */ diff --git a/scene/resources/audio_stream_sample.cpp b/scene/resources/audio_stream_sample.cpp index e6a4b01deb..9ee85b64b6 100644 --- a/scene/resources/audio_stream_sample.cpp +++ b/scene/resources/audio_stream_sample.cpp @@ -29,8 +29,8 @@ /*************************************************************************/ #include "audio_stream_sample.h" -#include "io/marshalls.h" -#include "os/file_access.h" +#include "core/io/marshalls.h" +#include "core/os/file_access.h" void AudioStreamPlaybackSample::start(float p_from_pos) { @@ -562,13 +562,13 @@ void AudioStreamSample::save_to_wav(String p_path) { PoolVector<uint8_t>::Read read_data = get_data().read(); switch (format) { case AudioStreamSample::FORMAT_8_BITS: - for (int i = 0; i < data_bytes; i++) { + for (unsigned 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++) { + for (unsigned int i = 0; i < data_bytes / 2; i++) { uint16_t data_point = decode_uint16(&read_data[i * 2]); file->store_16(data_point); } diff --git a/scene/resources/bit_mask.cpp b/scene/resources/bit_mask.cpp index 5694099754..56b236d03b 100644 --- a/scene/resources/bit_mask.cpp +++ b/scene/resources/bit_mask.cpp @@ -30,7 +30,7 @@ #include "bit_mask.h" -#include "io/image_loader.h" +#include "core/io/image_loader.h" void BitMap::create(const Size2 &p_size) { @@ -183,7 +183,6 @@ Vector<Vector2> BitMap::_march_square(const Rect2i &rect, const Point2i &start) unsigned int count = 0; Set<Point2i> case9s; Set<Point2i> case6s; - int i; Vector<Vector2> _points; do { int sv = 0; diff --git a/scene/resources/bit_mask.h b/scene/resources/bit_mask.h index 40f0bfb04a..04191a7774 100644 --- a/scene/resources/bit_mask.h +++ b/scene/resources/bit_mask.h @@ -31,9 +31,9 @@ #ifndef BIT_MASK_H #define BIT_MASK_H -#include "image.h" -#include "io/resource_loader.h" -#include "resource.h" +#include "core/image.h" +#include "core/io/resource_loader.h" +#include "core/resource.h" class BitMap : public Resource { diff --git a/scene/resources/bounds.h b/scene/resources/bounds.h index dfe2fe40c6..c86e15ae40 100644 --- a/scene/resources/bounds.h +++ b/scene/resources/bounds.h @@ -31,8 +31,8 @@ #ifndef BOUNDS_H #define BOUNDS_H -#include "bsp_tree.h" -#include "resource.h" +#include "core/math/bsp_tree.h" +#include "core/resource.h" class Bounds : public Resource { diff --git a/scene/resources/canvas.h b/scene/resources/canvas.h index dfdea82ca5..cd37ea3583 100644 --- a/scene/resources/canvas.h +++ b/scene/resources/canvas.h @@ -31,7 +31,7 @@ #ifndef CANVAS_H #define CANVAS_H -#include "resource.h" +#include "core/resource.h" class Canvas : public Resource { diff --git a/scene/resources/color_ramp.cpp b/scene/resources/color_ramp.cpp index 4a43303d84..d5e8c17cbc 100644 --- a/scene/resources/color_ramp.cpp +++ b/scene/resources/color_ramp.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "color_ramp.h" -#include "core_string_names.h" +#include "core/core_string_names.h" //setter and getter names for property serialization #define COLOR_RAMP_GET_OFFSETS "get_offsets" diff --git a/scene/resources/color_ramp.h b/scene/resources/color_ramp.h index 070ad7f0d3..88fa4beb7f 100644 --- a/scene/resources/color_ramp.h +++ b/scene/resources/color_ramp.h @@ -31,7 +31,7 @@ #ifndef SCENE_RESOURCES_COLOR_RAMP_H_ #define SCENE_RESOURCES_COLOR_RAMP_H_ -#include "resource.h" +#include "core/resource.h" class Gradient : public Resource { GDCLASS(Gradient, Resource); diff --git a/scene/resources/convex_polygon_shape.cpp b/scene/resources/convex_polygon_shape.cpp index fa9369d3bc..39488760cd 100644 --- a/scene/resources/convex_polygon_shape.cpp +++ b/scene/resources/convex_polygon_shape.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "convex_polygon_shape.h" -#include "quick_hull.h" +#include "core/math/quick_hull.h" #include "servers/physics_server.h" Vector<Vector3> ConvexPolygonShape::_gen_debug_mesh_lines() { @@ -38,10 +38,9 @@ Vector<Vector3> ConvexPolygonShape::_gen_debug_mesh_lines() { if (points.size() > 3) { - QuickHull qh; Vector<Vector3> varr = Variant(points); Geometry::MeshData md; - Error err = qh.build(varr, md); + Error err = QuickHull::build(varr, md); if (err == OK) { Vector<Vector3> lines; lines.resize(md.edges.size() * 2); diff --git a/scene/resources/convex_polygon_shape_2d.cpp b/scene/resources/convex_polygon_shape_2d.cpp index d061c4ea1e..f325af7ea4 100644 --- a/scene/resources/convex_polygon_shape_2d.cpp +++ b/scene/resources/convex_polygon_shape_2d.cpp @@ -30,7 +30,7 @@ #include "convex_polygon_shape_2d.h" -#include "geometry.h" +#include "core/math/geometry.h" #include "servers/physics_2d_server.h" #include "servers/visual_server.h" diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp index d8989bf062..9188d890f7 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -30,7 +30,7 @@ #include "curve.h" -#include "core_string_names.h" +#include "core/core_string_names.h" template <class T> static _FORCE_INLINE_ T _bezier_interp(real_t t, T start, T control_1, T control_2, T end) { @@ -491,6 +491,7 @@ void Curve::ensure_default_setup(float p_min, float p_max) { void Curve::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_point_count"), &Curve::get_point_count); ClassDB::bind_method(D_METHOD("add_point", "position", "left_tangent", "right_tangent", "left_mode", "right_mode"), &Curve::add_point, DEFVAL(0), DEFVAL(0), DEFVAL(TANGENT_FREE), DEFVAL(TANGENT_FREE)); ClassDB::bind_method(D_METHOD("remove_point", "index"), &Curve::remove_point); ClassDB::bind_method(D_METHOD("clear_points"), &Curve::clear_points); diff --git a/scene/resources/curve.h b/scene/resources/curve.h index 058c4f1bc2..234e3a6779 100644 --- a/scene/resources/curve.h +++ b/scene/resources/curve.h @@ -31,7 +31,7 @@ #ifndef CURVE_H #define CURVE_H -#include "resource.h" +#include "core/resource.h" // y(x) curve class Curve : public Resource { diff --git a/scene/resources/default_theme/SCsub b/scene/resources/default_theme/SCsub index bf9125be7f..b01e2fd54d 100644 --- a/scene/resources/default_theme/SCsub +++ b/scene/resources/default_theme/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.scene_sources, "*.cpp") - -Export('env') diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index 0eee2ae393..4de47b2cb0 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -32,7 +32,7 @@ #include "scene/resources/theme.h" -#include "os/os.h" +#include "core/os/os.h" #include "theme_data.h" #include "font_hidpi.inc" @@ -120,41 +120,7 @@ static Ref<Texture> make_icon(T p_src) { return texture; } -static Ref<Shader> make_shader(const char *vertex_code, const char *fragment_code, const char *lighting_code) { - Ref<Shader> shader = (memnew(Shader())); - //shader->set_code(vertex_code, fragment_code, lighting_code); - - return shader; -} - -static Ref<BitmapFont> make_font(int p_height, int p_ascent, int p_valign, int p_charcount, const int *p_chars, const Ref<Texture> &p_texture) { - - Ref<BitmapFont> font(memnew(BitmapFont)); - font->add_texture(p_texture); - - for (int i = 0; i < p_charcount; i++) { - - const int *c = &p_chars[i * 8]; - - int chr = c[0]; - Rect2 frect; - frect.position.x = c[1]; - frect.position.y = c[2]; - frect.size.x = c[3]; - frect.size.y = c[4]; - Point2 align(c[5], c[6] + p_valign); - int advance = c[7]; - - font->add_char(chr, 0, frect, align, advance); - } - - font->set_height(p_height); - font->set_ascent(p_ascent); - - return font; -} - -static Ref<BitmapFont> make_font2(int p_height, int p_ascent, int p_charcount, const int *p_char_rects, int p_kerning_count, const int *p_kernings, int p_w, int p_h, const unsigned char *p_img) { +static Ref<BitmapFont> make_font(int p_height, int p_ascent, int p_charcount, const int *p_char_rects, int p_kerning_count, const int *p_kernings, int p_w, int p_h, const unsigned char *p_img) { Ref<BitmapFont> font(memnew(BitmapFont)); @@ -209,8 +175,6 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const tex_cache = memnew(TexCacheMap); - //Ref<BitmapFont> default_font = make_font(_bi_font_normal_height,_bi_font_normal_ascent,_bi_font_normal_valign,_bi_font_normal_charcount,_bi_font_normal_characters,make_icon(font_normal_png)); - // Font Colors Color control_font_color = Color::html("e0e0e0"); @@ -364,6 +328,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("pressed", "CheckBox", cbx_empty); theme->set_stylebox("disabled", "CheckBox", cbx_empty); theme->set_stylebox("hover", "CheckBox", cbx_empty); + theme->set_stylebox("hover_pressed", "CheckBox", cbx_empty); theme->set_stylebox("focus", "CheckBox", cbx_focus); theme->set_icon("checked", "CheckBox", make_icon(checked_png)); @@ -376,6 +341,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_color", "CheckBox", control_font_color); theme->set_color("font_color_pressed", "CheckBox", control_font_color_pressed); theme->set_color("font_color_hover", "CheckBox", control_font_color_hover); + theme->set_color("font_color_hover_pressed", "CheckBox", control_font_color_pressed); theme->set_color("font_color_disabled", "CheckBox", control_font_color_disabled); theme->set_constant("hseparation", "CheckBox", 4 * scale); @@ -393,6 +359,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("pressed", "CheckButton", cb_empty); theme->set_stylebox("disabled", "CheckButton", cb_empty); theme->set_stylebox("hover", "CheckButton", cb_empty); + theme->set_stylebox("hover_pressed", "CheckButton", cb_empty); theme->set_stylebox("focus", "CheckButton", focus); theme->set_icon("on", "CheckButton", make_icon(toggle_on_png)); @@ -403,6 +370,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_color", "CheckButton", control_font_color); theme->set_color("font_color_pressed", "CheckButton", control_font_color_pressed); theme->set_color("font_color_hover", "CheckButton", control_font_color_hover); + theme->set_color("font_color_hover_pressed", "CheckButton", control_font_color_pressed); theme->set_color("font_color_disabled", "CheckButton", control_font_color_disabled); theme->set_constant("hseparation", "CheckButton", 4 * scale); @@ -909,9 +877,9 @@ void make_default_theme(bool p_hidpi, Ref<Font> p_font) { if (p_font.is_valid()) { default_font = p_font; } else if (p_hidpi) { - default_font = make_font2(_hidpi_font_height, _hidpi_font_ascent, _hidpi_font_charcount, &_hidpi_font_charrects[0][0], _hidpi_font_kerning_pair_count, &_hidpi_font_kerning_pairs[0][0], _hidpi_font_img_width, _hidpi_font_img_height, _hidpi_font_img_data); + default_font = make_font(_hidpi_font_height, _hidpi_font_ascent, _hidpi_font_charcount, &_hidpi_font_charrects[0][0], _hidpi_font_kerning_pair_count, &_hidpi_font_kerning_pairs[0][0], _hidpi_font_img_width, _hidpi_font_img_height, _hidpi_font_img_data); } else { - default_font = make_font2(_lodpi_font_height, _lodpi_font_ascent, _lodpi_font_charcount, &_lodpi_font_charrects[0][0], _lodpi_font_kerning_pair_count, &_lodpi_font_kerning_pairs[0][0], _lodpi_font_img_width, _lodpi_font_img_height, _lodpi_font_img_data); + default_font = make_font(_lodpi_font_height, _lodpi_font_ascent, _lodpi_font_charcount, &_lodpi_font_charrects[0][0], _lodpi_font_kerning_pair_count, &_lodpi_font_kerning_pairs[0][0], _lodpi_font_img_width, _lodpi_font_img_height, _lodpi_font_img_data); } Ref<Font> large_font = default_font; fill_default_theme(t, default_font, large_font, default_icon, default_style, p_hidpi ? 2.0 : 1.0); diff --git a/scene/resources/dynamic_font.cpp b/scene/resources/dynamic_font.cpp index 4df849df6a..7422c56eee 100644 --- a/scene/resources/dynamic_font.cpp +++ b/scene/resources/dynamic_font.cpp @@ -30,8 +30,8 @@ #ifdef FREETYPE_ENABLED #include "dynamic_font.h" -#include "os/file_access.h" -#include "os/os.h" +#include "core/os/file_access.h" +#include "core/os/os.h" #include FT_STROKER_H @@ -201,10 +201,10 @@ Error DynamicFontAtSize::_load() { if (FT_HAS_COLOR(face)) { int best_match = 0; - int diff = ABS(id.size - face->available_sizes[0].width); + int diff = ABS(id.size - ((int64_t)face->available_sizes[0].width)); scale_color_font = float(id.size) / face->available_sizes[0].width; for (int i = 1; i < face->num_fixed_sizes; i++) { - int ndiff = ABS(id.size - face->available_sizes[i].width); + int ndiff = ABS(id.size - ((int64_t)face->available_sizes[i].width)); if (ndiff < diff) { best_match = i; diff = ndiff; @@ -1029,7 +1029,7 @@ void DynamicFont::_bind_methods() { Mutex *DynamicFont::dynamic_font_mutex = NULL; -SelfList<DynamicFont>::List DynamicFont::dynamic_fonts; +SelfList<DynamicFont>::List *DynamicFont::dynamic_fonts = NULL; DynamicFont::DynamicFont() : font_list(this) { @@ -1041,29 +1041,31 @@ DynamicFont::DynamicFont() : spacing_char = 0; spacing_space = 0; outline_color = Color(1, 1, 1); - if (dynamic_font_mutex) + if (dynamic_font_mutex) { dynamic_font_mutex->lock(); - dynamic_fonts.add(&font_list); - if (dynamic_font_mutex) + dynamic_fonts->add(&font_list); dynamic_font_mutex->unlock(); + } } DynamicFont::~DynamicFont() { - - if (dynamic_font_mutex) + if (dynamic_font_mutex) { dynamic_font_mutex->lock(); - dynamic_fonts.remove(&font_list); - if (dynamic_font_mutex) + dynamic_fonts->remove(&font_list); dynamic_font_mutex->unlock(); + } } void DynamicFont::initialize_dynamic_fonts() { + dynamic_fonts = memnew(SelfList<DynamicFont>::List()); dynamic_font_mutex = Mutex::create(); } void DynamicFont::finish_dynamic_fonts() { memdelete(dynamic_font_mutex); dynamic_font_mutex = NULL; + memdelete(dynamic_fonts); + dynamic_fonts = NULL; } void DynamicFont::update_oversampling() { @@ -1073,7 +1075,7 @@ void DynamicFont::update_oversampling() { if (dynamic_font_mutex) dynamic_font_mutex->lock(); - SelfList<DynamicFont> *E = dynamic_fonts.first(); + SelfList<DynamicFont> *E = dynamic_fonts->first(); while (E) { if (E->self()->data_at_size.is_valid()) { @@ -1083,8 +1085,19 @@ void DynamicFont::update_oversampling() { E->self()->outline_data_at_size->update_oversampling(); } + for (int i = 0; i < E->self()->fallback_data_at_size.size(); i++) { + if (E->self()->fallback_data_at_size[i].is_valid()) { + E->self()->fallback_data_at_size.write[i]->update_oversampling(); + + if (E->self()->has_outline() && E->self()->fallback_outline_data_at_size[i].is_valid()) { + E->self()->fallback_outline_data_at_size.write[i]->update_oversampling(); + } + } + } + changed.push_back(Ref<DynamicFont>(E->self())); } + E = E->next(); } diff --git a/scene/resources/dynamic_font.h b/scene/resources/dynamic_font.h index f460bca2d4..bd3f84d62c 100644 --- a/scene/resources/dynamic_font.h +++ b/scene/resources/dynamic_font.h @@ -32,10 +32,10 @@ #define DYNAMIC_FONT_H #ifdef FREETYPE_ENABLED -#include "io/resource_loader.h" -#include "os/mutex.h" -#include "os/thread_safe.h" -#include "pair.h" +#include "core/io/resource_loader.h" +#include "core/os/mutex.h" +#include "core/os/thread_safe.h" +#include "core/pair.h" #include "scene/resources/font.h" #include <ft2build.h> @@ -285,7 +285,7 @@ public: SelfList<DynamicFont> font_list; static Mutex *dynamic_font_mutex; - static SelfList<DynamicFont>::List dynamic_fonts; + static SelfList<DynamicFont>::List *dynamic_fonts; static void initialize_dynamic_fonts(); static void finish_dynamic_fonts(); diff --git a/scene/resources/dynamic_font_stb.cpp b/scene/resources/dynamic_font_stb.cpp index be394e19c4..8cb2cc4983 100644 --- a/scene/resources/dynamic_font_stb.cpp +++ b/scene/resources/dynamic_font_stb.cpp @@ -33,7 +33,7 @@ #ifndef FREETYPE_ENABLED #define STB_TRUETYPE_IMPLEMENTATION -#include "os/file_access.h" +#include "core/os/file_access.h" void DynamicFontData::lock() { diff --git a/scene/resources/dynamic_font_stb.h b/scene/resources/dynamic_font_stb.h index feae29c0c2..e1ef72ea4f 100644 --- a/scene/resources/dynamic_font_stb.h +++ b/scene/resources/dynamic_font_stb.h @@ -33,8 +33,8 @@ #ifndef FREETYPE_ENABLED +#include "core/io/resource_loader.h" #include "font.h" -#include "io/resource_loader.h" #include "thirdparty/misc/stb_truetype.h" diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp index d3da842b79..7b43c33692 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "environment.h" -#include "project_settings.h" +#include "core/project_settings.h" #include "servers/visual_server.h" #include "texture.h" @@ -961,7 +961,7 @@ void Environment::_bind_methods() { ClassDB::bind_method(D_METHOD("get_tonemap_auto_exposure_grey"), &Environment::get_tonemap_auto_exposure_grey); ADD_GROUP("Tonemap", "tonemap_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "tonemap_mode", PROPERTY_HINT_ENUM, "Linear,Reindhart,Filmic,Aces"), "set_tonemapper", "get_tonemapper"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "tonemap_mode", PROPERTY_HINT_ENUM, "Linear,Reinhard,Filmic,Aces"), "set_tonemapper", "get_tonemapper"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "tonemap_exposure", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_tonemap_exposure", "get_tonemap_exposure"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "tonemap_white", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_tonemap_white", "get_tonemap_white"); ADD_GROUP("Auto Exposure", "auto_exposure_"); diff --git a/scene/resources/environment.h b/scene/resources/environment.h index 7d66c7e60b..aab37719e0 100644 --- a/scene/resources/environment.h +++ b/scene/resources/environment.h @@ -31,7 +31,7 @@ #ifndef ENVIRONMENT_H #define ENVIRONMENT_H -#include "resource.h" +#include "core/resource.h" #include "scene/resources/sky_box.h" #include "scene/resources/texture.h" #include "servers/visual_server.h" diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index 3dfde01320..b78b3a6ffb 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -31,8 +31,8 @@ #include "font.h" #include "core/io/resource_loader.h" +#include "core/method_bind_ext.gen.inc" #include "core/os/file_access.h" -#include "method_bind_ext.gen.inc" void Font::draw_halign(RID p_canvas_item, const Point2 &p_pos, HAlign p_align, float p_width, const String &p_text, const Color &p_modulate, const Color &p_outline_modulate) const { float length = get_string_size(p_text).width; @@ -178,6 +178,7 @@ PoolVector<int> BitmapFont::_get_kernings() const { void BitmapFont::_set_textures(const Vector<Variant> &p_textures) { + textures.clear(); for (int i = 0; i < p_textures.size(); i++) { Ref<Texture> tex = p_textures[i]; ERR_CONTINUE(!tex.is_valid()); diff --git a/scene/resources/font.h b/scene/resources/font.h index 4e295b6035..39e66a822d 100644 --- a/scene/resources/font.h +++ b/scene/resources/font.h @@ -31,8 +31,8 @@ #ifndef FONT_H #define FONT_H -#include "map.h" -#include "resource.h" +#include "core/map.h" +#include "core/resource.h" #include "scene/resources/texture.h" /** @author Juan Linietsky <reduzio@gmail.com> diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index b6d1916b2c..a9d7b2adf7 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -145,6 +145,31 @@ void ShaderMaterial::_get_property_list(List<PropertyInfo> *p_list) const { } } +bool ShaderMaterial::property_can_revert(const String &p_name) { + if (shader.is_valid()) { + + StringName pr = shader->remap_param(p_name); + if (pr) { + Variant default_value = VisualServer::get_singleton()->material_get_param_default(_get_material(), pr); + Variant current_value; + _get(p_name, current_value); + return default_value.get_type() != Variant::NIL && default_value != current_value; + } + } + return false; +} + +Variant ShaderMaterial::property_get_revert(const String &p_name) { + Variant r_ret; + if (shader.is_valid()) { + StringName pr = shader->remap_param(p_name); + if (pr) { + r_ret = VisualServer::get_singleton()->material_get_param_default(_get_material(), pr); + } + } + return r_ret; +} + void ShaderMaterial::set_shader(const Ref<Shader> &p_shader) { if (shader.is_valid()) { @@ -190,6 +215,8 @@ void ShaderMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_shader_param", "param", "value"), &ShaderMaterial::set_shader_param); ClassDB::bind_method(D_METHOD("get_shader_param", "param"), &ShaderMaterial::get_shader_param); ClassDB::bind_method(D_METHOD("_shader_changed"), &ShaderMaterial::_shader_changed); + ClassDB::bind_method(D_METHOD("property_can_revert", "name"), &ShaderMaterial::property_can_revert); + ClassDB::bind_method(D_METHOD("property_get_revert", "name"), &ShaderMaterial::property_get_revert); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shader", PROPERTY_HINT_RESOURCE_TYPE, "Shader"), "set_shader", "get_shader"); } @@ -400,10 +427,8 @@ void SpatialMaterial::_update_shader() { if (flags[FLAG_USE_VERTEX_LIGHTING]) { code += ",vertex_lighting"; } - bool using_world = false; if (flags[FLAG_TRIPLANAR_USE_WORLD] && (flags[FLAG_UV1_USE_TRIPLANAR] || flags[FLAG_UV2_USE_TRIPLANAR])) { code += ",world_vertex_coords"; - using_world = true; } if (flags[FLAG_DONT_RECEIVE_SHADOWS]) { code += ",shadows_disabled"; @@ -535,7 +560,9 @@ void SpatialMaterial::_update_shader() { if (flags[FLAG_SRGB_VERTEX_COLOR]) { - code += "\tCOLOR.rgb = mix( pow((COLOR.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), COLOR.rgb* (1.0 / 12.92), lessThan(COLOR.rgb,vec3(0.04045)) );\n"; + code += "\tif (!OUTPUT_IS_SRGB) {\n"; + code += "\t\tCOLOR.rgb = mix( pow((COLOR.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), COLOR.rgb* (1.0 / 12.92), lessThan(COLOR.rgb,vec3(0.04045)) );\n"; + code += "\t}\n"; } if (flags[FLAG_USE_POINT_SIZE]) { @@ -583,11 +610,11 @@ void SpatialMaterial::_update_shader() { code += "\tMODELVIEW_MATRIX = INV_CAMERA_MATRIX * mat_world;\n"; //handle animation - code += "\tint particle_total_frames = particles_anim_h_frames * particles_anim_v_frames;\n"; - code += "\tint particle_frame = int(INSTANCE_CUSTOM.z * float(particle_total_frames));\n"; - 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 += "\tfloat particle_total_frames = float(particles_anim_h_frames * particles_anim_v_frames);\n"; + code += "\tfloat particle_frame = floor(INSTANCE_CUSTOM.z * float(particle_total_frames));\n"; + code += "\tif (!particles_anim_loop) particle_frame=clamp(particle_frame,0.0,particle_total_frames-1.0); else particle_frame=mod(particle_frame,float(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"; + code += "\tUV += vec2(mod(particle_frame,float(particles_anim_h_frames)) / float(particles_anim_h_frames), floor(particle_frame / float(particles_anim_h_frames)) / float(particles_anim_v_frames));\n"; } break; } @@ -790,7 +817,7 @@ void SpatialMaterial::_update_shader() { code += "\tALPHA = albedo.a * albedo_tex.a;\n"; } - if (proximity_fade_enabled) { + if (!VisualServer::get_singleton()->is_low_end() && proximity_fade_enabled) { code += "\tfloat depth_tex = textureLod(DEPTH_TEXTURE,SCREEN_UV,0.0).r;\n"; code += "\tvec4 world_pos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV*2.0-1.0,depth_tex*2.0-1.0,1.0);\n"; code += "\tworld_pos.xyz/=world_pos.w;\n"; @@ -798,43 +825,45 @@ void SpatialMaterial::_update_shader() { } if (distance_fade != DISTANCE_FADE_DISABLED) { - if (distance_fade == DISTANCE_FADE_OBJECT_DITHER || distance_fade == DISTANCE_FADE_PIXEL_DITHER) { - - code += "\t{\n"; - if (distance_fade == DISTANCE_FADE_OBJECT_DITHER) { - code += "\t\tfloat fade_distance = abs((INV_CAMERA_MATRIX * WORLD_MATRIX[3]).z);\n"; - - } else { - code += "\t\tfloat fade_distance=-VERTEX.z;\n"; + if ((distance_fade == DISTANCE_FADE_OBJECT_DITHER || distance_fade == DISTANCE_FADE_PIXEL_DITHER)) { + + if (!VisualServer::get_singleton()->is_low_end()) { + code += "\t{\n"; + if (distance_fade == DISTANCE_FADE_OBJECT_DITHER) { + code += "\t\tfloat fade_distance = abs((INV_CAMERA_MATRIX * WORLD_MATRIX[3]).z);\n"; + + } else { + code += "\t\tfloat fade_distance=-VERTEX.z;\n"; + } + + code += "\t\tfloat fade=clamp(smoothstep(distance_fade_min,distance_fade_max,fade_distance),0.0,1.0);\n"; + code += "\t\tint x = int(FRAGCOORD.x) % 4;\n"; + code += "\t\tint y = int(FRAGCOORD.y) % 4;\n"; + code += "\t\tint index = x + y * 4;\n"; + code += "\t\tfloat limit = 0.0;\n\n"; + code += "\t\tif (x < 8) {\n"; + code += "\t\t\tif (index == 0) limit = 0.0625;\n"; + code += "\t\t\tif (index == 1) limit = 0.5625;\n"; + code += "\t\t\tif (index == 2) limit = 0.1875;\n"; + code += "\t\t\tif (index == 3) limit = 0.6875;\n"; + code += "\t\t\tif (index == 4) limit = 0.8125;\n"; + code += "\t\t\tif (index == 5) limit = 0.3125;\n"; + code += "\t\t\tif (index == 6) limit = 0.9375;\n"; + code += "\t\t\tif (index == 7) limit = 0.4375;\n"; + code += "\t\t\tif (index == 8) limit = 0.25;\n"; + code += "\t\t\tif (index == 9) limit = 0.75;\n"; + code += "\t\t\tif (index == 10) limit = 0.125;\n"; + code += "\t\t\tif (index == 11) limit = 0.625;\n"; + code += "\t\t\tif (index == 12) limit = 1.0;\n"; + code += "\t\t\tif (index == 13) limit = 0.5;\n"; + code += "\t\t\tif (index == 14) limit = 0.875;\n"; + code += "\t\t\tif (index == 15) limit = 0.375;\n"; + code += "\t\t}\n\n"; + code += "\tif (fade < limit)\n"; + code += "\t\tdiscard;\n"; + code += "\t}\n\n"; } - code += "\t\tfloat fade=clamp(smoothstep(distance_fade_min,distance_fade_max,fade_distance),0.0,1.0);\n"; - code += "\t\tint x = int(FRAGCOORD.x) % 4;\n"; - code += "\t\tint y = int(FRAGCOORD.y) % 4;\n"; - code += "\t\tint index = x + y * 4;\n"; - code += "\t\tfloat limit = 0.0;\n\n"; - code += "\t\tif (x < 8) {\n"; - code += "\t\t\tif (index == 0) limit = 0.0625;\n"; - code += "\t\t\tif (index == 1) limit = 0.5625;\n"; - code += "\t\t\tif (index == 2) limit = 0.1875;\n"; - code += "\t\t\tif (index == 3) limit = 0.6875;\n"; - code += "\t\t\tif (index == 4) limit = 0.8125;\n"; - code += "\t\t\tif (index == 5) limit = 0.3125;\n"; - code += "\t\t\tif (index == 6) limit = 0.9375;\n"; - code += "\t\t\tif (index == 7) limit = 0.4375;\n"; - code += "\t\t\tif (index == 8) limit = 0.25;\n"; - code += "\t\t\tif (index == 9) limit = 0.75;\n"; - code += "\t\t\tif (index == 10) limit = 0.125;\n"; - code += "\t\t\tif (index == 11) limit = 0.625;\n"; - code += "\t\t\tif (index == 12) limit = 1.0;\n"; - code += "\t\t\tif (index == 13) limit = 0.5;\n"; - code += "\t\t\tif (index == 14) limit = 0.875;\n"; - code += "\t\t\tif (index == 15) limit = 0.375;\n"; - code += "\t\t}\n\n"; - code += "\tif (fade < limit)\n"; - code += "\t\tdiscard;\n"; - code += "\t}\n\n"; - } else { code += "\tALPHA*=clamp(smoothstep(distance_fade_min,distance_fade_max,-VERTEX.z),0.0,1.0);\n"; } @@ -924,7 +953,7 @@ void SpatialMaterial::_update_shader() { if (flags[FLAG_UV1_USE_TRIPLANAR]) { - code += "\tvec4 detail_mask_tex = triplanar_texture(texture_detail_mask,uv1_power_normal);\n"; + code += "\tvec4 detail_mask_tex = triplanar_texture(texture_detail_mask,uv1_power_normal,uv1_triplanar_pos);\n"; } else { code += "\tvec4 detail_mask_tex = texture(texture_detail_mask,base_uv);\n"; } @@ -1342,6 +1371,12 @@ void SpatialMaterial::_validate_feature(const String &text, Feature feature, Pro } } +void SpatialMaterial::_validate_high_end(const String &text, PropertyInfo &property) const { + if (property.name.begins_with(text)) { + property.usage |= PROPERTY_USAGE_HIGH_END_GFX; + } +} + void SpatialMaterial::_validate_property(PropertyInfo &property) const { _validate_feature("normal", FEATURE_NORMAL_MAPPING, property); _validate_feature("emission", FEATURE_EMISSION, property); @@ -1355,6 +1390,12 @@ void SpatialMaterial::_validate_property(PropertyInfo &property) const { _validate_feature("refraction", FEATURE_REFRACTION, property); _validate_feature("detail", FEATURE_DETAIL, property); + _validate_high_end("refraction", property); + _validate_high_end("subsurf_scatter", property); + _validate_high_end("anisotropy", property); + _validate_high_end("clearcoat", property); + _validate_high_end("depth", property); + if (property.name.begins_with("particles_anim_") && billboard_mode != BILLBOARD_PARTICLES) { property.usage = 0; } @@ -1500,13 +1541,13 @@ int SpatialMaterial::get_particles_anim_v_frames() const { return particles_anim_v_frames; } -void SpatialMaterial::set_particles_anim_loop(int p_frames) { +void SpatialMaterial::set_particles_anim_loop(bool p_loop) { - particles_anim_loop = p_frames; - VS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_loop, p_frames); + particles_anim_loop = p_loop; + VS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_loop, particles_anim_loop); } -int SpatialMaterial::get_particles_anim_loop() const { +bool SpatialMaterial::get_particles_anim_loop() const { return particles_anim_loop; } @@ -1857,7 +1898,7 @@ void SpatialMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_particles_anim_v_frames", "frames"), &SpatialMaterial::set_particles_anim_v_frames); ClassDB::bind_method(D_METHOD("get_particles_anim_v_frames"), &SpatialMaterial::get_particles_anim_v_frames); - ClassDB::bind_method(D_METHOD("set_particles_anim_loop", "frames"), &SpatialMaterial::set_particles_anim_loop); + ClassDB::bind_method(D_METHOD("set_particles_anim_loop", "loop"), &SpatialMaterial::set_particles_anim_loop); ClassDB::bind_method(D_METHOD("get_particles_anim_loop"), &SpatialMaterial::get_particles_anim_loop); ClassDB::bind_method(D_METHOD("set_depth_deep_parallax", "enable"), &SpatialMaterial::set_depth_deep_parallax); diff --git a/scene/resources/material.h b/scene/resources/material.h index f43d240a53..49b4a79d92 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -31,10 +31,10 @@ #ifndef MATERIAL_H #define MATERIAL_H -#include "resource.h" +#include "core/resource.h" +#include "core/self_list.h" #include "scene/resources/shader.h" #include "scene/resources/texture.h" -#include "self_list.h" #include "servers/visual/shader_language.h" #include "servers/visual_server.h" /** @@ -85,6 +85,8 @@ 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 property_can_revert(const String &p_name); + Variant property_get_revert(const String &p_name); static void _bind_methods(); @@ -284,7 +286,7 @@ private: mk.key = 0; for (int i = 0; i < FEATURE_MAX; i++) { if (features[i]) { - mk.feature_mask |= (1 << i); + mk.feature_mask |= ((uint64_t)1 << i); } } mk.detail_uv = detail_uv; @@ -293,7 +295,7 @@ private: mk.cull_mode = cull_mode; for (int i = 0; i < FLAG_MAX; i++) { if (flags[i]) { - mk.flags |= (1 << i); + mk.flags |= ((uint64_t)1 << i); } } mk.detail_blend_mode = detail_blend_mode; @@ -440,6 +442,8 @@ private: static Ref<SpatialMaterial> materials_for_2d[MAX_MATERIALS_FOR_2D]; //used by Sprite3D and other stuff + void _validate_high_end(const String &text, PropertyInfo &property) const; + protected: static void _bind_methods(); void _validate_property(PropertyInfo &property) const; @@ -570,8 +574,8 @@ public: void set_particles_anim_v_frames(int p_frames); int get_particles_anim_v_frames() const; - void set_particles_anim_loop(int p_frames); - int get_particles_anim_loop() const; + void set_particles_anim_loop(bool p_loop); + bool get_particles_anim_loop() const; void set_grow_enabled(bool p_enable); bool is_grow_enabled() const; diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index 042cf28fec..6cd701eb9a 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -30,18 +30,13 @@ #include "mesh.h" -#include "pair.h" +#include "core/pair.h" #include "scene/resources/concave_polygon_shape.h" #include "scene/resources/convex_polygon_shape.h" #include "surface_tool.h" #include <stdlib.h> -void Mesh::_clear_triangle_mesh() const { - - triangle_mesh.unref(); -} - Ref<TriangleMesh> Mesh::generate_triangle_mesh() const { if (triangle_mesh.is_valid()) @@ -111,6 +106,11 @@ Ref<TriangleMesh> Mesh::generate_triangle_mesh() const { void Mesh::generate_debug_mesh_lines(Vector<Vector3> &r_lines) { + if (debug_lines.size() > 0) { + r_lines = debug_lines; + return; + } + Ref<TriangleMesh> tm = generate_triangle_mesh(); if (tm.is_null()) return; @@ -120,23 +120,25 @@ void Mesh::generate_debug_mesh_lines(Vector<Vector3> &r_lines) { 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 + debug_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]]; + debug_lines.write[j + 0] = ver_r[ind_r[x + 0]]; + debug_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]]; + debug_lines.write[j + 2] = ver_r[ind_r[x + 1]]; + debug_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]]; + debug_lines.write[j + 4] = ver_r[ind_r[x + 2]]; + debug_lines.write[j + 5] = ver_r[ind_r[x + 0]]; } + + r_lines = debug_lines; } void Mesh::generate_debug_mesh_indices(Vector<Vector3> &r_points) { Ref<TriangleMesh> tm = generate_triangle_mesh(); @@ -482,6 +484,11 @@ void Mesh::_bind_methods() { ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "lightmap_size_hint"), "set_lightmap_size_hint", "get_lightmap_size_hint"); + ClassDB::bind_method(D_METHOD("get_surface_count"), &Mesh::get_surface_count); + ClassDB::bind_method(D_METHOD("surface_get_arrays", "surf_idx"), &Mesh::surface_get_arrays); + ClassDB::bind_method(D_METHOD("surface_get_blend_shape_arrays", "surf_idx"), &Mesh::surface_get_blend_shape_arrays); + ClassDB::bind_method(D_METHOD("surface_get_material", "surf_idx"), &Mesh::surface_get_material); + BIND_ENUM_CONSTANT(PRIMITIVE_POINTS); BIND_ENUM_CONSTANT(PRIMITIVE_LINES); BIND_ENUM_CONSTANT(PRIMITIVE_LINE_STRIP); @@ -531,53 +538,14 @@ void Mesh::_bind_methods() { BIND_ENUM_CONSTANT(ARRAY_MAX); } -void Mesh::clear_cache() { - _clear_triangle_mesh(); +void Mesh::clear_cache() const { + triangle_mesh.unref(); + debug_lines.clear(); } Mesh::Mesh() { } -static const char *_array_name[] = { - "vertex_array", - "normal_array", - "tangent_array", - "color_array", - "tex_uv_array", - "tex_uv2_array", - "bone_array", - "weights_array", - "index_array", - NULL -}; - -static const ArrayMesh::ArrayType _array_types[] = { - - ArrayMesh::ARRAY_VERTEX, - ArrayMesh::ARRAY_NORMAL, - ArrayMesh::ARRAY_TANGENT, - ArrayMesh::ARRAY_COLOR, - ArrayMesh::ARRAY_TEX_UV, - ArrayMesh::ARRAY_TEX_UV2, - ArrayMesh::ARRAY_BONES, - ArrayMesh::ARRAY_WEIGHTS, - ArrayMesh::ARRAY_INDEX -}; - -/* compatibility */ -static const int _format_translate[] = { - - ArrayMesh::ARRAY_FORMAT_VERTEX, - ArrayMesh::ARRAY_FORMAT_NORMAL, - ArrayMesh::ARRAY_FORMAT_TANGENT, - ArrayMesh::ARRAY_FORMAT_COLOR, - ArrayMesh::ARRAY_FORMAT_TEX_UV, - ArrayMesh::ARRAY_FORMAT_TEX_UV2, - ArrayMesh::ARRAY_FORMAT_BONES, - ArrayMesh::ARRAY_FORMAT_WEIGHTS, - ArrayMesh::ARRAY_FORMAT_INDEX, -}; - bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) { String sname = p_name; @@ -845,7 +813,7 @@ void ArrayMesh::add_surface_from_arrays(PrimitiveType p_primitive, const Array & _recompute_aabb(); } - _clear_triangle_mesh(); + clear_cache(); _change_notify(); emit_changed(); } @@ -924,7 +892,7 @@ void ArrayMesh::surface_remove(int p_idx) { VisualServer::get_singleton()->mesh_remove_surface(mesh, p_idx); surfaces.remove(p_idx); - _clear_triangle_mesh(); + clear_cache(); _recompute_aabb(); _change_notify(); emit_changed(); @@ -1030,7 +998,7 @@ void ArrayMesh::add_surface_from_mesh_data(const Geometry::MeshData &p_mesh_data else aabb.merge_with(s.aabb); - _clear_triangle_mesh(); + clear_cache(); surfaces.push_back(s); _change_notify(); @@ -1313,7 +1281,6 @@ void ArrayMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("get_blend_shape_mode"), &ArrayMesh::get_blend_shape_mode); ClassDB::bind_method(D_METHOD("add_surface_from_arrays", "primitive", "arrays", "blend_shapes", "compress_flags"), &ArrayMesh::add_surface_from_arrays, DEFVAL(Array()), DEFVAL(ARRAY_COMPRESS_DEFAULT)); - ClassDB::bind_method(D_METHOD("get_surface_count"), &ArrayMesh::get_surface_count); ClassDB::bind_method(D_METHOD("surface_remove", "surf_idx"), &ArrayMesh::surface_remove); ClassDB::bind_method(D_METHOD("surface_update_region", "surf_idx", "offset", "data"), &ArrayMesh::surface_update_region); ClassDB::bind_method(D_METHOD("surface_get_array_len", "surf_idx"), &ArrayMesh::surface_get_array_len); @@ -1321,12 +1288,9 @@ void ArrayMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("surface_get_format", "surf_idx"), &ArrayMesh::surface_get_format); 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); - ClassDB::bind_method(D_METHOD("surface_get_blend_shape_arrays", "surf_idx"), &ArrayMesh::surface_get_blend_shape_arrays); ClassDB::bind_method(D_METHOD("create_trimesh_shape"), &ArrayMesh::create_trimesh_shape); ClassDB::bind_method(D_METHOD("create_convex_shape"), &ArrayMesh::create_convex_shape); ClassDB::bind_method(D_METHOD("create_outline", "margin"), &ArrayMesh::create_outline); @@ -1374,6 +1338,7 @@ void ArrayMesh::reload_from_file() { VisualServer::get_singleton()->mesh_clear(mesh); surfaces.clear(); clear_blend_shapes(); + clear_cache(); Resource::reload_from_file(); diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h index 36bfca49f8..aebba09ef8 100644 --- a/scene/resources/mesh.h +++ b/scene/resources/mesh.h @@ -31,11 +31,11 @@ #ifndef MESH_H #define MESH_H -#include "resource.h" +#include "core/math/triangle_mesh.h" +#include "core/resource.h" #include "scene/resources/material.h" #include "scene/resources/shape.h" #include "servers/visual_server.h" -#include "triangle_mesh.h" /** @author Juan Linietsky <reduzio@gmail.com> */ @@ -44,11 +44,10 @@ class Mesh : public Resource { GDCLASS(Mesh, Resource); mutable Ref<TriangleMesh> triangle_mesh; //cached + mutable Vector<Vector3> debug_lines; Size2 lightmap_size_hint; protected: - void _clear_triangle_mesh() const; - static void _bind_methods(); public: @@ -146,7 +145,7 @@ public: void set_lightmap_size_hint(const Vector2 &p_size); Size2 get_lightmap_size_hint() const; - void clear_cache(); + void clear_cache() const; Mesh(); }; diff --git a/scene/resources/mesh_data_tool.cpp b/scene/resources/mesh_data_tool.cpp index 6732303925..7af9086ab7 100644 --- a/scene/resources/mesh_data_tool.cpp +++ b/scene/resources/mesh_data_tool.cpp @@ -42,8 +42,6 @@ void MeshDataTool::clear() { Error MeshDataTool::create_from_surface(const Ref<ArrayMesh> &p_mesh, int p_surface) { ERR_FAIL_COND_V(p_mesh.is_null(), ERR_INVALID_PARAMETER); - - ERR_FAIL_COND_V(p_mesh.is_null(), ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(p_mesh->surface_get_primitive_type(p_surface) != Mesh::PRIMITIVE_TRIANGLES, ERR_INVALID_PARAMETER); Array arrays = p_mesh->surface_get_arrays(p_surface); @@ -79,9 +77,9 @@ Error MeshDataTool::create_from_surface(const Ref<ArrayMesh> &p_mesh, int p_surf if (arrays[Mesh::ARRAY_COLOR].get_type() != Variant::NIL) col = arrays[Mesh::ARRAY_COLOR].operator PoolVector<Color>().read(); - PoolVector<real_t>::Read bo; + PoolVector<int>::Read bo; if (arrays[Mesh::ARRAY_BONES].get_type() != Variant::NIL) - bo = arrays[Mesh::ARRAY_BONES].operator PoolVector<real_t>().read(); + bo = arrays[Mesh::ARRAY_BONES].operator PoolVector<int>().read(); PoolVector<real_t>::Read we; if (arrays[Mesh::ARRAY_WEIGHTS].get_type() != Variant::NIL) @@ -194,7 +192,7 @@ Error MeshDataTool::commit_to_surface(const Ref<ArrayMesh> &p_mesh) { PoolVector<Vector2> u; PoolVector<Vector2> u2; PoolVector<Color> c; - PoolVector<real_t> b; + PoolVector<int> b; PoolVector<real_t> w; PoolVector<int> in; @@ -233,7 +231,7 @@ Error MeshDataTool::commit_to_surface(const Ref<ArrayMesh> &p_mesh) { col = c.write(); } - PoolVector<real_t>::Write bo; + PoolVector<int>::Write bo; if (format & Mesh::ARRAY_FORMAT_BONES) { b.resize(vcount * 4); bo = b.write(); diff --git a/scene/resources/mesh_library.h b/scene/resources/mesh_library.h index 69719960e2..3ce0cf9b66 100644 --- a/scene/resources/mesh_library.h +++ b/scene/resources/mesh_library.h @@ -31,9 +31,9 @@ #ifndef GRID_THEME_H #define GRID_THEME_H -#include "map.h" +#include "core/map.h" +#include "core/resource.h" #include "mesh.h" -#include "resource.h" #include "scene/3d/navigation_mesh.h" #include "shape.h" diff --git a/scene/resources/multimesh.cpp b/scene/resources/multimesh.cpp index 4d0a14e3aa..e8e19fdb60 100644 --- a/scene/resources/multimesh.cpp +++ b/scene/resources/multimesh.cpp @@ -33,8 +33,6 @@ void MultiMesh::_set_transform_array(const PoolVector<Vector3> &p_array) { - int instance_count = get_instance_count(); - PoolVector<Vector3> xforms = p_array; int len = xforms.size(); ERR_FAIL_COND((len / 4) != instance_count); @@ -57,8 +55,6 @@ void MultiMesh::_set_transform_array(const PoolVector<Vector3> &p_array) { PoolVector<Vector3> MultiMesh::_get_transform_array() const { - int instance_count = get_instance_count(); - if (instance_count == 0) return PoolVector<Vector3>(); @@ -81,13 +77,11 @@ PoolVector<Vector3> MultiMesh::_get_transform_array() const { void MultiMesh::_set_color_array(const PoolVector<Color> &p_array) { - int instance_count = get_instance_count(); - PoolVector<Color> colors = p_array; int len = colors.size(); - ERR_FAIL_COND(len != instance_count); if (len == 0) return; + ERR_FAIL_COND(len != instance_count); PoolVector<Color>::Read r = colors.read(); @@ -99,9 +93,7 @@ void MultiMesh::_set_color_array(const PoolVector<Color> &p_array) { PoolVector<Color> MultiMesh::_get_color_array() const { - int instance_count = get_instance_count(); - - if (instance_count == 0) + if (instance_count == 0 || color_format == COLOR_NONE) return PoolVector<Color>(); PoolVector<Color> colors; @@ -115,6 +107,37 @@ PoolVector<Color> MultiMesh::_get_color_array() const { return colors; } +void MultiMesh::_set_custom_data_array(const PoolVector<Color> &p_array) { + + PoolVector<Color> custom_datas = p_array; + int len = custom_datas.size(); + if (len == 0) + return; + ERR_FAIL_COND(len != instance_count); + + PoolVector<Color>::Read r = custom_datas.read(); + + for (int i = 0; i < len; i++) { + + set_instance_custom_data(i, r[i]); + } +} + +PoolVector<Color> MultiMesh::_get_custom_data_array() const { + + if (instance_count == 0 || custom_data_format == CUSTOM_DATA_NONE) + return PoolVector<Color>(); + + PoolVector<Color> custom_datas; + custom_datas.resize(instance_count); + + for (int i = 0; i < instance_count; i++) { + + custom_datas.set(i, get_instance_custom_data(i)); + } + + return custom_datas; +} void MultiMesh::set_mesh(const Ref<Mesh> &p_mesh) { mesh = p_mesh; @@ -130,12 +153,13 @@ Ref<Mesh> MultiMesh::get_mesh() const { } void MultiMesh::set_instance_count(int p_count) { - - VisualServer::get_singleton()->multimesh_allocate(multimesh, p_count, VS::MultimeshTransformFormat(transform_format), VS::MultimeshColorFormat(color_format)); + ERR_FAIL_COND(p_count < 0); + VisualServer::get_singleton()->multimesh_allocate(multimesh, p_count, VS::MultimeshTransformFormat(transform_format), VS::MultimeshColorFormat(color_format), VS::MultimeshCustomDataFormat(custom_data_format)); + instance_count = p_count; } int MultiMesh::get_instance_count() const { - return VisualServer::get_singleton()->multimesh_get_instance_count(multimesh); + return instance_count; } void MultiMesh::set_instance_transform(int p_instance, const Transform &p_transform) { @@ -156,6 +180,15 @@ Color MultiMesh::get_instance_color(int p_instance) const { return VisualServer::get_singleton()->multimesh_instance_get_color(multimesh, p_instance); } +void MultiMesh::set_instance_custom_data(int p_instance, const Color &p_custom_data) { + + VisualServer::get_singleton()->multimesh_instance_set_custom_data(multimesh, p_instance, p_custom_data); +} +Color MultiMesh::get_instance_custom_data(int p_instance) const { + + return VisualServer::get_singleton()->multimesh_instance_get_custom_data(multimesh, p_instance); +} + AABB MultiMesh::get_aabb() const { return VisualServer::get_singleton()->multimesh_get_aabb(multimesh); @@ -168,6 +201,7 @@ RID MultiMesh::get_rid() const { void MultiMesh::set_color_format(ColorFormat p_color_format) { + ERR_FAIL_COND(instance_count > 0); color_format = p_color_format; } @@ -176,8 +210,20 @@ MultiMesh::ColorFormat MultiMesh::get_color_format() const { return color_format; } +void MultiMesh::set_custom_data_format(CustomDataFormat p_custom_data_format) { + + ERR_FAIL_COND(instance_count > 0); + custom_data_format = p_custom_data_format; +} + +MultiMesh::CustomDataFormat MultiMesh::get_custom_data_format() const { + + return custom_data_format; +} + void MultiMesh::set_transform_format(TransformFormat p_transform_format) { + ERR_FAIL_COND(instance_count > 0); transform_format = p_transform_format; } MultiMesh::TransformFormat MultiMesh::get_transform_format() const { @@ -191,6 +237,8 @@ void MultiMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("get_mesh"), &MultiMesh::get_mesh); ClassDB::bind_method(D_METHOD("set_color_format", "format"), &MultiMesh::set_color_format); ClassDB::bind_method(D_METHOD("get_color_format"), &MultiMesh::get_color_format); + ClassDB::bind_method(D_METHOD("set_custom_data_format", "format"), &MultiMesh::set_custom_data_format); + ClassDB::bind_method(D_METHOD("get_custom_data_format"), &MultiMesh::get_custom_data_format); ClassDB::bind_method(D_METHOD("set_transform_format", "format"), &MultiMesh::set_transform_format); ClassDB::bind_method(D_METHOD("get_transform_format"), &MultiMesh::get_transform_format); @@ -200,19 +248,25 @@ void MultiMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("get_instance_transform", "instance"), &MultiMesh::get_instance_transform); ClassDB::bind_method(D_METHOD("set_instance_color", "instance", "color"), &MultiMesh::set_instance_color); ClassDB::bind_method(D_METHOD("get_instance_color", "instance"), &MultiMesh::get_instance_color); + ClassDB::bind_method(D_METHOD("set_instance_custom_data", "instance", "custom_data"), &MultiMesh::set_instance_custom_data); + ClassDB::bind_method(D_METHOD("get_instance_custom_data", "instance"), &MultiMesh::get_instance_custom_data); ClassDB::bind_method(D_METHOD("get_aabb"), &MultiMesh::get_aabb); ClassDB::bind_method(D_METHOD("_set_transform_array"), &MultiMesh::_set_transform_array); ClassDB::bind_method(D_METHOD("_get_transform_array"), &MultiMesh::_get_transform_array); ClassDB::bind_method(D_METHOD("_set_color_array"), &MultiMesh::_set_color_array); ClassDB::bind_method(D_METHOD("_get_color_array"), &MultiMesh::_get_color_array); + ClassDB::bind_method(D_METHOD("_set_custom_data_array"), &MultiMesh::_set_custom_data_array); + ClassDB::bind_method(D_METHOD("_get_custom_data_array"), &MultiMesh::_get_custom_data_array); ADD_PROPERTY(PropertyInfo(Variant::INT, "color_format", PROPERTY_HINT_ENUM, "None,Byte,Float"), "set_color_format", "get_color_format"); ADD_PROPERTY(PropertyInfo(Variant::INT, "transform_format", PROPERTY_HINT_ENUM, "2D,3D"), "set_transform_format", "get_transform_format"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "instance_count", PROPERTY_HINT_RANGE, "0,16384,1"), "set_instance_count", "get_instance_count"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "custom_data_format", PROPERTY_HINT_ENUM, "None,Byte,Float"), "set_custom_data_format", "get_custom_data_format"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "instance_count", PROPERTY_HINT_RANGE, "0,16384,1,or_greater"), "set_instance_count", "get_instance_count"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh"); ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR3_ARRAY, "transform_array", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_transform_array", "_get_transform_array"); ADD_PROPERTY(PropertyInfo(Variant::POOL_COLOR_ARRAY, "color_array", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_color_array", "_get_color_array"); + ADD_PROPERTY(PropertyInfo(Variant::POOL_COLOR_ARRAY, "custom_data_array", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_custom_data_array", "_get_custom_data_array"); BIND_ENUM_CONSTANT(TRANSFORM_2D); BIND_ENUM_CONSTANT(TRANSFORM_3D); @@ -220,13 +274,19 @@ void MultiMesh::_bind_methods() { BIND_ENUM_CONSTANT(COLOR_NONE); BIND_ENUM_CONSTANT(COLOR_8BIT); BIND_ENUM_CONSTANT(COLOR_FLOAT); + + BIND_ENUM_CONSTANT(CUSTOM_DATA_NONE); + BIND_ENUM_CONSTANT(CUSTOM_DATA_8BIT); + BIND_ENUM_CONSTANT(CUSTOM_DATA_FLOAT); } MultiMesh::MultiMesh() { multimesh = VisualServer::get_singleton()->multimesh_create(); color_format = COLOR_NONE; + custom_data_format = CUSTOM_DATA_NONE; transform_format = TRANSFORM_2D; + instance_count = 0; } MultiMesh::~MultiMesh() { diff --git a/scene/resources/multimesh.h b/scene/resources/multimesh.h index 0875d6d06d..ec5a5ca4d5 100644 --- a/scene/resources/multimesh.h +++ b/scene/resources/multimesh.h @@ -51,11 +51,19 @@ public: COLOR_FLOAT = VS::MULTIMESH_COLOR_FLOAT, }; + enum CustomDataFormat { + CUSTOM_DATA_NONE, + CUSTOM_DATA_8BIT, + CUSTOM_DATA_FLOAT, + }; + private: Ref<Mesh> mesh; RID multimesh; TransformFormat transform_format; ColorFormat color_format; + CustomDataFormat custom_data_format; + int instance_count; protected: static void _bind_methods(); @@ -66,6 +74,9 @@ protected: void _set_color_array(const PoolVector<Color> &p_array); PoolVector<Color> _get_color_array() const; + void _set_custom_data_array(const PoolVector<Color> &p_array); + PoolVector<Color> _get_custom_data_array() const; + public: void set_mesh(const Ref<Mesh> &p_mesh); Ref<Mesh> get_mesh() const; @@ -73,6 +84,9 @@ public: void set_color_format(ColorFormat p_color_format); ColorFormat get_color_format() const; + void set_custom_data_format(CustomDataFormat p_custom_data_format); + CustomDataFormat get_custom_data_format() const; + void set_transform_format(TransformFormat p_transform_format); TransformFormat get_transform_format() const; @@ -85,6 +99,9 @@ public: void set_instance_color(int p_instance, const Color &p_color); Color get_instance_color(int p_instance) const; + void set_instance_custom_data(int p_instance, const Color &p_custom_data); + Color get_instance_custom_data(int p_instance) const; + virtual AABB get_aabb() const; virtual RID get_rid() const; @@ -95,5 +112,6 @@ public: VARIANT_ENUM_CAST(MultiMesh::TransformFormat); VARIANT_ENUM_CAST(MultiMesh::ColorFormat); +VARIANT_ENUM_CAST(MultiMesh::CustomDataFormat); #endif // MULTI_MESH_H diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index f034e07ff9..42ce9d10cd 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -31,9 +31,9 @@ #include "packed_scene.h" #include "core/core_string_names.h" -#include "engine.h" -#include "io/resource_loader.h" -#include "project_settings.h" +#include "core/engine.h" +#include "core/io/resource_loader.h" +#include "core/project_settings.h" #include "scene/2d/node_2d.h" #include "scene/3d/spatial.h" #include "scene/gui/control.h" @@ -264,7 +264,7 @@ Node *SceneState::instance(GenEditState p_edit_state) const { } if (n.instance >= 0 || n.type != TYPE_INSTANCED || i == 0) { - //if node was not part of instance, must set it's name, parenthood and ownership + //if node was not part of instance, must set its name, parenthood and ownership if (i > 0) { if (parent) { parent->_add_child_nocheck(node, snames[n.name]); diff --git a/scene/resources/packed_scene.h b/scene/resources/packed_scene.h index 278986eb62..e5f22f5e3b 100644 --- a/scene/resources/packed_scene.h +++ b/scene/resources/packed_scene.h @@ -31,7 +31,7 @@ #ifndef PACKED_SCENE_H #define PACKED_SCENE_H -#include "resource.h" +#include "core/resource.h" #include "scene/main/node.h" class SceneState : public Reference { diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp new file mode 100644 index 0000000000..d500609c43 --- /dev/null +++ b/scene/resources/particles_material.cpp @@ -0,0 +1,1255 @@ +/*************************************************************************/ +/* particles_material.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 "particles_material.h" + +Mutex *ParticlesMaterial::material_mutex = NULL; +SelfList<ParticlesMaterial>::List ParticlesMaterial::dirty_materials; +Map<ParticlesMaterial::MaterialKey, ParticlesMaterial::ShaderData> ParticlesMaterial::shader_map; +ParticlesMaterial::ShaderNames *ParticlesMaterial::shader_names = NULL; + +void ParticlesMaterial::init_shaders() { + +#ifndef NO_THREADS + material_mutex = Mutex::create(); +#endif + + shader_names = memnew(ShaderNames); + + shader_names->spread = "spread"; + shader_names->flatness = "flatness"; + shader_names->initial_linear_velocity = "initial_linear_velocity"; + shader_names->initial_angle = "initial_angle"; + shader_names->angular_velocity = "angular_velocity"; + shader_names->orbit_velocity = "orbit_velocity"; + shader_names->linear_accel = "linear_accel"; + shader_names->radial_accel = "radial_accel"; + shader_names->tangent_accel = "tangent_accel"; + shader_names->damping = "damping"; + shader_names->scale = "scale"; + shader_names->hue_variation = "hue_variation"; + shader_names->anim_speed = "anim_speed"; + shader_names->anim_offset = "anim_offset"; + + shader_names->initial_linear_velocity_random = "initial_linear_velocity_random"; + shader_names->initial_angle_random = "initial_angle_random"; + shader_names->angular_velocity_random = "angular_velocity_random"; + shader_names->orbit_velocity_random = "orbit_velocity_random"; + shader_names->linear_accel_random = "linear_accel_random"; + shader_names->radial_accel_random = "radial_accel_random"; + shader_names->tangent_accel_random = "tangent_accel_random"; + shader_names->damping_random = "damping_random"; + shader_names->scale_random = "scale_random"; + shader_names->hue_variation_random = "hue_variation_random"; + shader_names->anim_speed_random = "anim_speed_random"; + shader_names->anim_offset_random = "anim_offset_random"; + + shader_names->angle_texture = "angle_texture"; + shader_names->angular_velocity_texture = "angular_velocity_texture"; + shader_names->orbit_velocity_texture = "orbit_velocity_texture"; + shader_names->linear_accel_texture = "linear_accel_texture"; + shader_names->radial_accel_texture = "radial_accel_texture"; + shader_names->tangent_accel_texture = "tangent_accel_texture"; + shader_names->damping_texture = "damping_texture"; + shader_names->scale_texture = "scale_texture"; + shader_names->hue_variation_texture = "hue_variation_texture"; + shader_names->anim_speed_texture = "anim_speed_texture"; + shader_names->anim_offset_texture = "anim_offset_texture"; + + shader_names->color = "color_value"; + shader_names->color_ramp = "color_ramp"; + + shader_names->emission_sphere_radius = "emission_sphere_radius"; + shader_names->emission_box_extents = "emission_box_extents"; + shader_names->emission_texture_point_count = "emission_texture_point_count"; + shader_names->emission_texture_points = "emission_texture_points"; + shader_names->emission_texture_normal = "emission_texture_normal"; + shader_names->emission_texture_color = "emission_texture_color"; + + shader_names->trail_divisor = "trail_divisor"; + shader_names->trail_size_modifier = "trail_size_modifier"; + shader_names->trail_color_modifier = "trail_color_modifier"; + + shader_names->gravity = "gravity"; +} + +void ParticlesMaterial::finish_shaders() { + +#ifndef NO_THREADS + memdelete(material_mutex); +#endif + + memdelete(shader_names); +} + +void ParticlesMaterial::_update_shader() { + + dirty_materials.remove(&element); + + MaterialKey mk = _compute_key(); + if (mk.key == current_key.key) + return; //no update required in the end + + if (shader_map.has(current_key)) { + shader_map[current_key].users--; + if (shader_map[current_key].users == 0) { + //deallocate shader, as it's no longer in use + VS::get_singleton()->free(shader_map[current_key].shader); + shader_map.erase(current_key); + } + } + + current_key = mk; + + if (shader_map.has(mk)) { + + VS::get_singleton()->material_set_shader(_get_material(), shader_map[mk].shader); + shader_map[mk].users++; + return; + } + + //must create a shader! + + String code = "shader_type particles;\n"; + + code += "uniform float spread;\n"; + code += "uniform float flatness;\n"; + code += "uniform float initial_linear_velocity;\n"; + code += "uniform float initial_angle;\n"; + code += "uniform float angular_velocity;\n"; + code += "uniform float orbit_velocity;\n"; + code += "uniform float linear_accel;\n"; + code += "uniform float radial_accel;\n"; + code += "uniform float tangent_accel;\n"; + code += "uniform float damping;\n"; + code += "uniform float scale;\n"; + code += "uniform float hue_variation;\n"; + code += "uniform float anim_speed;\n"; + code += "uniform float anim_offset;\n"; + + code += "uniform float initial_linear_velocity_random;\n"; + code += "uniform float initial_angle_random;\n"; + code += "uniform float angular_velocity_random;\n"; + code += "uniform float orbit_velocity_random;\n"; + code += "uniform float linear_accel_random;\n"; + code += "uniform float radial_accel_random;\n"; + code += "uniform float tangent_accel_random;\n"; + code += "uniform float damping_random;\n"; + code += "uniform float scale_random;\n"; + code += "uniform float hue_variation_random;\n"; + code += "uniform float anim_speed_random;\n"; + code += "uniform float anim_offset_random;\n"; + + switch (emission_shape) { + case EMISSION_SHAPE_POINT: { + //do none + } break; + case EMISSION_SHAPE_SPHERE: { + code += "uniform float emission_sphere_radius;\n"; + } break; + case EMISSION_SHAPE_BOX: { + code += "uniform vec3 emission_box_extents;\n"; + } break; + case EMISSION_SHAPE_DIRECTED_POINTS: { + code += "uniform sampler2D emission_texture_normal : hint_black;\n"; + } //fallthrough + case EMISSION_SHAPE_POINTS: { + code += "uniform sampler2D emission_texture_points : hint_black;\n"; + code += "uniform int emission_texture_point_count;\n"; + if (emission_color_texture.is_valid()) { + code += "uniform sampler2D emission_texture_color : hint_white;\n"; + } + } break; + } + + code += "uniform vec4 color_value : hint_color;\n"; + + code += "uniform int trail_divisor;\n"; + + code += "uniform vec3 gravity;\n"; + + if (color_ramp.is_valid()) + code += "uniform sampler2D color_ramp;\n"; + + if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) + code += "uniform sampler2D linear_velocity_texture;\n"; + if (tex_parameters[PARAM_ORBIT_VELOCITY].is_valid()) + code += "uniform sampler2D orbit_velocity_texture;\n"; + if (tex_parameters[PARAM_ANGULAR_VELOCITY].is_valid()) + code += "uniform sampler2D angular_velocity_texture;\n"; + if (tex_parameters[PARAM_LINEAR_ACCEL].is_valid()) + code += "uniform sampler2D linear_accel_texture;\n"; + if (tex_parameters[PARAM_RADIAL_ACCEL].is_valid()) + code += "uniform sampler2D radial_accel_texture;\n"; + if (tex_parameters[PARAM_TANGENTIAL_ACCEL].is_valid()) + code += "uniform sampler2D tangent_accel_texture;\n"; + if (tex_parameters[PARAM_DAMPING].is_valid()) + code += "uniform sampler2D damping_texture;\n"; + if (tex_parameters[PARAM_ANGLE].is_valid()) + code += "uniform sampler2D angle_texture;\n"; + if (tex_parameters[PARAM_SCALE].is_valid()) + code += "uniform sampler2D scale_texture;\n"; + if (tex_parameters[PARAM_HUE_VARIATION].is_valid()) + code += "uniform sampler2D hue_variation_texture;\n"; + if (tex_parameters[PARAM_ANIM_SPEED].is_valid()) + code += "uniform sampler2D anim_speed_texture;\n"; + if (tex_parameters[PARAM_ANIM_OFFSET].is_valid()) + code += "uniform sampler2D anim_offset_texture;\n"; + + if (trail_size_modifier.is_valid()) { + code += "uniform sampler2D trail_size_modifier;\n"; + } + + if (trail_color_modifier.is_valid()) { + code += "uniform sampler2D trail_color_modifier;\n"; + } + + //need a random function + code += "\n\n"; + code += "float rand_from_seed(inout uint seed) {\n"; + code += " int k;\n"; + code += " int s = int(seed);\n"; + code += " if (s == 0)\n"; + code += " s = 305420679;\n"; + code += " k = s / 127773;\n"; + code += " s = 16807 * (s - k * 127773) - 2836 * k;\n"; + code += " if (s < 0)\n"; + code += " s += 2147483647;\n"; + code += " seed = uint(s);\n"; + code += " return float(seed % uint(65536)) / 65535.0;\n"; + code += "}\n"; + code += "\n"; + + code += "float rand_from_seed_m1_p1(inout uint seed) {\n"; + code += " return rand_from_seed(seed) * 2.0 - 1.0;\n"; + code += "}\n"; + code += "\n"; + + //improve seed quality + code += "uint hash(uint x) {\n"; + code += " x = ((x >> uint(16)) ^ x) * uint(73244475);\n"; + code += " x = ((x >> uint(16)) ^ x) * uint(73244475);\n"; + code += " x = (x >> uint(16)) ^ x;\n"; + code += " return x;\n"; + code += "}\n"; + code += "\n"; + + code += "void vertex() {\n"; + code += " uint base_number = NUMBER / uint(trail_divisor);\n"; + code += " uint alt_seed = hash(base_number + uint(1) + RANDOM_SEED);\n"; + code += " float angle_rand = rand_from_seed(alt_seed);\n"; + code += " float scale_rand = rand_from_seed(alt_seed);\n"; + code += " float hue_rot_rand = rand_from_seed(alt_seed);\n"; + code += " float anim_offset_rand = rand_from_seed(alt_seed);\n"; + code += " float pi = 3.14159;\n"; + code += " float degree_to_rad = pi / 180.0;\n"; + code += "\n"; + + if (emission_shape >= EMISSION_SHAPE_POINTS) { + code += " int point = min(emission_texture_point_count - 1, int(rand_from_seed(alt_seed) * float(emission_texture_point_count)));\n"; + code += " ivec2 emission_tex_size = textureSize(emission_texture_points, 0);\n"; + code += " ivec2 emission_tex_ofs = ivec2(point % emission_tex_size.x, point / emission_tex_size.x);\n"; + } + code += " if (RESTART) {\n"; + + if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) + code += " float tex_linear_velocity = textureLod(linear_velocity_texture, vec2(0.0, 0.0), 0.0).r;\n"; + else + code += " float tex_linear_velocity = 0.0;\n"; + + if (tex_parameters[PARAM_ANGLE].is_valid()) + code += " float tex_angle = textureLod(angle_texture, vec2(0.0, 0.0), 0.0).r;\n"; + else + code += " float tex_angle = 0.0;\n"; + + if (tex_parameters[PARAM_ANIM_OFFSET].is_valid()) + code += " float tex_anim_offset = textureLod(anim_offset_texture, vec2(0.0, 0.0), 0.0).r;\n"; + else + code += " float tex_anim_offset = 0.0;\n"; + + code += " float spread_rad = spread * degree_to_rad;\n"; + + if (flags[FLAG_DISABLE_Z]) { + + code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n"; + code += " vec3 rot = vec3(cos(angle1_rad), sin(angle1_rad), 0.0);\n"; + code += " VELOCITY = rot * initial_linear_velocity * mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n"; + + } else { + //initiate velocity spread in 3D + code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad;\n"; + code += " float angle2_rad = rand_from_seed_m1_p1(alt_seed) * spread_rad * (1.0 - flatness);\n"; + code += " vec3 direction_xz = vec3(sin(angle1_rad), 0, cos(angle1_rad));\n"; + code += " vec3 direction_yz = vec3(0, sin(angle2_rad), cos(angle2_rad));\n"; + code += " direction_yz.z = direction_yz.z / max(0.0001,sqrt(abs(direction_yz.z))); // better uniform distribution\n"; + code += " vec3 direction = vec3(direction_xz.x * direction_yz.z, direction_yz.y, direction_xz.z * direction_yz.z);\n"; + code += " direction = normalize(direction);\n"; + code += " VELOCITY = direction * initial_linear_velocity * mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n"; + } + + code += " float base_angle = (initial_angle + tex_angle) * mix(1.0, angle_rand, initial_angle_random);\n"; + code += " CUSTOM.x = base_angle * degree_to_rad;\n"; // angle + code += " CUSTOM.y = 0.0;\n"; // phase + code += " CUSTOM.z = (anim_offset + tex_anim_offset) * mix(1.0, anim_offset_rand, anim_offset_random);\n"; // animation offset (0-1) + + switch (emission_shape) { + case EMISSION_SHAPE_POINT: { + //do none + } break; + case EMISSION_SHAPE_SPHERE: { + code += " TRANSFORM[3].xyz = normalize(vec3(rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0 - 1.0)) * emission_sphere_radius;\n"; + } break; + case EMISSION_SHAPE_BOX: { + code += " TRANSFORM[3].xyz = vec3(rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0 - 1.0, rand_from_seed(alt_seed) * 2.0 - 1.0) * emission_box_extents;\n"; + } break; + case EMISSION_SHAPE_POINTS: + case EMISSION_SHAPE_DIRECTED_POINTS: { + code += " TRANSFORM[3].xyz = texelFetch(emission_texture_points, emission_tex_ofs, 0).xyz;\n"; + + if (emission_shape == EMISSION_SHAPE_DIRECTED_POINTS) { + if (flags[FLAG_DISABLE_Z]) { + + code += " mat2 rotm;"; + code += " rotm[0] = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xy;\n"; + code += " rotm[1] = rotm[0].yx * vec2(1.0, -1.0);\n"; + code += " VELOCITY.xy = rotm * VELOCITY.xy;\n"; + } else { + code += " vec3 normal = texelFetch(emission_texture_normal, emission_tex_ofs, 0).xyz;\n"; + code += " vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0, 1.0, 0.0);\n"; + code += " vec3 tangent = normalize(cross(v0, normal));\n"; + code += " vec3 bitangent = normalize(cross(tangent, normal));\n"; + code += " VELOCITY = mat3(tangent, bitangent, normal) * VELOCITY;\n"; + } + } + } break; + } + code += " VELOCITY = (EMISSION_TRANSFORM * vec4(VELOCITY, 0.0)).xyz;\n"; + code += " TRANSFORM = EMISSION_TRANSFORM * TRANSFORM;\n"; + if (flags[FLAG_DISABLE_Z]) { + code += " VELOCITY.z = 0.0;\n"; + code += " TRANSFORM[3].z = 0.0;\n"; + } + + code += " } else {\n"; + + code += " CUSTOM.y += DELTA / LIFETIME;\n"; + if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) + code += " float tex_linear_velocity = textureLod(linear_velocity_texture, vec2(CUSTOM.y, 0.0), 0.0).r;\n"; + else + code += " float tex_linear_velocity = 0.0;\n"; + + if (flags[FLAG_DISABLE_Z]) { + + if (tex_parameters[PARAM_ORBIT_VELOCITY].is_valid()) + code += " float tex_orbit_velocity = textureLod(orbit_velocity_texture, vec2(CUSTOM.y, 0.0), 0.0).r;\n"; + else + code += " float tex_orbit_velocity = 0.0;\n"; + } + + if (tex_parameters[PARAM_ANGULAR_VELOCITY].is_valid()) + code += " float tex_angular_velocity = textureLod(angular_velocity_texture, vec2(CUSTOM.y, 0.0), 0.0).r;\n"; + else + code += " float tex_angular_velocity = 0.0;\n"; + + if (tex_parameters[PARAM_LINEAR_ACCEL].is_valid()) + code += " float tex_linear_accel = textureLod(linear_accel_texture, vec2(CUSTOM.y, 0.0), 0.0).r;\n"; + else + code += " float tex_linear_accel = 0.0;\n"; + + if (tex_parameters[PARAM_RADIAL_ACCEL].is_valid()) + code += " float tex_radial_accel = textureLod(radial_accel_texture, vec2(CUSTOM.y, 0.0), 0.0).r;\n"; + else + code += " float tex_radial_accel = 0.0;\n"; + + if (tex_parameters[PARAM_TANGENTIAL_ACCEL].is_valid()) + code += " float tex_tangent_accel = textureLod(tangent_accel_texture, vec2(CUSTOM.y, 0.0), 0.0).r;\n"; + else + code += " float tex_tangent_accel = 0.0;\n"; + + if (tex_parameters[PARAM_DAMPING].is_valid()) + code += " float tex_damping = textureLod(damping_texture, vec2(CUSTOM.y, 0.0), 0.0).r;\n"; + else + code += " float tex_damping = 0.0;\n"; + + if (tex_parameters[PARAM_ANGLE].is_valid()) + code += " float tex_angle = textureLod(angle_texture, vec2(CUSTOM.y, 0.0), 0.0).r;\n"; + else + code += " float tex_angle = 0.0;\n"; + + if (tex_parameters[PARAM_ANIM_SPEED].is_valid()) + code += " float tex_anim_speed = textureLod(anim_speed_texture, vec2(CUSTOM.y, 0.0), 0.0).r;\n"; + else + code += " float tex_anim_speed = 0.0;\n"; + + if (tex_parameters[PARAM_ANIM_OFFSET].is_valid()) + code += " float tex_anim_offset = textureLod(anim_offset_texture, vec2(CUSTOM.y, 0.0), 0.0).r;\n"; + else + code += " float tex_anim_offset = 0.0;\n"; + + code += " vec3 force = gravity;\n"; + code += " vec3 pos = TRANSFORM[3].xyz;\n"; + if (flags[FLAG_DISABLE_Z]) { + code += " pos.z = 0.0;\n"; + } + code += " // apply linear acceleration\n"; + code += " force += length(VELOCITY) > 0.0 ? normalize(VELOCITY) * (linear_accel + tex_linear_accel) * mix(1.0, rand_from_seed(alt_seed), linear_accel_random) : vec3(0.0);\n"; + code += " // apply radial acceleration\n"; + code += " vec3 org = EMISSION_TRANSFORM[3].xyz;\n"; + code += " vec3 diff = pos - org;\n"; + code += " force += length(diff) > 0.0 ? normalize(diff) * (radial_accel + tex_radial_accel) * mix(1.0, rand_from_seed(alt_seed), radial_accel_random) : vec3(0.0);\n"; + code += " // apply tangential acceleration;\n"; + if (flags[FLAG_DISABLE_Z]) { + code += " force += length(diff.yx) > 0.0 ? vec3(normalize(diff.yx * vec2(-1.0, 1.0)), 0.0) * ((tangent_accel + tex_tangent_accel) * mix(1.0, rand_from_seed(alt_seed), tangent_accel_random)) : vec3(0.0);\n"; + + } else { + code += " vec3 crossDiff = cross(normalize(diff), normalize(gravity));\n"; + code += " force += length(crossDiff) > 0.0 ? normalize(crossDiff) * ((tangent_accel + tex_tangent_accel) * mix(1.0, rand_from_seed(alt_seed), tangent_accel_random)) : vec3(0.0);\n"; + } + code += " // apply attractor forces\n"; + code += " VELOCITY += force * DELTA;\n"; + code += " // orbit velocity\n"; + if (flags[FLAG_DISABLE_Z]) { + + code += " float orbit_amount = (orbit_velocity + tex_orbit_velocity) * mix(1.0, rand_from_seed(alt_seed), orbit_velocity_random);\n"; + code += " if (orbit_amount != 0.0) {\n"; + code += " float ang = orbit_amount * DELTA * pi * 2.0;\n"; + code += " mat2 rot = mat2(vec2(cos(ang), -sin(ang)), vec2(sin(ang), cos(ang)));\n"; + code += " TRANSFORM[3].xy -= diff.xy;\n"; + code += " TRANSFORM[3].xy += rot * diff.xy;\n"; + code += " }\n"; + } + + if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { + code += " VELOCITY = normalize(VELOCITY) * tex_linear_velocity;\n"; + } + code += " if (damping + tex_damping > 0.0) {\n"; + code += " float v = length(VELOCITY);\n"; + code += " float damp = (damping + tex_damping) * mix(1.0, rand_from_seed(alt_seed), damping_random);\n"; + code += " v -= damp * DELTA;\n"; + code += " if (v < 0.0) {\n"; + code += " VELOCITY = vec3(0.0);\n"; + code += " } else {\n"; + code += " VELOCITY = normalize(VELOCITY) * v;\n"; + code += " }\n"; + code += " }\n"; + code += " float base_angle = (initial_angle + tex_angle) * mix(1.0, angle_rand, initial_angle_random);\n"; + code += " base_angle += CUSTOM.y * LIFETIME * (angular_velocity + tex_angular_velocity) * mix(1.0, rand_from_seed(alt_seed) * 2.0 - 1.0, angular_velocity_random);\n"; + code += " CUSTOM.x = base_angle * degree_to_rad;\n"; // angle + code += " CUSTOM.z = (anim_offset + tex_anim_offset) * mix(1.0, anim_offset_rand, anim_offset_random) + CUSTOM.y * (anim_speed + tex_anim_speed) * mix(1.0, rand_from_seed(alt_seed), anim_speed_random);\n"; // angle + code += " }\n"; + // apply color + // apply hue rotation + if (tex_parameters[PARAM_SCALE].is_valid()) + code += " float tex_scale = textureLod(scale_texture, vec2(CUSTOM.y, 0.0), 0.0).r;\n"; + else + code += " float tex_scale = 1.0;\n"; + + if (tex_parameters[PARAM_HUE_VARIATION].is_valid()) + code += " float tex_hue_variation = textureLod(hue_variation_texture, vec2(CUSTOM.y, 0.0), 0.0).r;\n"; + else + code += " float tex_hue_variation = 0.0;\n"; + + code += " float hue_rot_angle = (hue_variation + tex_hue_variation) * pi * 2.0 * mix(1.0, hue_rot_rand * 2.0 - 1.0, hue_variation_random);\n"; + code += " float hue_rot_c = cos(hue_rot_angle);\n"; + code += " float hue_rot_s = sin(hue_rot_angle);\n"; + code += " mat4 hue_rot_mat = mat4(vec4(0.299, 0.587, 0.114, 0.0),\n"; + code += " vec4(0.299, 0.587, 0.114, 0.0),\n"; + code += " vec4(0.299, 0.587, 0.114, 0.0),\n"; + code += " vec4(0.000, 0.000, 0.000, 1.0)) +\n"; + code += " mat4(vec4(0.701, -0.587, -0.114, 0.0),\n"; + code += " vec4(-0.299, 0.413, -0.114, 0.0),\n"; + code += " vec4(-0.300, -0.588, 0.886, 0.0),\n"; + code += " vec4(0.000, 0.000, 0.000, 0.0)) * hue_rot_c +\n"; + code += " mat4(vec4(0.168, 0.330, -0.497, 0.0),\n"; + code += " vec4(-0.328, 0.035, 0.292, 0.0),\n"; + code += " vec4(1.250, -1.050, -0.203, 0.0),\n"; + code += " vec4(0.000, 0.000, 0.000, 0.0)) * hue_rot_s;\n"; + if (color_ramp.is_valid()) { + code += " COLOR = hue_rot_mat * textureLod(color_ramp, vec2(CUSTOM.y, 0.0), 0.0);\n"; + } else { + code += " COLOR = hue_rot_mat * color_value;\n"; + } + if (emission_color_texture.is_valid() && emission_shape >= EMISSION_SHAPE_POINTS) { + code += " COLOR *= texelFetch(emission_texture_color, emission_tex_ofs, 0);\n"; + } + if (trail_color_modifier.is_valid()) { + code += " if (trail_divisor > 1) {\n"; + code += " COLOR *= textureLod(trail_color_modifier, vec2(float(int(NUMBER) % trail_divisor) / float(trail_divisor - 1), 0.0), 0.0);\n"; + code += " }\n"; + } + code += "\n"; + + if (flags[FLAG_DISABLE_Z]) { + + if (flags[FLAG_ALIGN_Y_TO_VELOCITY]) { + code += " if (length(VELOCITY) > 0.0) {\n"; + code += " TRANSFORM[1].xyz = normalize(VELOCITY);\n"; + code += " } else {\n"; + code += " TRANSFORM[1].xyz = normalize(TRANSFORM[1].xyz);\n"; + code += " }\n"; + code += " TRANSFORM[0].xyz = normalize(cross(TRANSFORM[1].xyz, TRANSFORM[2].xyz));\n"; + code += " TRANSFORM[2] = vec4(0.0, 0.0, 1.0, 0.0);\n"; + } else { + code += " TRANSFORM[0] = vec4(cos(CUSTOM.x), -sin(CUSTOM.x), 0.0, 0.0);\n"; + code += " TRANSFORM[1] = vec4(sin(CUSTOM.x), cos(CUSTOM.x), 0.0, 0.0);\n"; + code += " TRANSFORM[2] = vec4(0.0, 0.0, 1.0, 0.0);\n"; + } + + } else { + // orient particle Y towards velocity + if (flags[FLAG_ALIGN_Y_TO_VELOCITY]) { + code += " if (length(VELOCITY) > 0.0) {\n"; + code += " TRANSFORM[1].xyz = normalize(VELOCITY);\n"; + code += " } else {\n"; + code += " TRANSFORM[1].xyz = normalize(TRANSFORM[1].xyz);\n"; + code += " }\n"; + code += " if (TRANSFORM[1].xyz == normalize(TRANSFORM[0].xyz)) {\n"; + code += " TRANSFORM[0].xyz = normalize(cross(normalize(TRANSFORM[1].xyz), normalize(TRANSFORM[2].xyz)));\n"; + code += " TRANSFORM[2].xyz = normalize(cross(normalize(TRANSFORM[0].xyz), normalize(TRANSFORM[1].xyz)));\n"; + code += " } else {\n"; + code += " TRANSFORM[2].xyz = normalize(cross(normalize(TRANSFORM[0].xyz), normalize(TRANSFORM[1].xyz)));\n"; + code += " TRANSFORM[0].xyz = normalize(cross(normalize(TRANSFORM[1].xyz), normalize(TRANSFORM[2].xyz)));\n"; + code += " }\n"; + } else { + code += " TRANSFORM[0].xyz = normalize(TRANSFORM[0].xyz);\n"; + code += " TRANSFORM[1].xyz = normalize(TRANSFORM[1].xyz);\n"; + code += " TRANSFORM[2].xyz = normalize(TRANSFORM[2].xyz);\n"; + } + // turn particle by rotation in Y + if (flags[FLAG_ROTATE_Y]) { + code += " TRANSFORM = TRANSFORM * mat4(vec4(cos(CUSTOM.x), 0.0, -sin(CUSTOM.x), 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(sin(CUSTOM.x), 0.0, cos(CUSTOM.x), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n"; + } + } + //scale by scale + code += " float base_scale = mix(scale * tex_scale, 1.0, scale_random * scale_rand);\n"; + code += " if (base_scale == 0.0) {\n"; + code += " base_scale = 0.000001;\n"; + code += " }\n"; + if (trail_size_modifier.is_valid()) { + code += " if (trail_divisor > 1) {\n"; + code += " base_scale *= textureLod(trail_size_modifier, vec2(float(int(NUMBER) % trail_divisor) / float(trail_divisor - 1), 0.0), 0.0).r;\n"; + code += " }\n"; + } + + code += " TRANSFORM[0].xyz *= base_scale;\n"; + code += " TRANSFORM[1].xyz *= base_scale;\n"; + code += " TRANSFORM[2].xyz *= base_scale;\n"; + if (flags[FLAG_DISABLE_Z]) { + code += " VELOCITY.z = 0.0;\n"; + code += " TRANSFORM[3].z = 0.0;\n"; + } + code += "}\n"; + code += "\n"; + + ShaderData shader_data; + shader_data.shader = VS::get_singleton()->shader_create(); + shader_data.users = 1; + + VS::get_singleton()->shader_set_code(shader_data.shader, code); + + shader_map[mk] = shader_data; + + VS::get_singleton()->material_set_shader(_get_material(), shader_data.shader); +} + +void ParticlesMaterial::flush_changes() { + + if (material_mutex) + material_mutex->lock(); + + while (dirty_materials.first()) { + + dirty_materials.first()->self()->_update_shader(); + } + + if (material_mutex) + material_mutex->unlock(); +} + +void ParticlesMaterial::_queue_shader_change() { + + if (material_mutex) + material_mutex->lock(); + + if (!element.in_list()) { + dirty_materials.add(&element); + } + + if (material_mutex) + material_mutex->unlock(); +} + +bool ParticlesMaterial::_is_shader_dirty() const { + + bool dirty = false; + + if (material_mutex) + material_mutex->lock(); + + dirty = element.in_list(); + + if (material_mutex) + material_mutex->unlock(); + + return dirty; +} + +void ParticlesMaterial::set_spread(float p_spread) { + + spread = p_spread; + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->spread, p_spread); +} + +float ParticlesMaterial::get_spread() const { + + return spread; +} + +void ParticlesMaterial::set_flatness(float p_flatness) { + + flatness = p_flatness; + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->flatness, p_flatness); +} +float ParticlesMaterial::get_flatness() const { + + return flatness; +} + +void ParticlesMaterial::set_param(Parameter p_param, float p_value) { + + ERR_FAIL_INDEX(p_param, PARAM_MAX); + + parameters[p_param] = p_value; + + switch (p_param) { + case PARAM_INITIAL_LINEAR_VELOCITY: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->initial_linear_velocity, p_value); + } break; + case PARAM_ANGULAR_VELOCITY: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->angular_velocity, p_value); + } break; + case PARAM_ORBIT_VELOCITY: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->orbit_velocity, p_value); + } break; + case PARAM_LINEAR_ACCEL: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->linear_accel, p_value); + } break; + case PARAM_RADIAL_ACCEL: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->radial_accel, p_value); + } break; + case PARAM_TANGENTIAL_ACCEL: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->tangent_accel, p_value); + } break; + case PARAM_DAMPING: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->damping, p_value); + } break; + case PARAM_ANGLE: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->initial_angle, p_value); + } break; + case PARAM_SCALE: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->scale, p_value); + } break; + case PARAM_HUE_VARIATION: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->hue_variation, p_value); + } break; + case PARAM_ANIM_SPEED: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_speed, p_value); + } break; + case PARAM_ANIM_OFFSET: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset, p_value); + } break; + case PARAM_MAX: break; // Can't happen, but silences warning + } +} +float ParticlesMaterial::get_param(Parameter p_param) const { + + ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0); + + return parameters[p_param]; +} + +void ParticlesMaterial::set_param_randomness(Parameter p_param, float p_value) { + + ERR_FAIL_INDEX(p_param, PARAM_MAX); + + randomness[p_param] = p_value; + + switch (p_param) { + case PARAM_INITIAL_LINEAR_VELOCITY: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->initial_linear_velocity_random, p_value); + } break; + case PARAM_ANGULAR_VELOCITY: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->angular_velocity_random, p_value); + } break; + case PARAM_ORBIT_VELOCITY: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->orbit_velocity_random, p_value); + } break; + case PARAM_LINEAR_ACCEL: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->linear_accel_random, p_value); + } break; + case PARAM_RADIAL_ACCEL: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->radial_accel_random, p_value); + } break; + case PARAM_TANGENTIAL_ACCEL: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->tangent_accel_random, p_value); + } break; + case PARAM_DAMPING: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->damping_random, p_value); + } break; + case PARAM_ANGLE: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->initial_angle_random, p_value); + } break; + case PARAM_SCALE: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->scale_random, p_value); + } break; + case PARAM_HUE_VARIATION: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->hue_variation_random, p_value); + } break; + case PARAM_ANIM_SPEED: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_speed_random, p_value); + } break; + case PARAM_ANIM_OFFSET: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset_random, p_value); + } break; + case PARAM_MAX: break; // Can't happen, but silences warning + } +} +float ParticlesMaterial::get_param_randomness(Parameter p_param) const { + + ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0); + + return randomness[p_param]; +} + +static void _adjust_curve_range(const Ref<Texture> &p_texture, float p_min, float p_max) { + + Ref<CurveTexture> curve_tex = p_texture; + if (!curve_tex.is_valid()) + return; + + curve_tex->ensure_default_setup(p_min, p_max); +} + +void ParticlesMaterial::set_param_texture(Parameter p_param, const Ref<Texture> &p_texture) { + + ERR_FAIL_INDEX(p_param, PARAM_MAX); + + tex_parameters[p_param] = p_texture; + + switch (p_param) { + case PARAM_INITIAL_LINEAR_VELOCITY: { + //do none for this one + } break; + case PARAM_ANGULAR_VELOCITY: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->angular_velocity_texture, p_texture); + _adjust_curve_range(p_texture, -360, 360); + } break; + case PARAM_ORBIT_VELOCITY: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->orbit_velocity_texture, p_texture); + _adjust_curve_range(p_texture, -500, 500); + } break; + case PARAM_LINEAR_ACCEL: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->linear_accel_texture, p_texture); + _adjust_curve_range(p_texture, -200, 200); + } break; + case PARAM_RADIAL_ACCEL: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->radial_accel_texture, p_texture); + _adjust_curve_range(p_texture, -200, 200); + } break; + case PARAM_TANGENTIAL_ACCEL: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->tangent_accel_texture, p_texture); + _adjust_curve_range(p_texture, -200, 200); + } break; + case PARAM_DAMPING: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->damping_texture, p_texture); + _adjust_curve_range(p_texture, 0, 100); + } break; + case PARAM_ANGLE: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->angle_texture, p_texture); + _adjust_curve_range(p_texture, -360, 360); + } break; + case PARAM_SCALE: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->scale_texture, p_texture); + + Ref<CurveTexture> curve_tex = p_texture; + if (curve_tex.is_valid()) { + curve_tex->ensure_default_setup(); + } + + } break; + case PARAM_HUE_VARIATION: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->hue_variation_texture, p_texture); + _adjust_curve_range(p_texture, -1, 1); + } break; + case PARAM_ANIM_SPEED: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_speed_texture, p_texture); + _adjust_curve_range(p_texture, 0, 200); + } break; + case PARAM_ANIM_OFFSET: { + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset_texture, p_texture); + } break; + case PARAM_MAX: break; // Can't happen, but silences warning + } + + _queue_shader_change(); +} +Ref<Texture> ParticlesMaterial::get_param_texture(Parameter p_param) const { + + ERR_FAIL_INDEX_V(p_param, PARAM_MAX, Ref<Texture>()); + + return tex_parameters[p_param]; +} + +void ParticlesMaterial::set_color(const Color &p_color) { + + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->color, p_color); + color = p_color; +} + +Color ParticlesMaterial::get_color() const { + + return color; +} + +void ParticlesMaterial::set_color_ramp(const Ref<Texture> &p_texture) { + + color_ramp = p_texture; + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->color_ramp, p_texture); + _queue_shader_change(); + _change_notify(); +} + +Ref<Texture> ParticlesMaterial::get_color_ramp() const { + + return color_ramp; +} + +void ParticlesMaterial::set_flag(Flags p_flag, bool p_enable) { + ERR_FAIL_INDEX(p_flag, FLAG_MAX); + flags[p_flag] = p_enable; + _queue_shader_change(); + if (p_flag == FLAG_DISABLE_Z) { + _change_notify(); + } +} + +bool ParticlesMaterial::get_flag(Flags p_flag) const { + ERR_FAIL_INDEX_V(p_flag, FLAG_MAX, false); + return flags[p_flag]; +} + +void ParticlesMaterial::set_emission_shape(EmissionShape p_shape) { + + emission_shape = p_shape; + _change_notify(); + _queue_shader_change(); +} + +void ParticlesMaterial::set_emission_sphere_radius(float p_radius) { + + emission_sphere_radius = p_radius; + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_sphere_radius, p_radius); +} + +void ParticlesMaterial::set_emission_box_extents(Vector3 p_extents) { + + emission_box_extents = p_extents; + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_box_extents, p_extents); +} + +void ParticlesMaterial::set_emission_point_texture(const Ref<Texture> &p_points) { + + emission_point_texture = p_points; + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_points, p_points); +} + +void ParticlesMaterial::set_emission_normal_texture(const Ref<Texture> &p_normals) { + + emission_normal_texture = p_normals; + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_normal, p_normals); +} + +void ParticlesMaterial::set_emission_color_texture(const Ref<Texture> &p_colors) { + + emission_color_texture = p_colors; + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_color, p_colors); + _queue_shader_change(); +} + +void ParticlesMaterial::set_emission_point_count(int p_count) { + + emission_point_count = p_count; + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_point_count, p_count); +} + +ParticlesMaterial::EmissionShape ParticlesMaterial::get_emission_shape() const { + + return emission_shape; +} + +float ParticlesMaterial::get_emission_sphere_radius() const { + + return emission_sphere_radius; +} +Vector3 ParticlesMaterial::get_emission_box_extents() const { + + return emission_box_extents; +} +Ref<Texture> ParticlesMaterial::get_emission_point_texture() const { + + return emission_point_texture; +} +Ref<Texture> ParticlesMaterial::get_emission_normal_texture() const { + + return emission_normal_texture; +} + +Ref<Texture> ParticlesMaterial::get_emission_color_texture() const { + + return emission_color_texture; +} + +int ParticlesMaterial::get_emission_point_count() const { + + return emission_point_count; +} + +void ParticlesMaterial::set_trail_divisor(int p_divisor) { + + trail_divisor = p_divisor; + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->trail_divisor, p_divisor); +} + +int ParticlesMaterial::get_trail_divisor() const { + + return trail_divisor; +} + +void ParticlesMaterial::set_trail_size_modifier(const Ref<CurveTexture> &p_trail_size_modifier) { + + trail_size_modifier = p_trail_size_modifier; + + Ref<CurveTexture> curve = trail_size_modifier; + if (curve.is_valid()) { + curve->ensure_default_setup(); + } + + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->trail_size_modifier, curve); + _queue_shader_change(); +} + +Ref<CurveTexture> ParticlesMaterial::get_trail_size_modifier() const { + + return trail_size_modifier; +} + +void ParticlesMaterial::set_trail_color_modifier(const Ref<GradientTexture> &p_trail_color_modifier) { + + trail_color_modifier = p_trail_color_modifier; + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->trail_color_modifier, p_trail_color_modifier); + _queue_shader_change(); +} + +Ref<GradientTexture> ParticlesMaterial::get_trail_color_modifier() const { + + return trail_color_modifier; +} + +void ParticlesMaterial::set_gravity(const Vector3 &p_gravity) { + + gravity = p_gravity; + Vector3 gset = gravity; + if (gset == Vector3()) { + gset = Vector3(0, -0.000001, 0); //as gravity is used as upvector in some calculations + } + VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->gravity, gset); +} + +Vector3 ParticlesMaterial::get_gravity() const { + + return gravity; +} + +RID ParticlesMaterial::get_shader_rid() const { + + ERR_FAIL_COND_V(!shader_map.has(current_key), RID()); + return shader_map[current_key].shader; +} + +void ParticlesMaterial::_validate_property(PropertyInfo &property) const { + + if (property.name == "color" && color_ramp.is_valid()) { + property.usage = 0; + } + + if (property.name == "emission_sphere_radius" && emission_shape != EMISSION_SHAPE_SPHERE) { + property.usage = 0; + } + + if (property.name == "emission_box_extents" && emission_shape != EMISSION_SHAPE_BOX) { + property.usage = 0; + } + + if ((property.name == "emission_point_texture" || property.name == "emission_color_texture") && (emission_shape < EMISSION_SHAPE_POINTS)) { + property.usage = 0; + } + + if (property.name == "emission_normal_texture" && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) { + property.usage = 0; + } + + if (property.name == "emission_point_count" && (emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS)) { + property.usage = 0; + } + + if (property.name.begins_with("orbit_") && !flags[FLAG_DISABLE_Z]) { + property.usage = 0; + } +} + +Shader::Mode ParticlesMaterial::get_shader_mode() const { + + return Shader::MODE_PARTICLES; +} + +void ParticlesMaterial::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_spread", "degrees"), &ParticlesMaterial::set_spread); + ClassDB::bind_method(D_METHOD("get_spread"), &ParticlesMaterial::get_spread); + + ClassDB::bind_method(D_METHOD("set_flatness", "amount"), &ParticlesMaterial::set_flatness); + ClassDB::bind_method(D_METHOD("get_flatness"), &ParticlesMaterial::get_flatness); + + ClassDB::bind_method(D_METHOD("set_param", "param", "value"), &ParticlesMaterial::set_param); + ClassDB::bind_method(D_METHOD("get_param", "param"), &ParticlesMaterial::get_param); + + ClassDB::bind_method(D_METHOD("set_param_randomness", "param", "randomness"), &ParticlesMaterial::set_param_randomness); + ClassDB::bind_method(D_METHOD("get_param_randomness", "param"), &ParticlesMaterial::get_param_randomness); + + ClassDB::bind_method(D_METHOD("set_param_texture", "param", "texture"), &ParticlesMaterial::set_param_texture); + ClassDB::bind_method(D_METHOD("get_param_texture", "param"), &ParticlesMaterial::get_param_texture); + + ClassDB::bind_method(D_METHOD("set_color", "color"), &ParticlesMaterial::set_color); + ClassDB::bind_method(D_METHOD("get_color"), &ParticlesMaterial::get_color); + + ClassDB::bind_method(D_METHOD("set_color_ramp", "ramp"), &ParticlesMaterial::set_color_ramp); + ClassDB::bind_method(D_METHOD("get_color_ramp"), &ParticlesMaterial::get_color_ramp); + + ClassDB::bind_method(D_METHOD("set_flag", "flag", "enable"), &ParticlesMaterial::set_flag); + ClassDB::bind_method(D_METHOD("get_flag", "flag"), &ParticlesMaterial::get_flag); + + ClassDB::bind_method(D_METHOD("set_emission_shape", "shape"), &ParticlesMaterial::set_emission_shape); + ClassDB::bind_method(D_METHOD("get_emission_shape"), &ParticlesMaterial::get_emission_shape); + + ClassDB::bind_method(D_METHOD("set_emission_sphere_radius", "radius"), &ParticlesMaterial::set_emission_sphere_radius); + ClassDB::bind_method(D_METHOD("get_emission_sphere_radius"), &ParticlesMaterial::get_emission_sphere_radius); + + ClassDB::bind_method(D_METHOD("set_emission_box_extents", "extents"), &ParticlesMaterial::set_emission_box_extents); + ClassDB::bind_method(D_METHOD("get_emission_box_extents"), &ParticlesMaterial::get_emission_box_extents); + + ClassDB::bind_method(D_METHOD("set_emission_point_texture", "texture"), &ParticlesMaterial::set_emission_point_texture); + ClassDB::bind_method(D_METHOD("get_emission_point_texture"), &ParticlesMaterial::get_emission_point_texture); + + ClassDB::bind_method(D_METHOD("set_emission_normal_texture", "texture"), &ParticlesMaterial::set_emission_normal_texture); + ClassDB::bind_method(D_METHOD("get_emission_normal_texture"), &ParticlesMaterial::get_emission_normal_texture); + + ClassDB::bind_method(D_METHOD("set_emission_color_texture", "texture"), &ParticlesMaterial::set_emission_color_texture); + ClassDB::bind_method(D_METHOD("get_emission_color_texture"), &ParticlesMaterial::get_emission_color_texture); + + ClassDB::bind_method(D_METHOD("set_emission_point_count", "point_count"), &ParticlesMaterial::set_emission_point_count); + ClassDB::bind_method(D_METHOD("get_emission_point_count"), &ParticlesMaterial::get_emission_point_count); + + ClassDB::bind_method(D_METHOD("set_trail_divisor", "divisor"), &ParticlesMaterial::set_trail_divisor); + ClassDB::bind_method(D_METHOD("get_trail_divisor"), &ParticlesMaterial::get_trail_divisor); + + ClassDB::bind_method(D_METHOD("set_trail_size_modifier", "texture"), &ParticlesMaterial::set_trail_size_modifier); + ClassDB::bind_method(D_METHOD("get_trail_size_modifier"), &ParticlesMaterial::get_trail_size_modifier); + + ClassDB::bind_method(D_METHOD("set_trail_color_modifier", "texture"), &ParticlesMaterial::set_trail_color_modifier); + ClassDB::bind_method(D_METHOD("get_trail_color_modifier"), &ParticlesMaterial::get_trail_color_modifier); + + ClassDB::bind_method(D_METHOD("get_gravity"), &ParticlesMaterial::get_gravity); + ClassDB::bind_method(D_METHOD("set_gravity", "accel_vec"), &ParticlesMaterial::set_gravity); + + ADD_GROUP("Trail", "trail_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "trail_divisor", PROPERTY_HINT_RANGE, "1,1000000,1"), "set_trail_divisor", "get_trail_divisor"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "trail_size_modifier", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_trail_size_modifier", "get_trail_size_modifier"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "trail_color_modifier", PROPERTY_HINT_RESOURCE_TYPE, "GradientTexture"), "set_trail_color_modifier", "get_trail_color_modifier"); + ADD_GROUP("Emission Shape", "emission_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Box,Points,Directed Points"), "set_emission_shape", "get_emission_shape"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "emission_sphere_radius", PROPERTY_HINT_RANGE, "0.01,128,0.01,or_greater"), "set_emission_sphere_radius", "get_emission_sphere_radius"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "emission_box_extents"), "set_emission_box_extents", "get_emission_box_extents"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_point_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_emission_point_texture", "get_emission_point_texture"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_normal_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_emission_normal_texture", "get_emission_normal_texture"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_color_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_emission_color_texture", "get_emission_color_texture"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_point_count", PROPERTY_HINT_RANGE, "0,1000000,1"), "set_emission_point_count", "get_emission_point_count"); + ADD_GROUP("Flags", "flag_"); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_align_y"), "set_flag", "get_flag", FLAG_ALIGN_Y_TO_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_rotate_y"), "set_flag", "get_flag", FLAG_ROTATE_Y); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_disable_z"), "set_flag", "get_flag", FLAG_DISABLE_Z); + ADD_GROUP("Spread", ""); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "spread", PROPERTY_HINT_RANGE, "0,180,0.01"), "set_spread", "get_spread"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "flatness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_flatness", "get_flatness"); + ADD_GROUP("Gravity", ""); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity"), "set_gravity", "get_gravity"); + ADD_GROUP("Initial Velocity", "initial_"); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "initial_velocity", PROPERTY_HINT_RANGE, "0,1000,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_INITIAL_LINEAR_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "initial_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_INITIAL_LINEAR_VELOCITY); + ADD_GROUP("Angular Velocity", "angular_"); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity", PROPERTY_HINT_RANGE, "-720,720,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_ANGULAR_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGULAR_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angular_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANGULAR_VELOCITY); + ADD_GROUP("Orbit Velocity", "orbit_"); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "orbit_velocity", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_ORBIT_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "orbit_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ORBIT_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "orbit_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ORBIT_VELOCITY); + ADD_GROUP("Linear Accel", "linear_"); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_LINEAR_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_LINEAR_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "linear_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_LINEAR_ACCEL); + ADD_GROUP("Radial Accel", "radial_"); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "radial_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_RADIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "radial_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_RADIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "radial_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_RADIAL_ACCEL); + ADD_GROUP("Tangential Accel", "tangential_"); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "tangential_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_TANGENTIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "tangential_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_TANGENTIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "tangential_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_TANGENTIAL_ACCEL); + ADD_GROUP("Damping", ""); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "damping", PROPERTY_HINT_RANGE, "0,100,0.01,or_greater"), "set_param", "get_param", PARAM_DAMPING); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "damping_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_DAMPING); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "damping_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_DAMPING); + ADD_GROUP("Angle", ""); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angle", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater"), "set_param", "get_param", PARAM_ANGLE); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angle_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGLE); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angle_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANGLE); + ADD_GROUP("Scale", ""); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "scale", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param", "get_param", PARAM_SCALE); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "scale_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_SCALE); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "scale_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_SCALE); + ADD_GROUP("Color", ""); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "GradientTexture"), "set_color_ramp", "get_color_ramp"); + + ADD_GROUP("Hue Variation", "hue_"); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "hue_variation", PROPERTY_HINT_RANGE, "-1,1,0.1"), "set_param", "get_param", PARAM_HUE_VARIATION); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "hue_variation_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_HUE_VARIATION); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "hue_variation_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_HUE_VARIATION); + ADD_GROUP("Animation", "anim_"); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_speed", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater"), "set_param", "get_param", PARAM_ANIM_SPEED); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_speed_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_SPEED); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_speed_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANIM_SPEED); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_offset", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_ANIM_OFFSET); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_offset_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_OFFSET); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_offset_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANIM_OFFSET); + + BIND_ENUM_CONSTANT(PARAM_INITIAL_LINEAR_VELOCITY); + BIND_ENUM_CONSTANT(PARAM_ANGULAR_VELOCITY); + BIND_ENUM_CONSTANT(PARAM_ORBIT_VELOCITY); + BIND_ENUM_CONSTANT(PARAM_LINEAR_ACCEL); + BIND_ENUM_CONSTANT(PARAM_RADIAL_ACCEL); + BIND_ENUM_CONSTANT(PARAM_TANGENTIAL_ACCEL); + BIND_ENUM_CONSTANT(PARAM_DAMPING); + BIND_ENUM_CONSTANT(PARAM_ANGLE); + BIND_ENUM_CONSTANT(PARAM_SCALE); + BIND_ENUM_CONSTANT(PARAM_HUE_VARIATION); + BIND_ENUM_CONSTANT(PARAM_ANIM_SPEED); + BIND_ENUM_CONSTANT(PARAM_ANIM_OFFSET); + BIND_ENUM_CONSTANT(PARAM_MAX); + + BIND_ENUM_CONSTANT(FLAG_ALIGN_Y_TO_VELOCITY); + BIND_ENUM_CONSTANT(FLAG_ROTATE_Y); + BIND_ENUM_CONSTANT(FLAG_MAX); + + BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINT); + BIND_ENUM_CONSTANT(EMISSION_SHAPE_SPHERE); + BIND_ENUM_CONSTANT(EMISSION_SHAPE_BOX); + BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINTS); + BIND_ENUM_CONSTANT(EMISSION_SHAPE_DIRECTED_POINTS); +} + +ParticlesMaterial::ParticlesMaterial() : + element(this) { + + set_spread(45); + set_flatness(0); + set_param(PARAM_INITIAL_LINEAR_VELOCITY, 0); + set_param(PARAM_ORBIT_VELOCITY, 0); + set_param(PARAM_LINEAR_ACCEL, 0); + set_param(PARAM_RADIAL_ACCEL, 0); + set_param(PARAM_TANGENTIAL_ACCEL, 0); + set_param(PARAM_DAMPING, 0); + set_param(PARAM_ANGLE, 0); + set_param(PARAM_SCALE, 1); + set_param(PARAM_HUE_VARIATION, 0); + set_param(PARAM_ANIM_SPEED, 0); + set_param(PARAM_ANIM_OFFSET, 0); + set_emission_shape(EMISSION_SHAPE_POINT); + set_emission_sphere_radius(1); + set_emission_box_extents(Vector3(1, 1, 1)); + set_trail_divisor(1); + set_gravity(Vector3(0, -9.8, 0)); + emission_point_count = 1; + + for (int i = 0; i < PARAM_MAX; i++) { + set_param_randomness(Parameter(i), 0); + } + + for (int i = 0; i < FLAG_MAX; i++) { + flags[i] = false; + } + + set_color(Color(1, 1, 1, 1)); + + current_key.key = 0; + current_key.invalid_key = 1; + + _queue_shader_change(); +} + +ParticlesMaterial::~ParticlesMaterial() { + + if (material_mutex) + material_mutex->lock(); + + if (shader_map.has(current_key)) { + shader_map[current_key].users--; + if (shader_map[current_key].users == 0) { + //deallocate shader, as it's no longer in use + VS::get_singleton()->free(shader_map[current_key].shader); + shader_map.erase(current_key); + } + + VS::get_singleton()->material_set_shader(_get_material(), RID()); + } + + if (material_mutex) + material_mutex->unlock(); +} diff --git a/scene/resources/particles_material.h b/scene/resources/particles_material.h new file mode 100644 index 0000000000..06ebb3c4dc --- /dev/null +++ b/scene/resources/particles_material.h @@ -0,0 +1,301 @@ +/*************************************************************************/ +/* particles_material.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. */ +/*************************************************************************/ + +#include "core/rid.h" +#include "scene/resources/material.h" + +#ifndef PARTICLES_MATERIAL_H +#define PARTICLES_MATERIAL_H + +class ParticlesMaterial : public Material { + + GDCLASS(ParticlesMaterial, Material) + +public: + enum Parameter { + + PARAM_INITIAL_LINEAR_VELOCITY, + PARAM_ANGULAR_VELOCITY, + PARAM_ORBIT_VELOCITY, + PARAM_LINEAR_ACCEL, + PARAM_RADIAL_ACCEL, + PARAM_TANGENTIAL_ACCEL, + PARAM_DAMPING, + PARAM_ANGLE, + PARAM_SCALE, + PARAM_HUE_VARIATION, + PARAM_ANIM_SPEED, + PARAM_ANIM_OFFSET, + PARAM_MAX + }; + + enum Flags { + FLAG_ALIGN_Y_TO_VELOCITY, + FLAG_ROTATE_Y, + FLAG_DISABLE_Z, + FLAG_MAX + }; + + enum EmissionShape { + EMISSION_SHAPE_POINT, + EMISSION_SHAPE_SPHERE, + EMISSION_SHAPE_BOX, + EMISSION_SHAPE_POINTS, + EMISSION_SHAPE_DIRECTED_POINTS, + }; + +private: + union MaterialKey { + + struct { + uint32_t texture_mask : 16; + uint32_t texture_color : 1; + uint32_t flags : 4; + uint32_t emission_shape : 2; + uint32_t trail_size_texture : 1; + uint32_t trail_color_texture : 1; + uint32_t invalid_key : 1; + uint32_t has_emission_color : 1; + }; + + uint32_t key; + + bool operator<(const MaterialKey &p_key) const { + return key < p_key.key; + } + }; + + struct ShaderData { + RID shader; + int users; + }; + + static Map<MaterialKey, ShaderData> shader_map; + + MaterialKey current_key; + + _FORCE_INLINE_ MaterialKey _compute_key() const { + + MaterialKey mk; + mk.key = 0; + for (int i = 0; i < PARAM_MAX; i++) { + if (tex_parameters[i].is_valid()) { + mk.texture_mask |= (1 << i); + } + } + for (int i = 0; i < FLAG_MAX; i++) { + if (flags[i]) { + mk.flags |= (1 << i); + } + } + + mk.texture_color = color_ramp.is_valid() ? 1 : 0; + mk.emission_shape = emission_shape; + mk.trail_color_texture = trail_color_modifier.is_valid() ? 1 : 0; + mk.trail_size_texture = trail_size_modifier.is_valid() ? 1 : 0; + mk.has_emission_color = emission_shape >= EMISSION_SHAPE_POINTS && emission_color_texture.is_valid(); + + return mk; + } + + static Mutex *material_mutex; + static SelfList<ParticlesMaterial>::List dirty_materials; + + struct ShaderNames { + StringName spread; + StringName flatness; + StringName initial_linear_velocity; + StringName initial_angle; + StringName angular_velocity; + StringName orbit_velocity; + StringName linear_accel; + StringName radial_accel; + StringName tangent_accel; + StringName damping; + StringName scale; + StringName hue_variation; + StringName anim_speed; + StringName anim_offset; + + StringName initial_linear_velocity_random; + StringName initial_angle_random; + StringName angular_velocity_random; + StringName orbit_velocity_random; + StringName linear_accel_random; + StringName radial_accel_random; + StringName tangent_accel_random; + StringName damping_random; + StringName scale_random; + StringName hue_variation_random; + StringName anim_speed_random; + StringName anim_offset_random; + + StringName angle_texture; + StringName angular_velocity_texture; + StringName orbit_velocity_texture; + StringName linear_accel_texture; + StringName radial_accel_texture; + StringName tangent_accel_texture; + StringName damping_texture; + StringName scale_texture; + StringName hue_variation_texture; + StringName anim_speed_texture; + StringName anim_offset_texture; + + StringName color; + StringName color_ramp; + + StringName emission_sphere_radius; + StringName emission_box_extents; + StringName emission_texture_point_count; + StringName emission_texture_points; + StringName emission_texture_normal; + StringName emission_texture_color; + + StringName trail_divisor; + StringName trail_size_modifier; + StringName trail_color_modifier; + + StringName gravity; + }; + + static ShaderNames *shader_names; + + SelfList<ParticlesMaterial> element; + + void _update_shader(); + _FORCE_INLINE_ void _queue_shader_change(); + _FORCE_INLINE_ bool _is_shader_dirty() const; + + float spread; + float flatness; + + float parameters[PARAM_MAX]; + float randomness[PARAM_MAX]; + + Ref<Texture> tex_parameters[PARAM_MAX]; + Color color; + Ref<Texture> color_ramp; + + bool flags[FLAG_MAX]; + + EmissionShape emission_shape; + float emission_sphere_radius; + Vector3 emission_box_extents; + Ref<Texture> emission_point_texture; + Ref<Texture> emission_normal_texture; + Ref<Texture> emission_color_texture; + int emission_point_count; + + bool anim_loop; + + int trail_divisor; + + Ref<CurveTexture> trail_size_modifier; + Ref<GradientTexture> trail_color_modifier; + + Vector3 gravity; + + //do not save emission points here + +protected: + static void _bind_methods(); + virtual void _validate_property(PropertyInfo &property) const; + +public: + void set_spread(float p_spread); + float get_spread() const; + + void set_flatness(float p_flatness); + float get_flatness() const; + + void set_param(Parameter p_param, float p_value); + float get_param(Parameter p_param) const; + + void set_param_randomness(Parameter p_param, float p_value); + float get_param_randomness(Parameter p_param) const; + + void set_param_texture(Parameter p_param, const Ref<Texture> &p_texture); + Ref<Texture> get_param_texture(Parameter p_param) const; + + void set_color(const Color &p_color); + Color get_color() const; + + void set_color_ramp(const Ref<Texture> &p_texture); + Ref<Texture> get_color_ramp() const; + + void set_flag(Flags p_flag, bool p_enable); + bool get_flag(Flags p_flag) const; + + void set_emission_shape(EmissionShape p_shape); + void set_emission_sphere_radius(float p_radius); + void set_emission_box_extents(Vector3 p_extents); + void set_emission_point_texture(const Ref<Texture> &p_points); + void set_emission_normal_texture(const Ref<Texture> &p_normals); + void set_emission_color_texture(const Ref<Texture> &p_colors); + void set_emission_point_count(int p_count); + + EmissionShape get_emission_shape() const; + float get_emission_sphere_radius() const; + Vector3 get_emission_box_extents() const; + Ref<Texture> get_emission_point_texture() const; + Ref<Texture> get_emission_normal_texture() const; + Ref<Texture> get_emission_color_texture() const; + int get_emission_point_count() const; + + void set_trail_divisor(int p_divisor); + int get_trail_divisor() const; + + void set_trail_size_modifier(const Ref<CurveTexture> &p_trail_size_modifier); + Ref<CurveTexture> get_trail_size_modifier() const; + + void set_trail_color_modifier(const Ref<GradientTexture> &p_trail_color_modifier); + Ref<GradientTexture> get_trail_color_modifier() const; + + void set_gravity(const Vector3 &p_gravity); + Vector3 get_gravity() const; + + static void init_shaders(); + static void finish_shaders(); + static void flush_changes(); + + RID get_shader_rid() const; + + virtual Shader::Mode get_shader_mode() const; + + ParticlesMaterial(); + ~ParticlesMaterial(); +}; + +VARIANT_ENUM_CAST(ParticlesMaterial::Parameter) +VARIANT_ENUM_CAST(ParticlesMaterial::Flags) +VARIANT_ENUM_CAST(ParticlesMaterial::EmissionShape) + +#endif // PARTICLES_MATERIAL_H diff --git a/scene/resources/physics_material.cpp b/scene/resources/physics_material.cpp index dc5ca1aef6..03b3589727 100644 --- a/scene/resources/physics_material.cpp +++ b/scene/resources/physics_material.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,6 +27,7 @@ /* 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() { diff --git a/scene/resources/physics_material.h b/scene/resources/physics_material.h index c69e44a7da..bf11bf0ac1 100644 --- a/scene/resources/physics_material.h +++ b/scene/resources/physics_material.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* 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 */ @@ -27,10 +27,11 @@ /* 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 "core/resource.h" #include "servers/physics_server.h" class PhysicsMaterial : public Resource { diff --git a/scene/resources/polygon_path_finder.cpp b/scene/resources/polygon_path_finder.cpp index 44f9ebaf33..bd03930c9e 100644 --- a/scene/resources/polygon_path_finder.cpp +++ b/scene/resources/polygon_path_finder.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "polygon_path_finder.h" -#include "geometry.h" +#include "core/math/geometry.h" bool PolygonPathFinder::_is_point_inside(const Vector2 &p_point) const { diff --git a/scene/resources/polygon_path_finder.h b/scene/resources/polygon_path_finder.h index 19761c274c..66282458af 100644 --- a/scene/resources/polygon_path_finder.h +++ b/scene/resources/polygon_path_finder.h @@ -31,7 +31,7 @@ #ifndef POLYGON_PATH_FINDER_H #define POLYGON_PATH_FINDER_H -#include "resource.h" +#include "core/resource.h" class PolygonPathFinder : public Resource { diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index 28aa6f1aa7..28af3d3220 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -89,7 +89,7 @@ void PrimitiveMesh::_update() const { pending_request = false; - _clear_triangle_mesh(); + clear_cache(); const_cast<PrimitiveMesh *>(this)->emit_changed(); } @@ -1282,7 +1282,7 @@ void PrismMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("get_subdivide_depth"), &PrismMesh::get_subdivide_depth); ADD_PROPERTY(PropertyInfo(Variant::REAL, "left_to_right", PROPERTY_HINT_RANGE, "-2.0,2.0,0.1"), "set_left_to_right", "get_left_to_right"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size"), "set_size", "get_size"); ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_width", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_width", "get_subdivide_width"); ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_height", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_height", "get_subdivide_height"); ADD_PROPERTY(PropertyInfo(Variant::INT, "subdivide_depth", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_subdivide_depth", "get_subdivide_depth"); diff --git a/scene/resources/room.h b/scene/resources/room.h index 359d918665..d5ad847516 100644 --- a/scene/resources/room.h +++ b/scene/resources/room.h @@ -31,8 +31,8 @@ #ifndef ROOM_BOUNDS_H #define ROOM_BOUNDS_H -#include "bsp_tree.h" -#include "resource.h" +#include "core/math/bsp_tree.h" +#include "core/resource.h" /** @author Juan Linietsky <reduzio@gmail.com> */ diff --git a/scene/resources/scene_format_text.cpp b/scene/resources/scene_format_text.cpp index fd9989fe72..597ffe49ae 100644 --- a/scene/resources/scene_format_text.cpp +++ b/scene/resources/scene_format_text.cpp @@ -30,15 +30,15 @@ #include "scene_format_text.h" #include "core/io/resource_format_binary.h" -#include "os/dir_access.h" -#include "project_settings.h" -#include "version.h" +#include "core/os/dir_access.h" +#include "core/project_settings.h" +#include "core/version.h" //version 2: changed names for basis, aabb, poolvectors, etc. #define FORMAT_VERSION 2 -#include "os/dir_access.h" -#include "version.h" +#include "core/os/dir_access.h" +#include "core/version.h" #define _printerr() ERR_PRINT(String(res_path + ":" + itos(lines) + " - Parse Error: " + error_text).utf8().get_data()); @@ -294,25 +294,25 @@ Ref<PackedScene> ResourceInteractiveLoaderText::_parse_node_tag(VariantParser::R if (!next_tag.fields.has("from")) { error = ERR_FILE_CORRUPT; - error_text = "missing 'from' field fron connection tag"; + error_text = "missing 'from' field from connection tag"; return Ref<PackedScene>(); } if (!next_tag.fields.has("to")) { error = ERR_FILE_CORRUPT; - error_text = "missing 'to' field fron connection tag"; + error_text = "missing 'to' field from connection tag"; return Ref<PackedScene>(); } if (!next_tag.fields.has("signal")) { error = ERR_FILE_CORRUPT; - error_text = "missing 'signal' field fron connection tag"; + error_text = "missing 'signal' field from connection tag"; return Ref<PackedScene>(); } if (!next_tag.fields.has("method")) { error = ERR_FILE_CORRUPT; - error_text = "missing 'method' field fron connection tag"; + error_text = "missing 'method' field from connection tag"; return Ref<PackedScene>(); } @@ -358,7 +358,7 @@ Ref<PackedScene> ResourceInteractiveLoaderText::_parse_node_tag(VariantParser::R if (!next_tag.fields.has("path")) { error = ERR_FILE_CORRUPT; - error_text = "missing 'path' field fron connection tag"; + error_text = "missing 'path' field from connection tag"; _printerr(); return Ref<PackedScene>(); } diff --git a/scene/resources/scene_format_text.h b/scene/resources/scene_format_text.h index c28ded3d77..8d1af2bbb2 100644 --- a/scene/resources/scene_format_text.h +++ b/scene/resources/scene_format_text.h @@ -31,11 +31,11 @@ #ifndef SCENE_FORMAT_TEXT_H #define SCENE_FORMAT_TEXT_H -#include "io/resource_loader.h" -#include "io/resource_saver.h" -#include "os/file_access.h" +#include "core/io/resource_loader.h" +#include "core/io/resource_saver.h" +#include "core/os/file_access.h" +#include "core/variant_parser.h" #include "scene/resources/packed_scene.h" -#include "variant_parser.h" class ResourceInteractiveLoaderText : public ResourceInteractiveLoader { diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp index f53f03c1c8..66bf3b4991 100644 --- a/scene/resources/shader.cpp +++ b/scene/resources/shader.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "shader.h" -#include "os/file_access.h" +#include "core/os/file_access.h" #include "scene/scene_string_names.h" #include "servers/visual/shader_language.h" #include "servers/visual_server.h" @@ -126,6 +126,11 @@ void Shader::get_default_texture_param_list(List<StringName> *r_textures) const r_textures->push_back(E->key()); } } + +bool Shader::is_text_shader() const { + return true; +} + bool Shader::has_param(const StringName &p_param) const { return params_cache.has(p_param); @@ -235,8 +240,10 @@ Error ResourceFormatSaverShader::save(const String &p_path, const RES &p_resourc void ResourceFormatSaverShader::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const { - if (Object::cast_to<Shader>(*p_resource)) { - p_extensions->push_back("shader"); + if (const Shader *shader = Object::cast_to<Shader>(*p_resource)) { + if (shader->is_text_shader()) { + p_extensions->push_back("shader"); + } } } bool ResourceFormatSaverShader::recognize(const RES &p_resource) const { diff --git a/scene/resources/shader.h b/scene/resources/shader.h index efc5da7753..c2c205237f 100644 --- a/scene/resources/shader.h +++ b/scene/resources/shader.h @@ -31,9 +31,9 @@ #ifndef SHADER_H #define SHADER_H -#include "io/resource_loader.h" -#include "io/resource_saver.h" -#include "resource.h" +#include "core/io/resource_loader.h" +#include "core/io/resource_saver.h" +#include "core/resource.h" #include "scene/resources/texture.h" class Shader : public Resource { @@ -79,6 +79,8 @@ public: Ref<Texture> get_default_texture_param(const StringName &p_param) const; void get_default_texture_param_list(List<StringName> *r_textures) const; + virtual bool is_text_shader() const; + _FORCE_INLINE_ StringName remap_param(const StringName &p_param) const { if (params_cache_dirty) get_param_list(NULL); diff --git a/scene/resources/shape.cpp b/scene/resources/shape.cpp index a48ce0564b..869f4a3a9b 100644 --- a/scene/resources/shape.cpp +++ b/scene/resources/shape.cpp @@ -30,7 +30,7 @@ #include "shape.h" -#include "os/os.h" +#include "core/os/os.h" #include "scene/main/scene_tree.h" #include "scene/resources/mesh.h" #include "servers/physics_server.h" @@ -101,7 +101,7 @@ void Shape::_bind_methods() { ClassDB::bind_method(D_METHOD("set_margin", "margin"), &Shape::set_margin); ClassDB::bind_method(D_METHOD("get_margin"), &Shape::get_margin); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "margin", PROPERTY_HINT_RANGE, "0.04,10,0.01"), "set_margin", "get_margin"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "margin", PROPERTY_HINT_RANGE, "0.04,10,0.001"), "set_margin", "get_margin"); } Shape::Shape() : diff --git a/scene/resources/shape.h b/scene/resources/shape.h index 0c44b86e92..6643f4ee44 100644 --- a/scene/resources/shape.h +++ b/scene/resources/shape.h @@ -31,7 +31,7 @@ #ifndef SHAPE_H #define SHAPE_H -#include "resource.h" +#include "core/resource.h" class ArrayMesh; class Shape : public Resource { diff --git a/scene/resources/shape_2d.h b/scene/resources/shape_2d.h index 7eb0406bd8..fa39daa565 100644 --- a/scene/resources/shape_2d.h +++ b/scene/resources/shape_2d.h @@ -31,7 +31,7 @@ #ifndef SHAPE_2D_H #define SHAPE_2D_H -#include "resource.h" +#include "core/resource.h" class Shape2D : public Resource { GDCLASS(Shape2D, Resource); diff --git a/scene/resources/sky_box.cpp b/scene/resources/sky_box.cpp index 4176aed4d8..a6a52c7bba 100644 --- a/scene/resources/sky_box.cpp +++ b/scene/resources/sky_box.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "sky_box.h" -#include "io/image_loader.h" +#include "core/io/image_loader.h" void Sky::set_radiance_size(RadianceSize p_size) { ERR_FAIL_INDEX(p_size, RADIANCE_SIZE_MAX); diff --git a/scene/resources/sky_box.h b/scene/resources/sky_box.h index e561653a9e..bbb852822d 100644 --- a/scene/resources/sky_box.h +++ b/scene/resources/sky_box.h @@ -31,7 +31,7 @@ #ifndef SKY_BOX_H #define SKY_BOX_H -#include "os/thread.h" +#include "core/os/thread.h" #include "scene/resources/texture.h" class Sky : public Resource { GDCLASS(Sky, Resource); diff --git a/scene/resources/space_2d.h b/scene/resources/space_2d.h index 148285ac6d..1143ad2bd5 100644 --- a/scene/resources/space_2d.h +++ b/scene/resources/space_2d.h @@ -31,7 +31,7 @@ #ifndef SPACE_2D_H #define SPACE_2D_H -#include "resource.h" +#include "core/resource.h" #include "servers/physics_2d_server.h" class Space2D : public Resource { diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp index fb81375b0a..05050a5ea2 100644 --- a/scene/resources/style_box.cpp +++ b/scene/resources/style_box.cpp @@ -136,7 +136,7 @@ Ref<Texture> StyleBoxTexture::get_normal_map() const { void StyleBoxTexture::set_margin_size(Margin p_margin, float p_size) { - ERR_FAIL_INDEX(p_margin, 4); + ERR_FAIL_INDEX((int)p_margin, 4); margin[p_margin] = p_size; emit_changed(); @@ -200,7 +200,7 @@ Size2 StyleBoxTexture::get_center_size() const { void StyleBoxTexture::set_expand_margin_size(Margin p_expand_margin, float p_size) { - ERR_FAIL_INDEX(p_expand_margin, 4); + ERR_FAIL_INDEX((int)p_expand_margin, 4); expand_margin[p_expand_margin] = p_size; emit_changed(); } @@ -223,7 +223,7 @@ void StyleBoxTexture::set_expand_margin_size_all(float p_expand_margin_size) { float StyleBoxTexture::get_expand_margin_size(Margin p_expand_margin) const { - ERR_FAIL_INDEX_V(p_expand_margin, 4, 0); + ERR_FAIL_INDEX_V((int)p_expand_margin, 4, 0); return expand_margin[p_expand_margin]; } @@ -565,8 +565,6 @@ inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color vert_offset = 0; } int adapted_corner_detail = (corner_radius[0] == 0 && corner_radius[1] == 0 && corner_radius[2] == 0 && corner_radius[3] == 0) ? 1 : corner_detail; - int rings = (border_width[0] == 0 && border_width[1] == 0 && border_width[2] == 0 && border_width[3] == 0) ? 1 : 2; - rings = 2; int ring_corner_radius[4]; set_inner_corner_radius(style_rect, ring_rect, corner_radius, ring_corner_radius); @@ -592,7 +590,7 @@ inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color //calculate the vert array for (int corner_index = 0; corner_index < 4; corner_index++) { for (int detail = 0; detail <= adapted_corner_detail; detail++) { - for (int inner_outer = (2 - rings); inner_outer < 2; inner_outer++) { + for (int inner_outer = 0; inner_outer < 2; inner_outer++) { float radius; Color color; Point2 corner_point; @@ -613,19 +611,17 @@ inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color } } - if (rings == 2) { - int vert_count = (adapted_corner_detail + 1) * 4 * rings; - //fill the indices and the colors for the border - for (int i = 0; i < vert_count; i++) { - //poly 1 - indices.push_back(vert_offset + ((i + 0) % vert_count)); - indices.push_back(vert_offset + ((i + 2) % vert_count)); - indices.push_back(vert_offset + ((i + 1) % vert_count)); - //poly 2 - indices.push_back(vert_offset + ((i + 1) % vert_count)); - indices.push_back(vert_offset + ((i + 2) % vert_count)); - indices.push_back(vert_offset + ((i + 3) % vert_count)); - } + int vert_count = (adapted_corner_detail + 1) * 4 * 2; + //fill the indices and the colors for the border + for (int i = 0; i < vert_count; i++) { + //poly 1 + indices.push_back(vert_offset + ((i + 0) % vert_count)); + indices.push_back(vert_offset + ((i + 2) % vert_count)); + indices.push_back(vert_offset + ((i + 1) % vert_count)); + //poly 2 + indices.push_back(vert_offset + ((i + 1) % vert_count)); + indices.push_back(vert_offset + ((i + 2) % vert_count)); + indices.push_back(vert_offset + ((i + 3) % vert_count)); } } diff --git a/scene/resources/style_box.h b/scene/resources/style_box.h index ed193a1ab4..df3ebe1c36 100644 --- a/scene/resources/style_box.h +++ b/scene/resources/style_box.h @@ -31,7 +31,7 @@ #ifndef STYLE_BOX_H #define STYLE_BOX_H -#include "resource.h" +#include "core/resource.h" #include "scene/resources/texture.h" #include "servers/visual_server.h" /** diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index 81fabf40fe..5d4c7861e3 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -29,7 +29,8 @@ /*************************************************************************/ #include "surface_tool.h" -#include "method_bind_ext.gen.inc" + +#include "core/method_bind_ext.gen.inc" #define _VERTEX_SNAP 0.0001 #define EQ_VERTEX_DIST 0.00001 diff --git a/scene/resources/text_file.cpp b/scene/resources/text_file.cpp index e2fe0adfc5..2af24ad2d5 100644 --- a/scene/resources/text_file.cpp +++ b/scene/resources/text_file.cpp @@ -30,7 +30,7 @@ #include "text_file.h" -#include "os/file_access.h" +#include "core/os/file_access.h" bool TextFile::has_text() const { return text != ""; diff --git a/scene/resources/text_file.h b/scene/resources/text_file.h index 40b648eebb..3abc769dc6 100644 --- a/scene/resources/text_file.h +++ b/scene/resources/text_file.h @@ -31,8 +31,8 @@ #ifndef TEXTFILE_H #define TEXTFILE_H -#include "io/resource_loader.h" -#include "io/resource_saver.h" +#include "core/io/resource_loader.h" +#include "core/io/resource_saver.h" class TextFile : public Resource { diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index 811e5c3d2c..682bfebdd2 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -29,11 +29,12 @@ /*************************************************************************/ #include "texture.h" -#include "bit_mask.h" + +#include "core/core_string_names.h" +#include "core/io/image_loader.h" #include "core/method_bind_ext.gen.inc" #include "core/os/os.h" -#include "core_string_names.h" -#include "io/image_loader.h" +#include "scene/resources/bit_mask.h" Size2 Texture::get_size() const { @@ -206,6 +207,9 @@ void ImageTexture::set_flags(uint32_t p_flags) { flags=p_flags|cube; */ flags = p_flags; + if (w == 0 || h == 0) { + return; //uninitialized, do not set to texture + } VisualServer::get_singleton()->texture_set_flags(texture, p_flags); } @@ -422,6 +426,15 @@ ImageTexture::~ImageTexture() { ////////////////////////////////////////// +void StreamTexture::set_path(const String &p_path, bool p_take_over) { + + if (texture.is_valid()) { + VisualServer::get_singleton()->texture_set_path(texture, p_path); + } + + Resource::set_path(p_path, p_take_over); +} + void StreamTexture::_requested_3d(void *p_ud) { StreamTexture *st = (StreamTexture *)p_ud; @@ -1606,7 +1619,7 @@ void GradientTexture::_bind_methods() { ClassDB::bind_method(D_METHOD("_update"), &GradientTexture::_update); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_gradient", "get_gradient"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "width"), "set_width", "get_width"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,2048,1,or_greater"), "set_width", "get_width"); } void GradientTexture::set_gradient(Ref<Gradient> p_gradient) { @@ -1632,16 +1645,17 @@ void GradientTexture::_queue_update() { if (update_pending) return; + update_pending = true; call_deferred("_update"); } void GradientTexture::_update() { + update_pending = false; + if (gradient.is_null()) return; - update_pending = false; - PoolVector<uint8_t> data; data.resize(width * 4); { @@ -1944,8 +1958,8 @@ void AnimatedTexture::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::REAL, "fps", PROPERTY_HINT_RANGE, "0,1024,0.1"), "set_fps", "get_fps"); for (int i = 0; i < MAX_FRAMES; i++) { - ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "frame_" + itos(i) + "/texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_frame_texture", "get_frame_texture", i); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "frame_" + itos(i) + "/delay_sec", PROPERTY_HINT_RANGE, "0.0,16.0,0.01"), "set_frame_delay", "get_frame_delay", i); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "frame_" + itos(i) + "/texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_frame_texture", "get_frame_texture", i); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "frame_" + itos(i) + "/delay_sec", PROPERTY_HINT_RANGE, "0.0,16.0,0.01", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_frame_delay", "get_frame_delay", i); } } diff --git a/scene/resources/texture.h b/scene/resources/texture.h index 79e6d2cdf9..e9b69e9cb1 100644 --- a/scene/resources/texture.h +++ b/scene/resources/texture.h @@ -31,15 +31,16 @@ #ifndef TEXTURE_H #define TEXTURE_H -#include "curve.h" -#include "io/resource_loader.h" -#include "os/mutex.h" -#include "os/rw_lock.h" -#include "os/thread_safe.h" -#include "rect2.h" -#include "resource.h" +#include "core/io/resource_loader.h" +#include "core/math/rect2.h" +#include "core/os/mutex.h" +#include "core/os/rw_lock.h" +#include "core/os/thread_safe.h" +#include "core/resource.h" #include "scene/resources/color_ramp.h" +#include "scene/resources/curve.h" #include "servers/visual_server.h" + /** @author Juan Linietsky <reduzio@gmail.com> */ @@ -218,6 +219,8 @@ public: int get_height() const; virtual RID get_rid() const; + virtual void set_path(const String &p_path, bool p_take_over); + virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture> &p_normal_map = Ref<Texture>()) const; virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture> &p_normal_map = Ref<Texture>()) const; virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture> &p_normal_map = Ref<Texture>(), bool p_clip_uv = true) const; diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp index f903669fc7..b102d477f2 100644 --- a/scene/resources/theme.cpp +++ b/scene/resources/theme.cpp @@ -29,8 +29,8 @@ /*************************************************************************/ #include "theme.h" -#include "os/file_access.h" -#include "print_string.h" +#include "core/os/file_access.h" +#include "core/print_string.h" Ref<Theme> Theme::default_theme; diff --git a/scene/resources/theme.h b/scene/resources/theme.h index e0d4038e7e..0b76e95f18 100644 --- a/scene/resources/theme.h +++ b/scene/resources/theme.h @@ -31,8 +31,8 @@ #ifndef THEME_H #define THEME_H -#include "io/resource_loader.h" -#include "resource.h" +#include "core/io/resource_loader.h" +#include "core/resource.h" #include "scene/resources/font.h" #include "scene/resources/shader.h" #include "scene/resources/style_box.h" diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 3d2b6c36de..f852ecd7eb 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "tile_set.h" -#include "array.h" +#include "core/array.h" bool TileSet::_set(const StringName &p_name, const Variant &p_value) { @@ -59,7 +59,13 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { tile_set_region(id, p_value); else if (what == "tile_mode") tile_set_tile_mode(id, (TileMode)((int)p_value)); - else if (what.left(9) == "autotile/") { + else if (what == "is_autotile") { + // backward compatibility for Godot 3.0.x + // autotile used to be a bool, it's now an enum + bool is_autotile = p_value; + if (is_autotile) + tile_set_tile_mode(id, AUTO_TILE); + } else if (what.left(9) == "autotile/") { what = what.right(9); if (what == "bitmask_mode") autotile_set_bitmask_mode(id, (BitmaskMode)((int)p_value)); @@ -934,6 +940,8 @@ void TileSet::_bind_methods() { ClassDB::bind_method(D_METHOD("create_tile", "id"), &TileSet::create_tile); ClassDB::bind_method(D_METHOD("autotile_set_bitmask_mode", "id", "mode"), &TileSet::autotile_set_bitmask_mode); ClassDB::bind_method(D_METHOD("autotile_get_bitmask_mode", "id"), &TileSet::autotile_get_bitmask_mode); + ClassDB::bind_method(D_METHOD("autotile_set_size", "id", "size"), &TileSet::autotile_set_size); + ClassDB::bind_method(D_METHOD("autotile_get_size", "id"), &TileSet::autotile_get_size); ClassDB::bind_method(D_METHOD("tile_set_name", "id", "name"), &TileSet::tile_set_name); ClassDB::bind_method(D_METHOD("tile_get_name", "id"), &TileSet::tile_get_name); ClassDB::bind_method(D_METHOD("tile_set_texture", "id", "texture"), &TileSet::tile_set_texture); @@ -950,6 +958,8 @@ void TileSet::_bind_methods() { ClassDB::bind_method(D_METHOD("tile_get_region", "id"), &TileSet::tile_get_region); ClassDB::bind_method(D_METHOD("tile_set_shape", "id", "shape_id", "shape"), &TileSet::tile_set_shape); ClassDB::bind_method(D_METHOD("tile_get_shape", "id", "shape_id"), &TileSet::tile_get_shape); + ClassDB::bind_method(D_METHOD("tile_set_shape_offset", "id", "shape_id", "shape_offset"), &TileSet::tile_set_shape_offset); + ClassDB::bind_method(D_METHOD("tile_get_shape_offset", "id", "shape_id"), &TileSet::tile_get_shape_offset); ClassDB::bind_method(D_METHOD("tile_set_shape_transform", "id", "shape_id", "shape_transform"), &TileSet::tile_set_shape_transform); ClassDB::bind_method(D_METHOD("tile_get_shape_transform", "id", "shape_id"), &TileSet::tile_get_shape_transform); ClassDB::bind_method(D_METHOD("tile_set_shape_one_way", "id", "shape_id", "one_way"), &TileSet::tile_set_shape_one_way); diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index 40eee2700d..1802bf12b6 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -32,7 +32,7 @@ #define TILE_SET_H #include "core/array.h" -#include "resource.h" +#include "core/resource.h" #include "scene/2d/light_occluder_2d.h" #include "scene/2d/navigation_polygon.h" #include "scene/resources/shape_2d.h" @@ -80,8 +80,8 @@ public: struct AutotileData { BitmaskMode bitmask_mode; - int spacing; Size2 size; + int spacing; Vector2 icon_coord; Map<Vector2, uint16_t> flags; Map<Vector2, Ref<OccluderPolygon2D> > occluder_map; @@ -90,11 +90,10 @@ public: // Default size to prevent invalid value explicit AutotileData() : + bitmask_mode(BITMASK_2X2), size(64, 64), spacing(0), - icon_coord(0, 0) { - bitmask_mode = BITMASK_2X2; - } + icon_coord(0, 0) {} }; private: @@ -111,8 +110,8 @@ private: Vector2 navigation_polygon_offset; Ref<NavigationPolygon> navigation_polygon; Ref<ShaderMaterial> material; - Color modulate; TileMode tile_mode; + Color modulate; AutotileData autotile_data; int z_index; diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index dac12205b6..96b9cfa137 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -1,6 +1,36 @@ +/*************************************************************************/ +/* visual_shader.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_shader.h" +#include "core/vmap.h" #include "servers/visual/shader_types.h" -#include "vmap.h" void VisualShaderNode::set_output_port_for_preview(int p_index) { @@ -384,6 +414,10 @@ Shader::Mode VisualShader::get_mode() const { return shader_mode; } +bool VisualShader::is_text_shader() const { + return false; +} + String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port, Vector<DefaultTextureParam> &default_tex_params) const { Ref<VisualShaderNode> node = get_node(p_type, p_node); @@ -762,10 +796,10 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui val.basis.transpose(); inputs[i] = "n_in" + itos(node) + "p" + itos(i); Array values; - for (int i = 0; i < 3; i++) { - values.push_back(val.basis[i].x); - values.push_back(val.basis[i].y); - values.push_back(val.basis[i].z); + for (int j = 0; j < 3; j++) { + values.push_back(val.basis[j].x); + values.push_back(val.basis[j].y); + values.push_back(val.basis[j].z); } values.push_back(val.origin.x); values.push_back(val.origin.y); diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h index 6ff1c9a9fe..2867daac3a 100644 --- a/scene/resources/visual_shader.h +++ b/scene/resources/visual_shader.h @@ -1,8 +1,38 @@ +/*************************************************************************/ +/* visual_shader.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 VISUAL_SHADER_H #define VISUAL_SHADER_H +#include "core/string_builder.h" #include "scene/resources/shader.h" -#include "string_builder.h" class VisualShaderNodeUniform; class VisualShaderNode; @@ -111,6 +141,8 @@ public: void set_mode(Mode p_mode); virtual Mode get_mode() const; + virtual bool is_text_shader() const; + void set_graph_offset(const Vector2 &p_offset); Vector2 get_graph_offset() const; diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index 98ecdbdf30..d011b102a2 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* visual_shader_nodes.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_shader_nodes.h" ////////////// Scalar diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h index 2ede36fbc8..3057f7239a 100644 --- a/scene/resources/visual_shader_nodes.h +++ b/scene/resources/visual_shader_nodes.h @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* visual_shader_nodes.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 VISUAL_SHADER_NODES_H #define VISUAL_SHADER_NODES_H diff --git a/scene/resources/world.cpp b/scene/resources/world.cpp index 82183d24e7..b4588cd87c 100644 --- a/scene/resources/world.cpp +++ b/scene/resources/world.cpp @@ -30,8 +30,8 @@ #include "world.h" -#include "camera_matrix.h" -#include "octree.h" +#include "core/math/camera_matrix.h" +#include "core/math/octree.h" #include "scene/3d/camera.h" #include "scene/3d/visibility_notifier.h" #include "scene/scene_string_names.h" diff --git a/scene/resources/world.h b/scene/resources/world.h index 54bdf25784..4ba6b13476 100644 --- a/scene/resources/world.h +++ b/scene/resources/world.h @@ -31,14 +31,14 @@ #ifndef WORLD_H #define WORLD_H -#include "resource.h" +#include "core/resource.h" #include "scene/resources/environment.h" #include "servers/physics_server.h" #include "servers/visual_server.h" -class SpatialIndexer; class Camera; class VisibilityNotifier; +struct SpatialIndexer; class World : public Resource { GDCLASS(World, Resource); diff --git a/scene/resources/world_2d.cpp b/scene/resources/world_2d.cpp index bed6ffd1bd..dd78d04104 100644 --- a/scene/resources/world_2d.cpp +++ b/scene/resources/world_2d.cpp @@ -29,13 +29,13 @@ /*************************************************************************/ #include "world_2d.h" -#include "servers/physics_2d_server.h" -#include "servers/visual_server.h" -//#include "servers/spatial_sound_2d_server.h" -#include "project_settings.h" + +#include "core/project_settings.h" #include "scene/2d/camera_2d.h" #include "scene/2d/visibility_notifier_2d.h" #include "scene/main/viewport.h" +#include "servers/physics_2d_server.h" +#include "servers/visual_server.h" struct SpatialIndexer2D { diff --git a/scene/resources/world_2d.h b/scene/resources/world_2d.h index 59f34e32f2..88ad392f85 100644 --- a/scene/resources/world_2d.h +++ b/scene/resources/world_2d.h @@ -31,13 +31,13 @@ #ifndef WORLD_2D_H #define WORLD_2D_H -#include "project_settings.h" -#include "resource.h" +#include "core/project_settings.h" +#include "core/resource.h" #include "servers/physics_2d_server.h" -class SpatialIndexer2D; class VisibilityNotifier2D; class Viewport; +struct SpatialIndexer2D; class World2D : public Resource { diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h index dbbcf79b9f..25e2c5d4a6 100644 --- a/scene/scene_string_names.h +++ b/scene/scene_string_names.h @@ -31,8 +31,8 @@ #ifndef SCENE_STRING_NAMES_H #define SCENE_STRING_NAMES_H -#include "node_path.h" -#include "string_db.h" +#include "core/node_path.h" +#include "core/string_db.h" class SceneStringNames { friend void register_scene_types(); diff --git a/servers/SCsub b/servers/SCsub index 252a18ffd3..f4af347fe6 100644 --- a/servers/SCsub +++ b/servers/SCsub @@ -5,8 +5,6 @@ Import('env') env.servers_sources = [] env.add_source_files(env.servers_sources, "*.cpp") -Export('env') - SConscript('arvr/SCsub') SConscript('physics/SCsub') SConscript('physics_2d/SCsub') diff --git a/servers/arvr/SCsub b/servers/arvr/SCsub index ccc76e823f..d730144861 100644 --- a/servers/arvr/SCsub +++ b/servers/arvr/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.servers_sources, "*.cpp") - -Export('env') diff --git a/servers/arvr/arvr_interface.h b/servers/arvr/arvr_interface.h index 910b401db9..b26ee6cb1b 100644 --- a/servers/arvr/arvr_interface.h +++ b/servers/arvr/arvr_interface.h @@ -32,7 +32,7 @@ #define ARVR_INTERFACE_H #include "core/math/camera_matrix.h" -#include "os/thread_safe.h" +#include "core/os/thread_safe.h" #include "scene/main/viewport.h" #include "servers/arvr_server.h" diff --git a/servers/arvr/arvr_positional_tracker.h b/servers/arvr/arvr_positional_tracker.h index 525e47a681..69b89f11d3 100644 --- a/servers/arvr/arvr_positional_tracker.h +++ b/servers/arvr/arvr_positional_tracker.h @@ -31,7 +31,7 @@ #ifndef ARVR_POSITIONAL_TRACKER_H #define ARVR_POSITIONAL_TRACKER_H -#include "os/thread_safe.h" +#include "core/os/thread_safe.h" #include "servers/arvr_server.h" /** diff --git a/servers/arvr_server.cpp b/servers/arvr_server.cpp index 2040377dd4..55f8ea8f5b 100644 --- a/servers/arvr_server.cpp +++ b/servers/arvr_server.cpp @@ -31,7 +31,7 @@ #include "arvr_server.h" #include "arvr/arvr_interface.h" #include "arvr/arvr_positional_tracker.h" -#include "project_settings.h" +#include "core/project_settings.h" ARVRServer *ARVRServer::singleton = NULL; diff --git a/servers/arvr_server.h b/servers/arvr_server.h index 1f4d84fe19..a28b91c225 100644 --- a/servers/arvr_server.h +++ b/servers/arvr_server.h @@ -31,11 +31,11 @@ #ifndef ARVR_SERVER_H #define ARVR_SERVER_H -#include "os/os.h" -#include "os/thread_safe.h" -#include "reference.h" -#include "rid.h" -#include "variant.h" +#include "core/os/os.h" +#include "core/os/thread_safe.h" +#include "core/reference.h" +#include "core/rid.h" +#include "core/variant.h" class ARVRInterface; class ARVRPositionalTracker; @@ -87,7 +87,7 @@ private: uint64_t last_process_usec; /* for frame timing, usec when we did our processing */ uint64_t last_commit_usec; /* for frame timing, usec when we finished committing both eyes */ - uint64_t last_frame_usec; /* time it took between process and commiting, we should probably average this over the last x frames */ + uint64_t last_frame_usec; /* time it took between process and committing, we should probably average this over the last x frames */ protected: static ARVRServer *singleton; diff --git a/servers/audio/SCsub b/servers/audio/SCsub index afaffcfe93..3c18c18043 100644 --- a/servers/audio/SCsub +++ b/servers/audio/SCsub @@ -4,6 +4,4 @@ Import('env') env.add_source_files(env.servers_sources, "*.cpp") -Export('env') - SConscript("effects/SCsub") diff --git a/servers/audio/audio_driver_dummy.cpp b/servers/audio/audio_driver_dummy.cpp index be36c3b748..512ff5da7b 100644 --- a/servers/audio/audio_driver_dummy.cpp +++ b/servers/audio/audio_driver_dummy.cpp @@ -30,8 +30,8 @@ #include "audio_driver_dummy.h" -#include "os/os.h" -#include "project_settings.h" +#include "core/os/os.h" +#include "core/project_settings.h" Error AudioDriverDummy::init() { diff --git a/servers/audio/audio_effect.h b/servers/audio/audio_effect.h index b950e824c0..bbabedbc30 100644 --- a/servers/audio/audio_effect.h +++ b/servers/audio/audio_effect.h @@ -31,8 +31,8 @@ #ifndef AUDIOEFFECT_H #define AUDIOEFFECT_H -#include "audio_frame.h" -#include "resource.h" +#include "core/math/audio_frame.h" +#include "core/resource.h" class AudioEffectInstance : public Reference { GDCLASS(AudioEffectInstance, Reference) diff --git a/servers/audio/audio_filter_sw.h b/servers/audio/audio_filter_sw.h index 4174f9bd51..d137ed8ff9 100644 --- a/servers/audio/audio_filter_sw.h +++ b/servers/audio/audio_filter_sw.h @@ -31,7 +31,7 @@ #ifndef AUDIO_FILTER_SW_H #define AUDIO_FILTER_SW_H -#include "math_funcs.h" +#include "core/math/math_funcs.h" class AudioFilterSW { public: diff --git a/servers/audio/audio_rb_resampler.cpp b/servers/audio/audio_rb_resampler.cpp index 3414351681..84a87de2e2 100644 --- a/servers/audio/audio_rb_resampler.cpp +++ b/servers/audio/audio_rb_resampler.cpp @@ -30,7 +30,7 @@ #include "audio_rb_resampler.h" #include "core/math/math_funcs.h" -#include "os/os.h" +#include "core/os/os.h" #include "servers/audio_server.h" int AudioRBResampler::get_channel_count() const { @@ -82,19 +82,28 @@ uint32_t AudioRBResampler::_resample(AudioFrame *p_dest, int p_todo, int32_t p_i // For now, channels higher than stereo are almost ignored if (C == 4) { + // FIXME: v2 and v3 are not being used (thus were commented out to prevent + // compilation warnings, but they should likely be uncommented *and* used). + // See also C == 6 with similar issues. float v0 = rb[(pos << 2) + 0]; float v1 = rb[(pos << 2) + 1]; + /* float v2 = rb[(pos << 2) + 2]; float v3 = rb[(pos << 2) + 3]; + */ float v0n = rb[(pos_next << 2) + 0]; float v1n = rb[(pos_next << 2) + 1]; + /* float v2n = rb[(pos_next << 2) + 2]; float v3n = rb[(pos_next << 2) + 3]; + */ v0 += (v0n - v0) * frac; v1 += (v1n - v1) * frac; + /* v2 += (v2n - v2) * frac; v3 += (v3n - v3) * frac; + */ p_dest[i] = AudioFrame(v0, v1); } @@ -104,6 +113,7 @@ uint32_t AudioRBResampler::_resample(AudioFrame *p_dest, int p_todo, int32_t p_i // 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]; float v3 = rb[(pos * 6) + 3]; float v4 = rb[(pos * 6) + 4]; @@ -114,6 +124,7 @@ uint32_t AudioRBResampler::_resample(AudioFrame *p_dest, int p_todo, int32_t p_i float v3n = rb[(pos_next * 6) + 3]; float v4n = rb[(pos_next * 6) + 4]; float v5n = rb[(pos_next * 6) + 5]; + */ p_dest[i] = AudioFrame(v0, v1); } @@ -153,7 +164,7 @@ bool AudioRBResampler::mix(AudioFrame *p_dest, int p_frames) { } // Fill zeros (silence) for the rest of frames - for (uint32_t i = target_todo; i < p_frames; i++) { + for (int i = target_todo; i < p_frames; i++) { p_dest[i] = AudioFrame(0, 0); } } diff --git a/servers/audio/audio_rb_resampler.h b/servers/audio/audio_rb_resampler.h index 6ef79c93fa..b21c480fc4 100644 --- a/servers/audio/audio_rb_resampler.h +++ b/servers/audio/audio_rb_resampler.h @@ -31,9 +31,9 @@ #ifndef AUDIO_RB_RESAMPLER_H #define AUDIO_RB_RESAMPLER_H -#include "os/memory.h" +#include "core/os/memory.h" +#include "core/typedefs.h" #include "servers/audio_server.h" -#include "typedefs.h" struct AudioRBResampler { diff --git a/servers/audio/audio_stream.cpp b/servers/audio/audio_stream.cpp index eef8aba0c4..02a0bed964 100644 --- a/servers/audio/audio_stream.cpp +++ b/servers/audio/audio_stream.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "audio_stream.h" -#include "os/os.h" +#include "core/os/os.h" ////////////////////////////// @@ -136,16 +136,20 @@ void AudioStreamPlaybackMicrophone::_mix_internal(AudioFrame *p_buffer, int p_fr Vector<int32_t> buf = AudioDriver::get_singleton()->get_input_buffer(); unsigned int input_size = AudioDriver::get_singleton()->get_input_size(); + int mix_rate = AudioDriver::get_singleton()->get_mix_rate(); + int playback_delay = MIN(((50 * mix_rate) / 1000) * 2, buf.size() >> 1); +#ifdef DEBUG_ENABLED + unsigned int input_position = AudioDriver::get_singleton()->get_input_position(); +#endif - // p_frames is multipled by two since an AudioFrame is stereo - if ((p_frames + MICROPHONE_PLAYBACK_DELAY * 2) > input_size) { + if (playback_delay > 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) { + if (input_size > input_ofs) { float l = (buf[input_ofs++] >> 16) / 32768.f; if (input_ofs >= buf.size()) { input_ofs = 0; @@ -162,6 +166,12 @@ void AudioStreamPlaybackMicrophone::_mix_internal(AudioFrame *p_buffer, int p_fr } } +#ifdef DEBUG_ENABLED + if (input_ofs > input_position && (input_ofs - input_position) < (p_frames * 2)) { + print_verbose(String(get_class_name()) + " buffer underrun: input_position=" + itos(input_position) + " input_ofs=" + itos(input_ofs) + " input_size=" + itos(input_size)); + } +#endif + AudioDriver::get_singleton()->unlock(); } diff --git a/servers/audio/audio_stream.h b/servers/audio/audio_stream.h index 66e1b6ee2f..f6ed45cc9c 100644 --- a/servers/audio/audio_stream.h +++ b/servers/audio/audio_stream.h @@ -31,8 +31,8 @@ #ifndef AUDIO_STREAM_H #define AUDIO_STREAM_H -#include "image.h" -#include "resource.h" +#include "core/image.h" +#include "core/resource.h" #include "servers/audio/audio_filter_sw.h" #include "servers/audio_server.h" @@ -122,8 +122,6 @@ class AudioStreamPlaybackMicrophone : public AudioStreamPlaybackResampled { GDCLASS(AudioStreamPlaybackMicrophone, AudioStreamPlayback) friend class AudioStreamMicrophone; - const int MICROPHONE_PLAYBACK_DELAY = 256; - bool active; unsigned int input_ofs; diff --git a/servers/audio/effects/SCsub b/servers/audio/effects/SCsub index ccc76e823f..d730144861 100644 --- a/servers/audio/effects/SCsub +++ b/servers/audio/effects/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.servers_sources, "*.cpp") - -Export('env') diff --git a/servers/audio/effects/audio_effect_chorus.cpp b/servers/audio/effects/audio_effect_chorus.cpp index fd9e3311e7..3c88d050f6 100644 --- a/servers/audio/effects/audio_effect_chorus.cpp +++ b/servers/audio/effects/audio_effect_chorus.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "audio_effect_chorus.h" -#include "math_funcs.h" +#include "core/math/math_funcs.h" #include "servers/audio_server.h" void AudioEffectChorusInstance::process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) { diff --git a/servers/audio/effects/audio_effect_delay.cpp b/servers/audio/effects/audio_effect_delay.cpp index e3af898afa..abc10ee096 100644 --- a/servers/audio/effects/audio_effect_delay.cpp +++ b/servers/audio/effects/audio_effect_delay.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "audio_effect_delay.h" -#include "math_funcs.h" +#include "core/math/math_funcs.h" #include "servers/audio_server.h" void AudioEffectDelayInstance::process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) { diff --git a/servers/audio/effects/audio_effect_distortion.cpp b/servers/audio/effects/audio_effect_distortion.cpp index c9ac0db644..0422083363 100644 --- a/servers/audio/effects/audio_effect_distortion.cpp +++ b/servers/audio/effects/audio_effect_distortion.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "audio_effect_distortion.h" -#include "math_funcs.h" +#include "core/math/math_funcs.h" #include "servers/audio_server.h" void AudioEffectDistortionInstance::process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) { diff --git a/servers/audio/effects/audio_effect_phaser.cpp b/servers/audio/effects/audio_effect_phaser.cpp index c0a9bd773d..3151111ec8 100644 --- a/servers/audio/effects/audio_effect_phaser.cpp +++ b/servers/audio/effects/audio_effect_phaser.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "audio_effect_phaser.h" -#include "math_funcs.h" +#include "core/math/math_funcs.h" #include "servers/audio_server.h" void AudioEffectPhaserInstance::process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) { diff --git a/servers/audio/effects/audio_effect_pitch_shift.cpp b/servers/audio/effects/audio_effect_pitch_shift.cpp index c6d1217308..f5ca8f2da4 100644 --- a/servers/audio/effects/audio_effect_pitch_shift.cpp +++ b/servers/audio/effects/audio_effect_pitch_shift.cpp @@ -30,7 +30,7 @@ #include "audio_effect_pitch_shift.h" -#include "math_funcs.h" +#include "core/math/math_funcs.h" #include "servers/audio_server.h" /* Thirdparty code, so disable clang-format with Godot style */ diff --git a/servers/audio/effects/audio_effect_record.cpp b/servers/audio/effects/audio_effect_record.cpp index 78ba658ed8..a0094f66b8 100644 --- a/servers/audio/effects/audio_effect_record.cpp +++ b/servers/audio/effects/audio_effect_record.cpp @@ -44,7 +44,7 @@ void AudioEffectRecordInstance::process(const AudioFrame *p_src_frames, AudioFra } } -bool AudioEffectRecordInstance::process_silence() { +bool AudioEffectRecordInstance::process_silence() const { return true; } diff --git a/servers/audio/effects/audio_effect_record.h b/servers/audio/effects/audio_effect_record.h index e4f5ba8a23..4b8ee2bcdd 100644 --- a/servers/audio/effects/audio_effect_record.h +++ b/servers/audio/effects/audio_effect_record.h @@ -31,11 +31,11 @@ #ifndef AUDIOEFFECTRECORD_H #define AUDIOEFFECTRECORD_H +#include "core/io/marshalls.h" +#include "core/os/file_access.h" +#include "core/os/os.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" @@ -49,7 +49,7 @@ class AudioEffectRecordInstance : public AudioEffectInstance { bool is_recording; Thread *io_thread; - bool thread_active = false; + bool thread_active; Vector<AudioFrame> ring_buffer; Vector<float> recording_data; @@ -66,7 +66,10 @@ class AudioEffectRecordInstance : public AudioEffectInstance { public: void init(); virtual void process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count); - virtual bool process_silence(); + virtual bool process_silence() const; + + AudioEffectRecordInstance() : + thread_active(false) {} }; class AudioEffectRecord : public AudioEffect { diff --git a/servers/audio/effects/eq.cpp b/servers/audio/effects/eq.cpp index b15fc7ecf4..70ac70a5a8 100644 --- a/servers/audio/effects/eq.cpp +++ b/servers/audio/effects/eq.cpp @@ -31,8 +31,8 @@ // Author: reduzio@gmail.com (C) 2006 #include "eq.h" -#include "error_macros.h" -#include "math_funcs.h" +#include "core/error_macros.h" +#include "core/math/math_funcs.h" #include <math.h> #define POW2(v) ((v) * (v)) diff --git a/servers/audio/effects/eq.h b/servers/audio/effects/eq.h index 5c654529c3..c06f1f8bf8 100644 --- a/servers/audio/effects/eq.h +++ b/servers/audio/effects/eq.h @@ -33,8 +33,8 @@ #ifndef EQ_FILTER_H #define EQ_FILTER_H -#include "typedefs.h" -#include "vector.h" +#include "core/typedefs.h" +#include "core/vector.h" /** @author Juan Linietsky diff --git a/servers/audio/effects/reverb.cpp b/servers/audio/effects/reverb.cpp index 5e31202e58..b032c91da2 100644 --- a/servers/audio/effects/reverb.cpp +++ b/servers/audio/effects/reverb.cpp @@ -31,27 +31,27 @@ // Author: Juan Linietsky <reduzio@gmail.com>, (C) 2006 #include "reverb.h" -#include "math_funcs.h" +#include "core/math/math_funcs.h" #include <math.h> const float Reverb::comb_tunings[MAX_COMBS] = { //freeverb comb tunings - 0.025306122448979593, - 0.026938775510204082, - 0.028956916099773241, - 0.03074829931972789, - 0.032244897959183672, - 0.03380952380952381, - 0.035306122448979592, - 0.036666666666666667 + 0.025306122448979593f, + 0.026938775510204082f, + 0.028956916099773241f, + 0.03074829931972789f, + 0.032244897959183672f, + 0.03380952380952381f, + 0.035306122448979592f, + 0.036666666666666667f }; const float Reverb::allpass_tunings[MAX_ALLPASS] = { //freeverb allpass tunings - 0.0051020408163265302, - 0.007732426303854875, - 0.01, - 0.012607709750566893 + 0.0051020408163265302f, + 0.007732426303854875f, + 0.01f, + 0.012607709750566893f }; void Reverb::process(float *p_src, float *p_dst, int p_frames) { diff --git a/servers/audio/effects/reverb.h b/servers/audio/effects/reverb.h index c3d3873dcd..03cf197456 100644 --- a/servers/audio/effects/reverb.h +++ b/servers/audio/effects/reverb.h @@ -33,9 +33,9 @@ #ifndef REVERB_H #define REVERB_H -#include "audio_frame.h" -#include "os/memory.h" -#include "typedefs.h" +#include "core/math/audio_frame.h" +#include "core/os/memory.h" +#include "core/typedefs.h" class Reverb { public: diff --git a/servers/audio/reverb_sw.cpp b/servers/audio/reverb_sw.cpp index 52e7699deb..d078da38b4 100644 --- a/servers/audio/reverb_sw.cpp +++ b/servers/audio/reverb_sw.cpp @@ -29,33 +29,21 @@ /*************************************************************************/ #include "reverb_sw.h" -#include "print_string.h" -#include "stdlib.h" + +#include "core/print_string.h" + +#include <stdlib.h> + #define SETMIN(x, y) (x) = MIN((x), (y)) + #define rangeloop(c, min, max) \ for ((c) = (min); (c) < (max); (c)++) #define ABSDIFF(x, y) \ (((x) < (y)) ? ((y) - (x)) : ((x) - (y))) -#ifdef bleh_MSC_VER - -#if _MSC_VER >= 1400 -_FORCE_INLINE_ int32_tMULSHIFT_S32( - int32_t Factor1, - int32_t Factor2, - uint8_t Bits) { - - return __ll_rshift( - __emul(Factor1, Factor2), - Bits); -} -#endif - -#else #define MULSHIFT_S32(Factor1, Factor2, Bits) \ ((int)(((int64_t)(Factor1) * (Factor2)) >> (Bits))) -#endif struct ReverbParamsSW { unsigned int BufferSize; // Required buffer size diff --git a/servers/audio/reverb_sw.h b/servers/audio/reverb_sw.h index 15f9a43183..01977d1f61 100644 --- a/servers/audio/reverb_sw.h +++ b/servers/audio/reverb_sw.h @@ -31,10 +31,10 @@ #ifndef REVERB_SW_H #define REVERB_SW_H -#include "os/memory.h" -#include "typedefs.h" +#include "core/os/memory.h" +#include "core/typedefs.h" -class ReverbParamsSW; +struct ReverbParamsSW; class ReverbSW { public: diff --git a/servers/audio/voice_rb_sw.h b/servers/audio/voice_rb_sw.h index 42045428a8..fc02cc1827 100644 --- a/servers/audio/voice_rb_sw.h +++ b/servers/audio/voice_rb_sw.h @@ -31,7 +31,7 @@ #ifndef VOICE_RB_SW_H #define VOICE_RB_SW_H -#include "os/os.h" +#include "core/os/os.h" #include "servers/audio_server.h" class VoiceRBSW { public: diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp index b737f4681d..b41fcac8fa 100644 --- a/servers/audio_server.cpp +++ b/servers/audio_server.cpp @@ -29,10 +29,10 @@ /*************************************************************************/ #include "audio_server.h" -#include "io/resource_loader.h" -#include "os/file_access.h" -#include "os/os.h" -#include "project_settings.h" +#include "core/io/resource_loader.h" +#include "core/os/file_access.h" +#include "core/os/os.h" +#include "core/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" @@ -80,6 +80,14 @@ double AudioDriver::get_mix_time() const { return total; } +void AudioDriver::input_buffer_init(int driver_buffer_frames) { + + const int input_buffer_channels = 2; + input_buffer.resize(driver_buffer_frames * input_buffer_channels * 4); + input_position = 0; + input_size = 0; +} + void AudioDriver::input_buffer_write(int32_t sample) { input_buffer.write[input_position++] = sample; @@ -185,9 +193,13 @@ void AudioDriverManager::initialize(int p_driver) { if (drivers[i]->init() == OK) { drivers[i]->set_singleton(); - return; + break; } } + + if (driver_count > 1 && String(AudioDriver::get_singleton()->get_name()) == "Dummy") { + WARN_PRINT("All audio drivers failed, falling back to the dummy driver."); + } } AudioDriver *AudioDriverManager::get_driver(int p_driver) { @@ -922,6 +934,7 @@ void AudioServer::init() { channel_disable_threshold_db = GLOBAL_DEF_RST("audio/channel_disable_threshold_db", -60.0); channel_disable_frames = float(GLOBAL_DEF_RST("audio/channel_disable_time", 2.0)) * get_mix_rate(); + ProjectSettings::get_singleton()->set_custom_property_info("audio/channel_disable_time", PropertyInfo(Variant::REAL, "audio/channel_disable_time", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater")); buffer_size = 1024; //hardcoded for now init_channels_and_buffers(); @@ -1293,6 +1306,7 @@ void AudioServer::_bind_methods() { ADD_SIGNAL(MethodInfo("bus_layout_changed")); BIND_ENUM_CONSTANT(SPEAKER_MODE_STEREO); + BIND_ENUM_CONSTANT(SPEAKER_SURROUND_31); BIND_ENUM_CONSTANT(SPEAKER_SURROUND_51); BIND_ENUM_CONSTANT(SPEAKER_SURROUND_71); } diff --git a/servers/audio_server.h b/servers/audio_server.h index 2663a0f968..b12ca6e589 100644 --- a/servers/audio_server.h +++ b/servers/audio_server.h @@ -31,11 +31,11 @@ #ifndef AUDIO_SERVER_H #define AUDIO_SERVER_H -#include "audio_frame.h" -#include "object.h" -#include "os/os.h" +#include "core/math/audio_frame.h" +#include "core/object.h" +#include "core/os/os.h" +#include "core/variant.h" #include "servers/audio/audio_effect.h" -#include "variant.h" class AudioDriverDummy; class AudioStream; @@ -59,6 +59,7 @@ protected: 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_init(int driver_buffer_frames); void input_buffer_write(int32_t sample); #ifdef DEBUG_ENABLED diff --git a/servers/physics/SCsub b/servers/physics/SCsub index c0ee2cc739..c5cc889112 100644 --- a/servers/physics/SCsub +++ b/servers/physics/SCsub @@ -4,6 +4,4 @@ Import('env') env.add_source_files(env.servers_sources, "*.cpp") -Export('env') - SConscript("joints/SCsub") diff --git a/servers/physics/area_pair_sw.cpp b/servers/physics/area_pair_sw.cpp index d2fef0ab77..5e295edcd1 100644 --- a/servers/physics/area_pair_sw.cpp +++ b/servers/physics/area_pair_sw.cpp @@ -147,10 +147,10 @@ Area2PairSW::~Area2PairSW() { if (colliding) { - if (area_b->has_area_monitor_callback() && area_a->is_monitorable()) + if (area_b->has_area_monitor_callback()) area_b->remove_area_from_query(area_a, shape_a, shape_b); - if (area_a->has_area_monitor_callback() && area_b->is_monitorable()) + if (area_a->has_area_monitor_callback()) area_a->remove_area_from_query(area_b, shape_b, shape_a); } diff --git a/servers/physics/area_sw.h b/servers/physics/area_sw.h index ae19b0e04e..63a4db5d02 100644 --- a/servers/physics/area_sw.h +++ b/servers/physics/area_sw.h @@ -32,7 +32,7 @@ #define AREA_SW_H #include "collision_object_sw.h" -#include "self_list.h" +#include "core/self_list.h" #include "servers/physics_server.h" //#include "servers/physics/query_sw.h" diff --git a/servers/physics/body_pair_sw.cpp b/servers/physics/body_pair_sw.cpp index 0ce38e4486..357fc05355 100644 --- a/servers/physics/body_pair_sw.cpp +++ b/servers/physics/body_pair_sw.cpp @@ -31,7 +31,7 @@ #include "body_pair_sw.h" #include "collision_solver_sw.h" -#include "os/os.h" +#include "core/os/os.h" #include "space_sw.h" /* diff --git a/servers/physics/body_pair_sw.h b/servers/physics/body_pair_sw.h index fd85d77718..17ff9d6a88 100644 --- a/servers/physics/body_pair_sw.h +++ b/servers/physics/body_pair_sw.h @@ -76,7 +76,6 @@ class BodyPairSW : public ConstraintSW { Contact contacts[MAX_CONTACTS]; int contact_count; bool collided; - int cc; static void _contact_added_callback(const Vector3 &p_point_A, const Vector3 &p_point_B, void *p_userdata); diff --git a/servers/physics/body_sw.cpp b/servers/physics/body_sw.cpp index cc9681193c..36511f78ce 100644 --- a/servers/physics/body_sw.cpp +++ b/servers/physics/body_sw.cpp @@ -755,10 +755,10 @@ void BodySW::set_kinematic_margin(real_t p_margin) { BodySW::BodySW() : CollisionObjectSW(TYPE_BODY), + locked_axis(0), active_list(this), inertia_update_list(this), - direct_state_query_list(this), - locked_axis(0) { + direct_state_query_list(this) { mode = PhysicsServer::BODY_MODE_RIGID; active = true; diff --git a/servers/physics/body_sw.h b/servers/physics/body_sw.h index 9d7b147fd6..0f7797254e 100644 --- a/servers/physics/body_sw.h +++ b/servers/physics/body_sw.h @@ -33,7 +33,7 @@ #include "area_sw.h" #include "collision_object_sw.h" -#include "vset.h" +#include "core/vset.h" class ConstraintSW; diff --git a/servers/physics/broad_phase_basic.cpp b/servers/physics/broad_phase_basic.cpp index 52483a8b14..d55951b39a 100644 --- a/servers/physics/broad_phase_basic.cpp +++ b/servers/physics/broad_phase_basic.cpp @@ -29,8 +29,8 @@ /*************************************************************************/ #include "broad_phase_basic.h" -#include "list.h" -#include "print_string.h" +#include "core/list.h" +#include "core/print_string.h" BroadPhaseSW::ID BroadPhaseBasic::create(CollisionObjectSW *p_object, int p_subindex) { diff --git a/servers/physics/broad_phase_basic.h b/servers/physics/broad_phase_basic.h index 47fcdb3060..500b8544a1 100644 --- a/servers/physics/broad_phase_basic.h +++ b/servers/physics/broad_phase_basic.h @@ -32,7 +32,7 @@ #define BROAD_PHASE_BASIC_H #include "broad_phase_sw.h" -#include "map.h" +#include "core/map.h" class BroadPhaseBasic : public BroadPhaseSW { diff --git a/servers/physics/broad_phase_octree.h b/servers/physics/broad_phase_octree.h index e7028eba98..c4b8ecb299 100644 --- a/servers/physics/broad_phase_octree.h +++ b/servers/physics/broad_phase_octree.h @@ -32,7 +32,7 @@ #define BROAD_PHASE_OCTREE_H #include "broad_phase_sw.h" -#include "octree.h" +#include "core/math/octree.h" class BroadPhaseOctree : public BroadPhaseSW { diff --git a/servers/physics/broad_phase_sw.h b/servers/physics/broad_phase_sw.h index 7559942cd4..2db1c1dd06 100644 --- a/servers/physics/broad_phase_sw.h +++ b/servers/physics/broad_phase_sw.h @@ -31,8 +31,8 @@ #ifndef BROAD_PHASE_SW_H #define BROAD_PHASE_SW_H -#include "aabb.h" -#include "math_funcs.h" +#include "core/math/aabb.h" +#include "core/math/math_funcs.h" class CollisionObjectSW; diff --git a/servers/physics/collision_object_sw.h b/servers/physics/collision_object_sw.h index b6430b38dc..993799ee10 100644 --- a/servers/physics/collision_object_sw.h +++ b/servers/physics/collision_object_sw.h @@ -32,7 +32,7 @@ #define COLLISION_OBJECT_SW_H #include "broad_phase_sw.h" -#include "self_list.h" +#include "core/self_list.h" #include "servers/physics_server.h" #include "shape_sw.h" diff --git a/servers/physics/collision_solver_sat.cpp b/servers/physics/collision_solver_sat.cpp index 294b1df241..f17f6f7014 100644 --- a/servers/physics/collision_solver_sat.cpp +++ b/servers/physics/collision_solver_sat.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "collision_solver_sat.h" -#include "geometry.h" +#include "core/math/geometry.h" #define _EDGE_IS_VALID_SUPPORT_THRESHOLD 0.02 @@ -98,7 +98,6 @@ static void _generate_contacts_edge_edge(const Vector3 *p_points_A, int p_point_ Vector3 c = rel_A.cross(rel_B).cross(rel_B); - //if ( Math::abs(rel_A.dot(c) )<_EDGE_IS_VALID_SUPPORT_TRESHOLD ) { if (Math::abs(rel_A.dot(c)) < CMP_EPSILON) { // should handle somehow.. diff --git a/servers/physics/collision_solver_sw.cpp b/servers/physics/collision_solver_sw.cpp index 2f2f6d2908..86ef719f6f 100644 --- a/servers/physics/collision_solver_sw.cpp +++ b/servers/physics/collision_solver_sw.cpp @@ -31,7 +31,6 @@ #include "collision_solver_sw.h" #include "collision_solver_sat.h" -#include "collision_solver_sat.h" #include "gjk_epa.h" #define collision_solver sat_calculate_penetration diff --git a/servers/physics/joints/SCsub b/servers/physics/joints/SCsub index ccc76e823f..d730144861 100644 --- a/servers/physics/joints/SCsub +++ b/servers/physics/joints/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.servers_sources, "*.cpp") - -Export('env') diff --git a/servers/physics/joints/cone_twist_joint_sw.cpp b/servers/physics/joints/cone_twist_joint_sw.cpp index c06f27cc57..37fcde4b76 100644 --- a/servers/physics/joints/cone_twist_joint_sw.cpp +++ b/servers/physics/joints/cone_twist_joint_sw.cpp @@ -332,6 +332,7 @@ void ConeTwistJointSW::set_param(PhysicsServer::ConeTwistJointParam p_param, rea m_relaxationFactor = p_value; } break; + case PhysicsServer::CONE_TWIST_MAX: break; // Can't happen, but silences warning } } @@ -358,6 +359,7 @@ real_t ConeTwistJointSW::get_param(PhysicsServer::ConeTwistJointParam p_param) c return m_relaxationFactor; } break; + case PhysicsServer::CONE_TWIST_MAX: break; // Can't happen, but silences warning } return 0; diff --git a/servers/physics/joints/generic_6dof_joint_sw.cpp b/servers/physics/joints/generic_6dof_joint_sw.cpp index c95e5cef32..60505c08c5 100644 --- a/servers/physics/joints/generic_6dof_joint_sw.cpp +++ b/servers/physics/joints/generic_6dof_joint_sw.cpp @@ -83,7 +83,7 @@ int G6DOFRotationalLimitMotorSW::testLimitValue(real_t test_value) { real_t G6DOFRotationalLimitMotorSW::solveAngularLimits( real_t timeStep, Vector3 &axis, real_t jacDiagABInv, BodySW *body0, BodySW *body1) { - if (needApplyTorques() == false) return 0.0f; + if (!needApplyTorques()) return 0.0f; real_t target_velocity = m_targetVelocity; real_t maxMotorForce = m_maxMotorForce; @@ -497,6 +497,13 @@ void Generic6DOFJointSW::set_param(Vector3::Axis p_axis, PhysicsServer::G6DOFJoi m_angularLimits[p_axis].m_maxLimitForce = p_value; } break; + case PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_TARGET_VELOCITY: { + // Not implemented in GodotPhysics backend + } break; + case PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_FORCE_LIMIT: { + // Not implemented in GodotPhysics backend + } break; + case PhysicsServer::G6DOF_JOINT_MAX: break; // Can't happen, but silences warning } } @@ -572,6 +579,13 @@ real_t Generic6DOFJointSW::get_param(Vector3::Axis p_axis, PhysicsServer::G6DOFJ return m_angularLimits[p_axis].m_maxMotorForce; } break; + case PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_TARGET_VELOCITY: { + // Not implemented in GodotPhysics backend + } break; + case PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_FORCE_LIMIT: { + // Not implemented in GodotPhysics backend + } break; + case PhysicsServer::G6DOF_JOINT_MAX: break; // Can't happen, but silences warning } return 0; } @@ -593,6 +607,10 @@ void Generic6DOFJointSW::set_flag(Vector3::Axis p_axis, PhysicsServer::G6DOFJoin m_angularLimits[p_axis].m_enableMotor = p_value; } break; + case PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_MOTOR: { + // Not implemented in GodotPhysics backend + } break; + case PhysicsServer::G6DOF_JOINT_FLAG_MAX: break; // Can't happen, but silences warning } } bool Generic6DOFJointSW::get_flag(Vector3::Axis p_axis, PhysicsServer::G6DOFJointAxisFlag p_flag) const { @@ -611,6 +629,10 @@ bool Generic6DOFJointSW::get_flag(Vector3::Axis p_axis, PhysicsServer::G6DOFJoin return m_angularLimits[p_axis].m_enableMotor; } break; + case PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_MOTOR: { + // Not implemented in GodotPhysics backend + } break; + case PhysicsServer::G6DOF_JOINT_FLAG_MAX: break; // Can't happen, but silences warning } return 0; diff --git a/servers/physics/joints/generic_6dof_joint_sw.h b/servers/physics/joints/generic_6dof_joint_sw.h index b350546c5d..035525c9e6 100644 --- a/servers/physics/joints/generic_6dof_joint_sw.h +++ b/servers/physics/joints/generic_6dof_joint_sw.h @@ -118,14 +118,12 @@ public: //! Is limited bool isLimited() { - if (m_loLimit >= m_hiLimit) return false; - return true; + return (m_loLimit < m_hiLimit); } //! Need apply correction bool needApplyTorques() { - if (m_currentLimit == 0 && m_enableMotor == false) return false; - return true; + return (m_enableMotor || m_currentLimit != 0); } //! calculates error diff --git a/servers/physics/joints/hinge_joint_sw.cpp b/servers/physics/joints/hinge_joint_sw.cpp index 368a349632..50de0e871e 100644 --- a/servers/physics/joints/hinge_joint_sw.cpp +++ b/servers/physics/joints/hinge_joint_sw.cpp @@ -409,6 +409,7 @@ void HingeJointSW::set_param(PhysicsServer::HingeJointParam p_param, real_t p_va case PhysicsServer::HINGE_JOINT_LIMIT_RELAXATION: m_relaxationFactor = p_value; break; case PhysicsServer::HINGE_JOINT_MOTOR_TARGET_VELOCITY: m_motorTargetVelocity = p_value; break; case PhysicsServer::HINGE_JOINT_MOTOR_MAX_IMPULSE: m_maxMotorImpulse = p_value; break; + case PhysicsServer::HINGE_JOINT_MAX: break; // Can't happen, but silences warning } } @@ -424,6 +425,7 @@ real_t HingeJointSW::get_param(PhysicsServer::HingeJointParam p_param) const { case PhysicsServer::HINGE_JOINT_LIMIT_RELAXATION: return m_relaxationFactor; case PhysicsServer::HINGE_JOINT_MOTOR_TARGET_VELOCITY: return m_motorTargetVelocity; case PhysicsServer::HINGE_JOINT_MOTOR_MAX_IMPULSE: return m_maxMotorImpulse; + case PhysicsServer::HINGE_JOINT_MAX: break; // Can't happen, but silences warning } return 0; @@ -434,6 +436,7 @@ void HingeJointSW::set_flag(PhysicsServer::HingeJointFlag p_flag, bool p_value) switch (p_flag) { case PhysicsServer::HINGE_JOINT_FLAG_USE_LIMIT: m_useLimit = p_value; break; case PhysicsServer::HINGE_JOINT_FLAG_ENABLE_MOTOR: m_enableAngularMotor = p_value; break; + case PhysicsServer::HINGE_JOINT_FLAG_MAX: break; // Can't happen, but silences warning } } bool HingeJointSW::get_flag(PhysicsServer::HingeJointFlag p_flag) const { @@ -441,6 +444,7 @@ bool HingeJointSW::get_flag(PhysicsServer::HingeJointFlag p_flag) const { switch (p_flag) { case PhysicsServer::HINGE_JOINT_FLAG_USE_LIMIT: return m_useLimit; case PhysicsServer::HINGE_JOINT_FLAG_ENABLE_MOTOR: return m_enableAngularMotor; + case PhysicsServer::HINGE_JOINT_FLAG_MAX: break; // Can't happen, but silences warning } return false; diff --git a/servers/physics/joints/jacobian_entry_sw.h b/servers/physics/joints/jacobian_entry_sw.h index 42c90c9ae9..4bc1255a9a 100644 --- a/servers/physics/joints/jacobian_entry_sw.h +++ b/servers/physics/joints/jacobian_entry_sw.h @@ -50,7 +50,7 @@ subject to the following restrictions: 3. This notice may not be removed or altered from any source distribution. */ -#include "transform.h" +#include "core/math/transform.h" class JacobianEntrySW { public: diff --git a/servers/physics/joints/slider_joint_sw.cpp b/servers/physics/joints/slider_joint_sw.cpp index c0e9660b22..30700d45f1 100644 --- a/servers/physics/joints/slider_joint_sw.cpp +++ b/servers/physics/joints/slider_joint_sw.cpp @@ -404,6 +404,8 @@ void SliderJointSW::set_param(PhysicsServer::SliderJointParam p_param, real_t p_ case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_SOFTNESS: m_softnessOrthoAng = p_value; break; case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_RESTITUTION: m_restitutionOrthoAng = p_value; break; case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_DAMPING: m_dampingOrthoAng = p_value; break; + + case PhysicsServer::SLIDER_JOINT_MAX: break; // Can't happen, but silences warning } } @@ -433,6 +435,8 @@ real_t SliderJointSW::get_param(PhysicsServer::SliderJointParam p_param) const { case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_SOFTNESS: return m_softnessOrthoAng; case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_RESTITUTION: return m_restitutionOrthoAng; case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_DAMPING: return m_dampingOrthoAng; + + case PhysicsServer::SLIDER_JOINT_MAX: break; // Can't happen, but silences warning } return 0; diff --git a/servers/physics/physics_server_sw.cpp b/servers/physics/physics_server_sw.cpp index 472283833e..76a6138817 100644 --- a/servers/physics/physics_server_sw.cpp +++ b/servers/physics/physics_server_sw.cpp @@ -32,13 +32,13 @@ #include "broad_phase_basic.h" #include "broad_phase_octree.h" +#include "core/os/os.h" +#include "core/script_language.h" #include "joints/cone_twist_joint_sw.h" #include "joints/generic_6dof_joint_sw.h" #include "joints/hinge_joint_sw.h" #include "joints/pin_joint_sw.h" #include "joints/slider_joint_sw.h" -#include "os/os.h" -#include "script_language.h" RID PhysicsServerSW::shape_create(ShapeType p_shape) { diff --git a/servers/physics/shape_sw.cpp b/servers/physics/shape_sw.cpp index 9d2e5e846d..e7fc821c2c 100644 --- a/servers/physics/shape_sw.cpp +++ b/servers/physics/shape_sw.cpp @@ -30,9 +30,9 @@ #include "shape_sw.h" -#include "geometry.h" -#include "quick_hull.h" -#include "sort.h" +#include "core/math/geometry.h" +#include "core/math/quick_hull.h" +#include "core/sort.h" #define _POINT_SNAP 0.001953125 #define _EDGE_IS_VALID_SUPPORT_THRESHOLD 0.0002 @@ -1047,7 +1047,7 @@ void FaceShapeSW::get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_su /** FIND SUPPORT VERTEX **/ int vert_support_idx = -1; - real_t support_max; + real_t support_max = 0; for (int i = 0; i < 3; i++) { diff --git a/servers/physics/shape_sw.h b/servers/physics/shape_sw.h index 7be818b23c..073d19e317 100644 --- a/servers/physics/shape_sw.h +++ b/servers/physics/shape_sw.h @@ -31,8 +31,8 @@ #ifndef SHAPE_SW_H #define SHAPE_SW_H -#include "bsp_tree.h" -#include "geometry.h" +#include "core/math/bsp_tree.h" +#include "core/math/geometry.h" #include "servers/physics_server.h" /* diff --git a/servers/physics/space_sw.cpp b/servers/physics/space_sw.cpp index b2ab7bec16..3b5344f020 100644 --- a/servers/physics/space_sw.cpp +++ b/servers/physics/space_sw.cpp @@ -31,8 +31,8 @@ #include "space_sw.h" #include "collision_solver_sw.h" +#include "core/project_settings.h" #include "physics_server_sw.h" -#include "project_settings.h" _FORCE_INLINE_ static bool _can_collide_with(CollisionObjectSW *p_object, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) { @@ -105,7 +105,7 @@ bool PhysicsDirectSpaceStateSW::intersect_ray(const Vector3 &p_from, const Vecto int amount = space->broadphase->cull_segment(begin, end, space->intersection_query_results, SpaceSW::INTERSECTION_QUERY_MAX, space->intersection_query_subindex_results); - //todo, create another array tha references results, compute AABBs and check closest point to ray origin, sort, and stop evaluating results when beyond first collision + //todo, create another array that references results, compute AABBs and check closest point to ray origin, sort, and stop evaluating results when beyond first collision bool collided = false; Vector3 res_point, res_normal; @@ -544,14 +544,24 @@ int SpaceSW::test_body_ray_separation(BodySW *p_body, const Transform &p_transfo AABB body_aabb; + bool shapes_found = false; + for (int i = 0; i < p_body->get_shape_count(); i++) { - if (i == 0) + if (p_body->is_shape_set_as_disabled(i)) + continue; + + if (!shapes_found) { body_aabb = p_body->get_shape_aabb(i); - else + shapes_found = true; + } else { body_aabb = body_aabb.merge(p_body->get_shape_aabb(i)); + } } + if (!shapes_found) { + return 0; + } // Undo the currently transform the physics server is aware of and apply the provided one body_aabb = p_transform.xform(p_body->get_inv_transform().xform(body_aabb)); body_aabb = body_aabb.grow(p_margin); @@ -691,13 +701,23 @@ bool SpaceSW::test_body_motion(BodySW *p_body, const Transform &p_from, const Ve r_result->collider_shape = 0; } AABB body_aabb; + bool shapes_found = false; for (int i = 0; i < p_body->get_shape_count(); i++) { - if (i == 0) + if (p_body->is_shape_set_as_disabled(i)) + continue; + + if (!shapes_found) { body_aabb = p_body->get_shape_aabb(i); - else + shapes_found = true; + } else { body_aabb = body_aabb.merge(p_body->get_shape_aabb(i)); + } + } + + if (!shapes_found) { + return false; } // Undo the currently transform the physics server is aware of and apply the provided one @@ -1183,6 +1203,7 @@ SpaceSW::SpaceSW() { body_linear_velocity_sleep_threshold = GLOBAL_DEF("physics/3d/sleep_threshold_linear", 0.1); body_angular_velocity_sleep_threshold = GLOBAL_DEF("physics/3d/sleep_threshold_angular", (8.0 / 180.0 * Math_PI)); body_time_to_sleep = GLOBAL_DEF("physics/3d/time_before_sleep", 0.5); + ProjectSettings::get_singleton()->set_custom_property_info("physics/3d/time_before_sleep", PropertyInfo(Variant::REAL, "physics/3d/time_before_sleep", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater")); body_angular_velocity_damp_ratio = 10; broadphase = BroadPhaseSW::create_func(); diff --git a/servers/physics/space_sw.h b/servers/physics/space_sw.h index e7231df532..d550b374e3 100644 --- a/servers/physics/space_sw.h +++ b/servers/physics/space_sw.h @@ -37,9 +37,9 @@ #include "body_sw.h" #include "broad_phase_sw.h" #include "collision_object_sw.h" -#include "hash_map.h" -#include "project_settings.h" -#include "typedefs.h" +#include "core/hash_map.h" +#include "core/project_settings.h" +#include "core/typedefs.h" class PhysicsDirectSpaceStateSW : public PhysicsDirectSpaceState { diff --git a/servers/physics/step_sw.cpp b/servers/physics/step_sw.cpp index 4128e1ec1a..5238f24b20 100644 --- a/servers/physics/step_sw.cpp +++ b/servers/physics/step_sw.cpp @@ -31,7 +31,7 @@ #include "step_sw.h" #include "joints_sw.h" -#include "os/os.h" +#include "core/os/os.h" void StepSW::_populate_island(BodySW *p_body, BodySW **p_island, ConstraintSW **p_constraint_island) { diff --git a/servers/physics_2d/area_2d_sw.h b/servers/physics_2d/area_2d_sw.h index d2058ad5af..90e30f68bc 100644 --- a/servers/physics_2d/area_2d_sw.h +++ b/servers/physics_2d/area_2d_sw.h @@ -32,7 +32,7 @@ #define AREA_2D_SW_H #include "collision_object_2d_sw.h" -#include "self_list.h" +#include "core/self_list.h" #include "servers/physics_2d_server.h" //#include "servers/physics/query_sw.h" diff --git a/servers/physics_2d/area_pair_2d_sw.cpp b/servers/physics_2d/area_pair_2d_sw.cpp index 9d515d2183..cdd35cf657 100644 --- a/servers/physics_2d/area_pair_2d_sw.cpp +++ b/servers/physics_2d/area_pair_2d_sw.cpp @@ -147,10 +147,10 @@ Area2Pair2DSW::~Area2Pair2DSW() { if (colliding) { - if (area_b->has_area_monitor_callback() && area_a->is_monitorable()) + if (area_b->has_area_monitor_callback()) area_b->remove_area_from_query(area_a, shape_a, shape_b); - if (area_a->has_area_monitor_callback() && area_b->is_monitorable()) + if (area_a->has_area_monitor_callback()) area_a->remove_area_from_query(area_b, shape_b, shape_a); } diff --git a/servers/physics_2d/body_2d_sw.h b/servers/physics_2d/body_2d_sw.h index 69184ad484..97dff69a20 100644 --- a/servers/physics_2d/body_2d_sw.h +++ b/servers/physics_2d/body_2d_sw.h @@ -33,7 +33,7 @@ #include "area_2d_sw.h" #include "collision_object_2d_sw.h" -#include "vset.h" +#include "core/vset.h" class Constraint2DSW; diff --git a/servers/physics_2d/body_pair_2d_sw.cpp b/servers/physics_2d/body_pair_2d_sw.cpp index 2633edf7bb..93a05b74ef 100644 --- a/servers/physics_2d/body_pair_2d_sw.cpp +++ b/servers/physics_2d/body_pair_2d_sw.cpp @@ -138,7 +138,7 @@ void BodyPair2DSW::_validate_contacts() { Contact &c = contacts[i]; bool erase = false; - if (c.reused == false) { + if (!c.reused) { //was left behind in previous frame erase = true; } else { diff --git a/servers/physics_2d/broad_phase_2d_basic.h b/servers/physics_2d/broad_phase_2d_basic.h index f0f0b3df88..da5dc38b8e 100644 --- a/servers/physics_2d/broad_phase_2d_basic.h +++ b/servers/physics_2d/broad_phase_2d_basic.h @@ -31,7 +31,7 @@ #ifndef BROAD_PHASE_2D_BASIC_H #define BROAD_PHASE_2D_BASIC_H -#include "map.h" +#include "core/map.h" #include "space_2d_sw.h" class BroadPhase2DBasic : public BroadPhase2DSW { diff --git a/servers/physics_2d/broad_phase_2d_hash_grid.cpp b/servers/physics_2d/broad_phase_2d_hash_grid.cpp index 1a5b2d5e3a..95195c8fff 100644 --- a/servers/physics_2d/broad_phase_2d_hash_grid.cpp +++ b/servers/physics_2d/broad_phase_2d_hash_grid.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "broad_phase_2d_hash_grid.h" -#include "project_settings.h" +#include "core/project_settings.h" #define LARGE_ELEMENT_FI 1.01239812 @@ -635,11 +635,15 @@ BroadPhase2DSW *BroadPhase2DHashGrid::_create() { BroadPhase2DHashGrid::BroadPhase2DHashGrid() { hash_table_size = GLOBAL_DEF("physics/2d/bp_hash_table_size", 4096); + ProjectSettings::get_singleton()->set_custom_property_info("physics/2d/bp_hash_table_size", PropertyInfo(Variant::INT, "physics/2d/bp_hash_table_size", PROPERTY_HINT_RANGE, "0,8192,1,or_greater")); hash_table_size = Math::larger_prime(hash_table_size); hash_table = memnew_arr(PosBin *, hash_table_size); cell_size = GLOBAL_DEF("physics/2d/cell_size", 128); + ProjectSettings::get_singleton()->set_custom_property_info("physics/2d/cell_size", PropertyInfo(Variant::INT, "physics/2d/cell_size", PROPERTY_HINT_RANGE, "0,512,1,or_greater")); + large_object_min_surface = GLOBAL_DEF("physics/2d/large_object_surface_threshold_in_cells", 512); + ProjectSettings::get_singleton()->set_custom_property_info("physics/2d/large_object_surface_threshold_in_cells", PropertyInfo(Variant::INT, "physics/2d/large_object_surface_threshold_in_cells", PROPERTY_HINT_RANGE, "0,1024,1,or_greater")); for (uint32_t i = 0; i < hash_table_size; i++) hash_table[i] = NULL; diff --git a/servers/physics_2d/broad_phase_2d_hash_grid.h b/servers/physics_2d/broad_phase_2d_hash_grid.h index 5188abc837..0ecb915a7b 100644 --- a/servers/physics_2d/broad_phase_2d_hash_grid.h +++ b/servers/physics_2d/broad_phase_2d_hash_grid.h @@ -32,7 +32,7 @@ #define BROAD_PHASE_2D_HASH_GRID_H #include "broad_phase_2d_sw.h" -#include "map.h" +#include "core/map.h" class BroadPhase2DHashGrid : public BroadPhase2DSW { diff --git a/servers/physics_2d/broad_phase_2d_sw.h b/servers/physics_2d/broad_phase_2d_sw.h index d7d236c4c6..5b512dac76 100644 --- a/servers/physics_2d/broad_phase_2d_sw.h +++ b/servers/physics_2d/broad_phase_2d_sw.h @@ -31,8 +31,8 @@ #ifndef BROAD_PHASE_2D_SW_H #define BROAD_PHASE_2D_SW_H -#include "math_funcs.h" -#include "rect2.h" +#include "core/math/math_funcs.h" +#include "core/math/rect2.h" class CollisionObject2DSW; diff --git a/servers/physics_2d/collision_object_2d_sw.h b/servers/physics_2d/collision_object_2d_sw.h index 57c3e6a349..f256910f52 100644 --- a/servers/physics_2d/collision_object_2d_sw.h +++ b/servers/physics_2d/collision_object_2d_sw.h @@ -32,7 +32,7 @@ #define COLLISION_OBJECT_2D_SW_H #include "broad_phase_2d_sw.h" -#include "self_list.h" +#include "core/self_list.h" #include "servers/physics_2d_server.h" #include "shape_2d_sw.h" @@ -116,23 +116,23 @@ public: _FORCE_INLINE_ int get_shape_count() const { return shapes.size(); } _FORCE_INLINE_ Shape2DSW *get_shape(int p_index) const { - ERR_FAIL_INDEX_V(p_index, shapes.size(), NULL); + CRASH_BAD_INDEX(p_index, shapes.size()); return shapes[p_index].shape; } _FORCE_INLINE_ const Transform2D &get_shape_transform(int p_index) const { - ERR_FAIL_INDEX_V(p_index, shapes.size(), Transform2D()); + CRASH_BAD_INDEX(p_index, shapes.size()); return shapes[p_index].xform; } _FORCE_INLINE_ const Transform2D &get_shape_inv_transform(int p_index) const { - ERR_FAIL_INDEX_V(p_index, shapes.size(), Transform2D()); + CRASH_BAD_INDEX(p_index, shapes.size()); return shapes[p_index].xform_inv; } _FORCE_INLINE_ const Rect2 &get_shape_aabb(int p_index) const { - ERR_FAIL_INDEX_V(p_index, shapes.size(), Rect2()); + CRASH_BAD_INDEX(p_index, shapes.size()); return shapes[p_index].aabb_cache; } _FORCE_INLINE_ const Variant &get_shape_metadata(int p_index) const { - ERR_FAIL_INDEX_V(p_index, shapes.size(), Variant()); + CRASH_BAD_INDEX(p_index, shapes.size()); return shapes[p_index].metadata; } @@ -142,16 +142,16 @@ public: void set_shape_as_disabled(int p_idx, bool p_disabled); _FORCE_INLINE_ bool is_shape_set_as_disabled(int p_idx) const { - ERR_FAIL_INDEX_V(p_idx, shapes.size(), false); + CRASH_BAD_INDEX(p_idx, shapes.size()); return shapes[p_idx].disabled; } _FORCE_INLINE_ void set_shape_as_one_way_collision(int p_idx, bool p_one_way_collision) { - ERR_FAIL_INDEX(p_idx, shapes.size()); + CRASH_BAD_INDEX(p_idx, shapes.size()); 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); + CRASH_BAD_INDEX(p_idx, shapes.size()); return shapes[p_idx].one_way_collision; } diff --git a/servers/physics_2d/collision_solver_2d_sat.cpp b/servers/physics_2d/collision_solver_2d_sat.cpp index 98fe4adb80..0f32f2ec85 100644 --- a/servers/physics_2d/collision_solver_2d_sat.cpp +++ b/servers/physics_2d/collision_solver_2d_sat.cpp @@ -30,7 +30,7 @@ #include "collision_solver_2d_sat.h" -#include "geometry.h" +#include "core/math/geometry.h" struct _CollectorCallback2D { diff --git a/servers/physics_2d/physics_2d_server_sw.cpp b/servers/physics_2d/physics_2d_server_sw.cpp index 2bb297e53b..80588c05e6 100644 --- a/servers/physics_2d/physics_2d_server_sw.cpp +++ b/servers/physics_2d/physics_2d_server_sw.cpp @@ -32,9 +32,9 @@ #include "broad_phase_2d_basic.h" #include "broad_phase_2d_hash_grid.h" #include "collision_solver_2d_sw.h" -#include "os/os.h" -#include "project_settings.h" -#include "script_language.h" +#include "core/os/os.h" +#include "core/project_settings.h" +#include "core/script_language.h" RID Physics2DServerSW::_shape_create(ShapeType p_shape) { @@ -169,7 +169,7 @@ void Physics2DServerSW::_shape_col_cbk(const Vector2 &p_point_A, const Vector2 & cbk->invalid_by_dir++; return; } - if (cbk->valid_dir.dot((p_point_A - p_point_B).normalized()) < 0.7071) { + if (cbk->valid_dir.dot((p_point_A - p_point_B).normalized()) < 0.7071) { //sqrt(2)/2.0 cbk->invalid_by_dir++; /* diff --git a/servers/physics_2d/physics_2d_server_wrap_mt.cpp b/servers/physics_2d/physics_2d_server_wrap_mt.cpp index 804b93ecd8..3ded4b717a 100644 --- a/servers/physics_2d/physics_2d_server_wrap_mt.cpp +++ b/servers/physics_2d/physics_2d_server_wrap_mt.cpp @@ -30,7 +30,7 @@ #include "physics_2d_server_wrap_mt.h" -#include "os/os.h" +#include "core/os/os.h" void Physics2DServerWrapMT::thread_exit() { diff --git a/servers/physics_2d/physics_2d_server_wrap_mt.h b/servers/physics_2d/physics_2d_server_wrap_mt.h index 0b135e25dc..58517bf1a7 100644 --- a/servers/physics_2d/physics_2d_server_wrap_mt.h +++ b/servers/physics_2d/physics_2d_server_wrap_mt.h @@ -31,9 +31,9 @@ #ifndef PHYSICS2DSERVERWRAPMT_H #define PHYSICS2DSERVERWRAPMT_H -#include "command_queue_mt.h" -#include "os/thread.h" -#include "project_settings.h" +#include "core/command_queue_mt.h" +#include "core/os/thread.h" +#include "core/project_settings.h" #include "servers/physics_2d_server.h" #ifdef DEBUG_SYNC diff --git a/servers/physics_2d/shape_2d_sw.cpp b/servers/physics_2d/shape_2d_sw.cpp index dc8ec23e69..94b0a0a0c7 100644 --- a/servers/physics_2d/shape_2d_sw.cpp +++ b/servers/physics_2d/shape_2d_sw.cpp @@ -30,8 +30,8 @@ #include "shape_2d_sw.h" -#include "geometry.h" -#include "sort.h" +#include "core/math/geometry.h" +#include "core/sort.h" void Shape2DSW::configure(const Rect2 &p_aabb) { aabb = p_aabb; diff --git a/servers/physics_2d/space_2d_sw.cpp b/servers/physics_2d/space_2d_sw.cpp index 222bae0b76..0e83993472 100644 --- a/servers/physics_2d/space_2d_sw.cpp +++ b/servers/physics_2d/space_2d_sw.cpp @@ -31,9 +31,9 @@ #include "space_2d_sw.h" #include "collision_solver_2d_sw.h" -#include "pair.h" +#include "core/os/os.h" +#include "core/pair.h" #include "physics_2d_server_sw.h" - _FORCE_INLINE_ static bool _can_collide_with(CollisionObject2DSW *p_object, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) { if (!(p_object->get_collision_layer() & p_collision_mask)) { @@ -125,7 +125,7 @@ bool Physics2DDirectSpaceStateSW::intersect_ray(const Vector2 &p_from, const Vec int amount = space->broadphase->cull_segment(begin, end, space->intersection_query_results, Space2DSW::INTERSECTION_QUERY_MAX, space->intersection_query_subindex_results); - //todo, create another array tha references results, compute AABBs and check closest point to ray origin, sort, and stop evaluating results when beyond first collision + //todo, create another array that references results, compute AABBs and check closest point to ray origin, sort, and stop evaluating results when beyond first collision bool collided = false; Vector2 res_point, res_normal; @@ -500,12 +500,23 @@ int Space2DSW::test_body_ray_separation(Body2DSW *p_body, const Transform2D &p_t Rect2 body_aabb; + bool shapes_found = false; + for (int i = 0; i < p_body->get_shape_count(); i++) { - if (i == 0) + if (p_body->is_shape_set_as_disabled(i)) + continue; + + if (!shapes_found) { body_aabb = p_body->get_shape_aabb(i); - else + shapes_found = true; + } else { body_aabb = body_aabb.merge(p_body->get_shape_aabb(i)); + } + } + + if (!shapes_found) { + return 0; } // Undo the currently transform the physics server is aware of and apply the provided one @@ -660,12 +671,23 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co } Rect2 body_aabb; + bool shapes_found = false; + for (int i = 0; i < p_body->get_shape_count(); i++) { - if (i == 0) + if (p_body->is_shape_set_as_disabled(i)) + continue; + + if (!shapes_found) { body_aabb = p_body->get_shape_aabb(i); - else + shapes_found = true; + } else { body_aabb = body_aabb.merge(p_body->get_shape_aabb(i)); + } + } + + if (!shapes_found) { + return false; } // Undo the currently transform the physics server is aware of and apply the provided one @@ -729,18 +751,33 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co cbk.valid_depth = p_margin; //only valid depth is the collision margin cbk.invalid_by_dir = 0; + if (col_obj->get_type() == CollisionObject2DSW::TYPE_BODY) { + const Body2DSW *b = static_cast<const Body2DSW *>(col_obj); + if (b->get_mode() == Physics2DServer::BODY_MODE_KINEMATIC || b->get_mode() == Physics2DServer::BODY_MODE_RIGID) { + //fix for moving platforms (kinematic and dynamic), margin is increased by how much it moved in the given direction + Vector2 lv = b->get_linear_velocity(); + //compute displacement from linear velocity + Vector2 motion = lv * Physics2DDirectBodyStateSW::singleton->step; + float motion_len = motion.length(); + motion.normalize(); + cbk.valid_depth += motion_len * MAX(motion.dot(-cbk.valid_dir), 0.0); + } + } } else { cbk.valid_dir = Vector2(); cbk.valid_depth = 0; cbk.invalid_by_dir = 0; } + int current_collisions = cbk.amount; + bool did_collide = false; + Shape2DSW *against_shape = col_obj->get_shape(shape_idx); if (CollisionSolver2DSW::solve(body_shape, body_shape_xform, Vector2(), against_shape, col_obj->get_transform() * col_obj->get_shape_transform(shape_idx), Vector2(), cbkres, cbkptr, NULL, p_margin)) { - collided = cbk.amount > 0; + did_collide = cbk.amount > current_collisions; } - if (!collided && cbk.invalid_by_dir > 0) { + if (!did_collide && cbk.invalid_by_dir > 0) { //this shape must be excluded if (excluded_shape_pair_count < max_excluded_shape_pairs) { ExcludedShapeSW esp; @@ -750,6 +787,10 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co excluded_shape_pairs[excluded_shape_pair_count++] = esp; } } + + if (did_collide) { + collided = true; + } } } @@ -1246,6 +1287,7 @@ Space2DSW::Space2DSW() { body_linear_velocity_sleep_threshold = GLOBAL_DEF("physics/2d/sleep_threshold_linear", 2.0); body_angular_velocity_sleep_threshold = GLOBAL_DEF("physics/2d/sleep_threshold_angular", (8.0 / 180.0 * Math_PI)); body_time_to_sleep = GLOBAL_DEF("physics/2d/time_before_sleep", 0.5); + ProjectSettings::get_singleton()->set_custom_property_info("physics/2d/time_before_sleep", PropertyInfo(Variant::REAL, "physics/2d/time_before_sleep", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater")); broadphase = BroadPhase2DSW::create_func(); broadphase->set_pair_callback(_broadphase_pair, this); diff --git a/servers/physics_2d/space_2d_sw.h b/servers/physics_2d/space_2d_sw.h index 29efc4e547..bf4ea12eb5 100644 --- a/servers/physics_2d/space_2d_sw.h +++ b/servers/physics_2d/space_2d_sw.h @@ -37,9 +37,9 @@ #include "body_pair_2d_sw.h" #include "broad_phase_2d_sw.h" #include "collision_object_2d_sw.h" -#include "hash_map.h" -#include "project_settings.h" -#include "typedefs.h" +#include "core/hash_map.h" +#include "core/project_settings.h" +#include "core/typedefs.h" class Physics2DDirectSpaceStateSW : public Physics2DDirectSpaceState { diff --git a/servers/physics_2d/step_2d_sw.cpp b/servers/physics_2d/step_2d_sw.cpp index d1078f1506..e4e1b03623 100644 --- a/servers/physics_2d/step_2d_sw.cpp +++ b/servers/physics_2d/step_2d_sw.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "step_2d_sw.h" -#include "os/os.h" +#include "core/os/os.h" void Step2DSW::_populate_island(Body2DSW *p_body, Body2DSW **p_island, Constraint2DSW **p_constraint_island) { @@ -222,7 +222,7 @@ void Step2DSW::step(Space2DSW *p_space, real_t p_delta, int p_iterations) { Constraint2DSW *prev_ci = NULL; while (ci) { - if (_setup_island(ci, p_delta) == true) { + if (_setup_island(ci, p_delta)) { //removed the root from the island graph because it is not to be processed diff --git a/servers/physics_2d_server.cpp b/servers/physics_2d_server.cpp index 1d7b7d63f9..2ed8a4c478 100644 --- a/servers/physics_2d_server.cpp +++ b/servers/physics_2d_server.cpp @@ -29,9 +29,10 @@ /*************************************************************************/ #include "physics_2d_server.h" + #include "core/method_bind_ext.gen.inc" +#include "core/print_string.h" #include "core/project_settings.h" -#include "print_string.h" Physics2DServer *Physics2DServer::singleton = NULL; @@ -291,6 +292,8 @@ Dictionary Physics2DDirectSpaceState::_intersect_ray(const Vector2 &p_from, cons Array Physics2DDirectSpaceState::_intersect_shape(const Ref<Physics2DShapeQueryParameters> &p_shape_query, int p_max_results) { + ERR_FAIL_COND_V(!p_shape_query.is_valid(), Array()); + Vector<ShapeResult> sr; sr.resize(p_max_results); int rc = intersect_shape(p_shape_query->shape, p_shape_query->transform, p_shape_query->motion, p_shape_query->margin, sr.ptrw(), sr.size(), p_shape_query->exclude, p_shape_query->collision_mask, p_shape_query->collide_with_bodies, p_shape_query->collide_with_areas); @@ -312,6 +315,8 @@ Array Physics2DDirectSpaceState::_intersect_shape(const Ref<Physics2DShapeQueryP Array Physics2DDirectSpaceState::_cast_motion(const Ref<Physics2DShapeQueryParameters> &p_shape_query) { + ERR_FAIL_COND_V(!p_shape_query.is_valid(), Array()); + float closest_safe, closest_unsafe; bool res = cast_motion(p_shape_query->shape, p_shape_query->transform, p_shape_query->motion, p_shape_query->margin, closest_safe, closest_unsafe, p_shape_query->exclude, p_shape_query->collision_mask, p_shape_query->collide_with_bodies, p_shape_query->collide_with_areas); if (!res) @@ -368,6 +373,8 @@ Array Physics2DDirectSpaceState::_intersect_point_on_canvas(const Vector2 &p_poi Array Physics2DDirectSpaceState::_collide_shape(const Ref<Physics2DShapeQueryParameters> &p_shape_query, int p_max_results) { + ERR_FAIL_COND_V(!p_shape_query.is_valid(), Array()); + Vector<Vector2> ret; ret.resize(p_max_results * 2); int rc = 0; @@ -382,6 +389,8 @@ Array Physics2DDirectSpaceState::_collide_shape(const Ref<Physics2DShapeQueryPar } Dictionary Physics2DDirectSpaceState::_get_rest_info(const Ref<Physics2DShapeQueryParameters> &p_shape_query) { + ERR_FAIL_COND_V(!p_shape_query.is_valid(), Dictionary()); + ShapeRestInfo sri; bool res = rest_info(p_shape_query->shape, p_shape_query->transform, p_shape_query->motion, p_shape_query->margin, &sri, p_shape_query->exclude, p_shape_query->collision_mask, p_shape_query->collide_with_bodies, p_shape_query->collide_with_areas); @@ -412,7 +421,6 @@ void Physics2DDirectSpaceState::_bind_methods() { ClassDB::bind_method(D_METHOD("cast_motion", "shape"), &Physics2DDirectSpaceState::_cast_motion); ClassDB::bind_method(D_METHOD("collide_shape", "shape", "max_results"), &Physics2DDirectSpaceState::_collide_shape, DEFVAL(32)); ClassDB::bind_method(D_METHOD("get_rest_info", "shape"), &Physics2DDirectSpaceState::_get_rest_info); - //ClassDB::bind_method(D_METHOD("cast_motion","shape","xform","motion","exclude","umask"),&Physics2DDirectSpaceState::_intersect_shape,DEFVAL(Array()),DEFVAL(0)); } int Physics2DShapeQueryResult::get_result_count() const { @@ -450,10 +458,6 @@ void Physics2DShapeQueryResult::_bind_methods() { /////////////////////////////// -/*bool Physics2DTestMotionResult::is_colliding() const { - - return colliding; -}*/ Vector2 Physics2DTestMotionResult::get_motion() const { return result.motion; @@ -495,7 +499,6 @@ int Physics2DTestMotionResult::get_collider_shape() const { void Physics2DTestMotionResult::_bind_methods() { - //ClassDB::bind_method(D_METHOD("is_colliding"),&Physics2DTestMotionResult::is_colliding); ClassDB::bind_method(D_METHOD("get_motion"), &Physics2DTestMotionResult::get_motion); ClassDB::bind_method(D_METHOD("get_motion_remainder"), &Physics2DTestMotionResult::get_motion_remainder); ClassDB::bind_method(D_METHOD("get_collision_point"), &Physics2DTestMotionResult::get_collision_point); @@ -650,7 +653,6 @@ void Physics2DServer::_bind_methods() { ClassDB::bind_method(D_METHOD("body_add_collision_exception", "body", "excepted_body"), &Physics2DServer::body_add_collision_exception); ClassDB::bind_method(D_METHOD("body_remove_collision_exception", "body", "excepted_body"), &Physics2DServer::body_remove_collision_exception); - //virtual void body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions)=0; ClassDB::bind_method(D_METHOD("body_set_max_contacts_reported", "body", "amount"), &Physics2DServer::body_set_max_contacts_reported); ClassDB::bind_method(D_METHOD("body_get_max_contacts_reported", "body"), &Physics2DServer::body_get_max_contacts_reported); @@ -684,11 +686,6 @@ void Physics2DServer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_process_info", "process_info"), &Physics2DServer::get_process_info); - //ClassDB::bind_method(D_METHOD("init"),&Physics2DServer::init); - //ClassDB::bind_method(D_METHOD("step"),&Physics2DServer::step); - //ClassDB::bind_method(D_METHOD("sync"),&Physics2DServer::sync); - //ClassDB::bind_method(D_METHOD("flush_queries"),&Physics2DServer::flush_queries); - BIND_ENUM_CONSTANT(SPACE_PARAM_CONTACT_RECYCLE_RADIUS); BIND_ENUM_CONSTANT(SPACE_PARAM_CONTACT_MAX_SEPARATION); BIND_ENUM_CONSTANT(SPACE_PARAM_BODY_MAX_ALLOWED_PENETRATION); @@ -758,9 +755,6 @@ void Physics2DServer::_bind_methods() { BIND_ENUM_CONSTANT(CCD_MODE_CAST_RAY); BIND_ENUM_CONSTANT(CCD_MODE_CAST_SHAPE); - //BIND_ENUM_CONSTANT( TYPE_BODY ); - //BIND_ENUM_CONSTANT( TYPE_AREA ); - BIND_ENUM_CONSTANT(AREA_BODY_ADDED); BIND_ENUM_CONSTANT(AREA_BODY_REMOVED); @@ -771,7 +765,6 @@ void Physics2DServer::_bind_methods() { Physics2DServer::Physics2DServer() { - //ERR_FAIL_COND( singleton!=NULL ); singleton = this; } diff --git a/servers/physics_2d_server.h b/servers/physics_2d_server.h index c6b4a9571f..2e91d89c53 100644 --- a/servers/physics_2d_server.h +++ b/servers/physics_2d_server.h @@ -31,9 +31,9 @@ #ifndef PHYSICS_2D_SERVER_H #define PHYSICS_2D_SERVER_H -#include "object.h" -#include "reference.h" -#include "resource.h" +#include "core/object.h" +#include "core/reference.h" +#include "core/resource.h" class Physics2DDirectSpaceState; @@ -377,7 +377,6 @@ public: BODY_MODE_KINEMATIC, BODY_MODE_RIGID, BODY_MODE_CHARACTER - //BODY_MODE_SOFT ?? }; virtual RID body_create() = 0; @@ -589,9 +588,7 @@ public: INFO_ACTIVE_OBJECTS, INFO_COLLISION_PAIRS, - INFO_ISLAND_COUNT, - INFO_STEP_TIME, - INFO_BROAD_PHASE_TIME + INFO_ISLAND_COUNT }; virtual int get_process_info(ProcessInfo p_info) = 0; diff --git a/servers/physics_server.cpp b/servers/physics_server.cpp index deb3cd9bbe..888e16e0c3 100644 --- a/servers/physics_server.cpp +++ b/servers/physics_server.cpp @@ -30,9 +30,9 @@ #include "physics_server.h" +#include "core/method_bind_ext.gen.inc" +#include "core/print_string.h" #include "core/project_settings.h" -#include "method_bind_ext.gen.inc" -#include "print_string.h" PhysicsServer *PhysicsServer::singleton = NULL; @@ -260,36 +260,6 @@ PhysicsShapeQueryParameters::PhysicsShapeQueryParameters() { ///////////////////////////////////// -/* -Variant PhysicsDirectSpaceState::_intersect_shape(const RID& p_shape, const Transform& p_xform,int p_result_max,const Vector<RID>& p_exclude,uint32_t p_collision_mask) { - - - - ERR_FAIL_INDEX_V(p_result_max,4096,Variant()); - if (p_result_max<=0) - return Variant(); - - Set<RID> exclude; - for(int i=0;i<p_exclude.size();i++) - exclude.insert(p_exclude[i]); - - ShapeResult *res=(ShapeResult*)alloca(p_result_max*sizeof(ShapeResult)); - - int rc = intersect_shape(p_shape,p_xform,0,res,p_result_max,exclude); - - if (rc==0) - return Variant(); - - Ref<PhysicsShapeQueryResult> result = memnew( PhysicsShapeQueryResult ); - result->result.resize(rc); - for(int i=0;i<rc;i++) - result->result[i]=res[i]; - - return result; - -} -*/ - Dictionary PhysicsDirectSpaceState::_intersect_ray(const Vector3 &p_from, const Vector3 &p_to, const Vector<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) { RayResult inters; @@ -315,6 +285,8 @@ Dictionary PhysicsDirectSpaceState::_intersect_ray(const Vector3 &p_from, const Array PhysicsDirectSpaceState::_intersect_shape(const Ref<PhysicsShapeQueryParameters> &p_shape_query, int p_max_results) { + ERR_FAIL_COND_V(!p_shape_query.is_valid(), Array()); + Vector<ShapeResult> sr; sr.resize(p_max_results); int rc = intersect_shape(p_shape_query->shape, p_shape_query->transform, p_shape_query->margin, sr.ptrw(), sr.size(), p_shape_query->exclude, p_shape_query->collision_mask, p_shape_query->collide_with_bodies, p_shape_query->collide_with_areas); @@ -335,6 +307,8 @@ Array PhysicsDirectSpaceState::_intersect_shape(const Ref<PhysicsShapeQueryParam Array PhysicsDirectSpaceState::_cast_motion(const Ref<PhysicsShapeQueryParameters> &p_shape_query, const Vector3 &p_motion) { + ERR_FAIL_COND_V(!p_shape_query.is_valid(), Array()); + float closest_safe, closest_unsafe; bool res = cast_motion(p_shape_query->shape, p_shape_query->transform, p_motion, p_shape_query->margin, closest_safe, closest_unsafe, p_shape_query->exclude, p_shape_query->collision_mask, p_shape_query->collide_with_bodies, p_shape_query->collide_with_areas); if (!res) @@ -347,6 +321,8 @@ Array PhysicsDirectSpaceState::_cast_motion(const Ref<PhysicsShapeQueryParameter } Array PhysicsDirectSpaceState::_collide_shape(const Ref<PhysicsShapeQueryParameters> &p_shape_query, int p_max_results) { + ERR_FAIL_COND_V(!p_shape_query.is_valid(), Array()); + Vector<Vector3> ret; ret.resize(p_max_results * 2); int rc = 0; @@ -361,6 +337,8 @@ Array PhysicsDirectSpaceState::_collide_shape(const Ref<PhysicsShapeQueryParamet } Dictionary PhysicsDirectSpaceState::_get_rest_info(const Ref<PhysicsShapeQueryParameters> &p_shape_query) { + ERR_FAIL_COND_V(!p_shape_query.is_valid(), Dictionary()); + ShapeRestInfo sri; bool res = rest_info(p_shape_query->shape, p_shape_query->transform, p_shape_query->margin, &sri, p_shape_query->exclude, p_shape_query->collision_mask, p_shape_query->collide_with_bodies, p_shape_query->collide_with_areas); @@ -383,9 +361,6 @@ PhysicsDirectSpaceState::PhysicsDirectSpaceState() { void PhysicsDirectSpaceState::_bind_methods() { - //ClassDB::bind_method(D_METHOD("intersect_ray","from","to","exclude","umask"),&PhysicsDirectSpaceState::_intersect_ray,DEFVAL(Array()),DEFVAL(0)); - //ClassDB::bind_method(D_METHOD("intersect_shape","shape","xform","result_max","exclude","umask"),&PhysicsDirectSpaceState::_intersect_shape,DEFVAL(Array()),DEFVAL(0)); - ClassDB::bind_method(D_METHOD("intersect_ray", "from", "to", "exclude", "collision_mask", "collide_with_bodies", "collide_with_areas"), &PhysicsDirectSpaceState::_intersect_ray, DEFVAL(Array()), DEFVAL(0x7FFFFFFF), DEFVAL(true), DEFVAL(false)); ClassDB::bind_method(D_METHOD("intersect_shape", "shape", "max_results"), &PhysicsDirectSpaceState::_intersect_shape, DEFVAL(32)); ClassDB::bind_method(D_METHOD("cast_motion", "shape", "motion"), &PhysicsDirectSpaceState::_cast_motion); @@ -513,9 +488,6 @@ void PhysicsServer::_bind_methods() { ClassDB::bind_method(D_METHOD("body_set_enable_continuous_collision_detection", "body", "enable"), &PhysicsServer::body_set_enable_continuous_collision_detection); ClassDB::bind_method(D_METHOD("body_is_continuous_collision_detection_enabled", "body"), &PhysicsServer::body_is_continuous_collision_detection_enabled); - //ClassDB::bind_method(D_METHOD("body_set_user_flags","flags""),&PhysicsServer::body_set_shape,DEFVAL(Transform)); - //ClassDB::bind_method(D_METHOD("body_get_user_flags","body","shape_idx","shape"),&PhysicsServer::body_get_shape); - ClassDB::bind_method(D_METHOD("body_set_param", "body", "param", "value"), &PhysicsServer::body_set_param); ClassDB::bind_method(D_METHOD("body_get_param", "body", "param"), &PhysicsServer::body_get_param); @@ -539,7 +511,6 @@ void PhysicsServer::_bind_methods() { ClassDB::bind_method(D_METHOD("body_add_collision_exception", "body", "excepted_body"), &PhysicsServer::body_add_collision_exception); ClassDB::bind_method(D_METHOD("body_remove_collision_exception", "body", "excepted_body"), &PhysicsServer::body_remove_collision_exception); - //virtual void body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions)=0; ClassDB::bind_method(D_METHOD("body_set_max_contacts_reported", "body", "amount"), &PhysicsServer::body_set_max_contacts_reported); ClassDB::bind_method(D_METHOD("body_get_max_contacts_reported", "body"), &PhysicsServer::body_get_max_contacts_reported); @@ -672,28 +643,10 @@ void PhysicsServer::_bind_methods() { ClassDB::bind_method(D_METHOD("generic_6dof_joint_set_flag", "joint", "axis", "flag", "enable"), &PhysicsServer::generic_6dof_joint_set_flag); ClassDB::bind_method(D_METHOD("generic_6dof_joint_get_flag", "joint", "axis", "flag"), &PhysicsServer::generic_6dof_joint_get_flag); - /* - ClassDB::bind_method(D_METHOD("joint_set_param","joint","param","value"),&PhysicsServer::joint_set_param); - ClassDB::bind_method(D_METHOD("joint_get_param","joint","param"),&PhysicsServer::joint_get_param); - - ClassDB::bind_method(D_METHOD("pin_joint_create","anchor","body_a","body_b"),&PhysicsServer::pin_joint_create,DEFVAL(RID())); - ClassDB::bind_method(D_METHOD("groove_joint_create","groove1_a","groove2_a","anchor_b","body_a","body_b"),&PhysicsServer::groove_joint_create,DEFVAL(RID()),DEFVAL(RID())); - ClassDB::bind_method(D_METHOD("damped_spring_joint_create","anchor_a","anchor_b","body_a","body_b"),&PhysicsServer::damped_spring_joint_create,DEFVAL(RID())); - - ClassDB::bind_method(D_METHOD("damped_string_joint_set_param","joint","param","value"),&PhysicsServer::damped_string_joint_set_param,DEFVAL(RID())); - ClassDB::bind_method(D_METHOD("damped_string_joint_get_param","joint","param"),&PhysicsServer::damped_string_joint_get_param); - - ClassDB::bind_method(D_METHOD("joint_get_type","joint"),&PhysicsServer::joint_get_type); -*/ ClassDB::bind_method(D_METHOD("free_rid", "rid"), &PhysicsServer::free); ClassDB::bind_method(D_METHOD("set_active", "active"), &PhysicsServer::set_active); - //ClassDB::bind_method(D_METHOD("init"),&PhysicsServer::init); - //ClassDB::bind_method(D_METHOD("step"),&PhysicsServer::step); - //ClassDB::bind_method(D_METHOD("sync"),&PhysicsServer::sync); - //ClassDB::bind_method(D_METHOD("flush_queries"),&PhysicsServer::flush_queries); - ClassDB::bind_method(D_METHOD("get_process_info", "process_info"), &PhysicsServer::get_process_info); BIND_ENUM_CONSTANT(SHAPE_PLANE); @@ -725,7 +678,6 @@ void PhysicsServer::_bind_methods() { BIND_ENUM_CONSTANT(BODY_MODE_STATIC); BIND_ENUM_CONSTANT(BODY_MODE_KINEMATIC); BIND_ENUM_CONSTANT(BODY_MODE_RIGID); - BIND_ENUM_CONSTANT(BODY_MODE_SOFT); BIND_ENUM_CONSTANT(BODY_MODE_CHARACTER); BIND_ENUM_CONSTANT(BODY_PARAM_BOUNCE); @@ -741,17 +693,6 @@ void PhysicsServer::_bind_methods() { BIND_ENUM_CONSTANT(BODY_STATE_ANGULAR_VELOCITY); BIND_ENUM_CONSTANT(BODY_STATE_SLEEPING); BIND_ENUM_CONSTANT(BODY_STATE_CAN_SLEEP); - /* - BIND_ENUM_CONSTANT( JOINT_PIN ); - BIND_ENUM_CONSTANT( JOINT_GROOVE ); - BIND_ENUM_CONSTANT( JOINT_DAMPED_SPRING ); - - BIND_ENUM_CONSTANT( DAMPED_STRING_REST_LENGTH ); - BIND_ENUM_CONSTANT( DAMPED_STRING_STIFFNESS ); - BIND_ENUM_CONSTANT( DAMPED_STRING_DAMPING ); -*/ - //BIND_ENUM_CONSTANT( TYPE_BODY ); - //BIND_ENUM_CONSTANT( TYPE_AREA ); BIND_ENUM_CONSTANT(AREA_BODY_ADDED); BIND_ENUM_CONSTANT(AREA_BODY_REMOVED); diff --git a/servers/physics_server.h b/servers/physics_server.h index f2aa33a6cc..d80d76305a 100644 --- a/servers/physics_server.h +++ b/servers/physics_server.h @@ -31,8 +31,8 @@ #ifndef PHYSICS_SERVER_H #define PHYSICS_SERVER_H -#include "object.h" -#include "resource.h" +#include "core/object.h" +#include "core/resource.h" class PhysicsDirectSpaceState; @@ -360,7 +360,6 @@ public: BODY_MODE_STATIC, BODY_MODE_KINEMATIC, BODY_MODE_RIGID, - BODY_MODE_SOFT, BODY_MODE_CHARACTER }; diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp index 4c764641e3..7deeec676b 100644 --- a/servers/register_server_types.cpp +++ b/servers/register_server_types.cpp @@ -29,8 +29,8 @@ /*************************************************************************/ #include "register_server_types.h" -#include "engine.h" -#include "project_settings.h" +#include "core/engine.h" +#include "core/project_settings.h" #include "arvr/arvr_interface.h" #include "arvr/arvr_positional_tracker.h" @@ -52,12 +52,12 @@ #include "audio/effects/audio_effect_reverb.h" #include "audio/effects/audio_effect_stereo_enhance.h" #include "audio_server.h" +#include "core/script_debugger_remote.h" #include "physics/physics_server_sw.h" #include "physics_2d/physics_2d_server_sw.h" #include "physics_2d/physics_2d_server_wrap_mt.h" #include "physics_2d_server.h" #include "physics_server.h" -#include "script_debugger_remote.h" #include "visual/shader_types.h" #include "visual_server.h" diff --git a/servers/visual/SCsub b/servers/visual/SCsub index ccc76e823f..d730144861 100644 --- a/servers/visual/SCsub +++ b/servers/visual/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.servers_sources, "*.cpp") - -Export('env') diff --git a/servers/visual/default_mouse_cursor.xpm b/servers/visual/default_mouse_cursor.xpm deleted file mode 100644 index 37d437dd15..0000000000 --- a/servers/visual/default_mouse_cursor.xpm +++ /dev/null @@ -1,23 +0,0 @@ -/* XPM */ -static const char * default_mouse_cursor_xpm[] = { -"16 16 4 1", -" c None", -". c #000000", -"+ c #FF00FF", -"@ c #FFFFFF", -"...+++++++++++++", -".@...+++++++++++", -".@@@...+++++++++", -".@@@@@....++++++", -".@@@@@@@@...++++", -".@@@@@@@@@@...++", -".@@@@@@@@@@@@..+", -".@@@@@@@@@@@@@..", -".@@@@@@@@@@@@..+", -".@@@@@@@@@@@..++", -".@@@@@@@@@...+++", -".@@@.....@@..+++", -".....+++.@@@..++", -"++++++++..@@@..+", -"+++++++++..@@@.+", -"++++++++++.....+"}; diff --git a/servers/visual/rasterizer.cpp b/servers/visual/rasterizer.cpp index be46690e55..6c04d1de63 100644 --- a/servers/visual/rasterizer.cpp +++ b/servers/visual/rasterizer.cpp @@ -30,8 +30,8 @@ #include "rasterizer.h" -#include "os/os.h" -#include "print_string.h" +#include "core/os/os.h" +#include "core/print_string.h" Rasterizer *(*Rasterizer::_create_func)() = NULL; diff --git a/servers/visual/rasterizer.h b/servers/visual/rasterizer.h index 49dff0d557..f87f838fd6 100644 --- a/servers/visual/rasterizer.h +++ b/servers/visual/rasterizer.h @@ -31,10 +31,10 @@ #ifndef RASTERIZER_H #define RASTERIZER_H -#include "camera_matrix.h" +#include "core/math/camera_matrix.h" #include "servers/visual_server.h" -#include "self_list.h" +#include "core/self_list.h" class RasterizerScene { public: @@ -249,6 +249,7 @@ public: virtual void material_set_param(RID p_material, const StringName &p_param, const Variant &p_value) = 0; virtual Variant material_get_param(RID p_material, const StringName &p_param) const = 0; + virtual Variant material_get_param_default(RID p_material, const StringName &p_param) const = 0; virtual void material_set_line_width(RID p_material, float p_width) = 0; @@ -396,6 +397,7 @@ public: virtual RID reflection_probe_create() = 0; virtual void reflection_probe_set_update_mode(RID p_probe, VS::ReflectionProbeUpdateMode p_mode) = 0; + virtual void reflection_probe_set_resolution(RID p_probe, int p_resolution) = 0; virtual void reflection_probe_set_intensity(RID p_probe, float p_intensity) = 0; virtual void reflection_probe_set_interior_ambient(RID p_probe, const Color &p_ambient) = 0; virtual void reflection_probe_set_interior_ambient_energy(RID p_probe, float p_energy) = 0; @@ -798,8 +800,6 @@ public: RID particles; RID texture; RID normal_map; - int h_frames; - int v_frames; CommandParticles() { type = TYPE_PARTICLES; } }; @@ -836,6 +836,7 @@ public: bool clip; bool visible; bool behind; + bool update_when_visible; //VS::MaterialBlendMode blend_mode; int light_mask; Vector<Command *> commands; @@ -1037,6 +1038,7 @@ public: copy_back_buffer = NULL; distance_field = false; light_masked = false; + update_when_visible = false; } virtual ~Item() { clear(); @@ -1100,9 +1102,12 @@ public: virtual void restore_render_target() = 0; virtual void clear_render_target(const Color &p_color) = 0; virtual void blit_render_target_to_screen(RID p_render_target, const Rect2 &p_screen_rect, int p_screen = 0) = 0; + virtual void output_lens_distorted_to_screen(RID p_render_target, const Rect2 &p_screen_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample) = 0; virtual void end_frame(bool p_swap_buffers) = 0; virtual void finalize() = 0; + virtual bool is_low_end() const = 0; + virtual ~Rasterizer() {} }; diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp index ffb130048f..e2db89e699 100644 --- a/servers/visual/shader_language.cpp +++ b/servers/visual/shader_language.cpp @@ -29,8 +29,8 @@ /*************************************************************************/ #include "shader_language.h" -#include "os/os.h" -#include "print_string.h" +#include "core/os/os.h" +#include "core/print_string.h" static bool _is_text_char(CharType c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'; @@ -1622,33 +1622,51 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = { { "min", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID } }, { "min", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID } }, + { "min", TYPE_VEC2, { TYPE_VEC2, TYPE_FLOAT, TYPE_VOID } }, { "min", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID } }, + { "min", TYPE_VEC3, { TYPE_VEC3, TYPE_FLOAT, TYPE_VOID } }, { "min", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID } }, + { "min", TYPE_VEC4, { TYPE_VEC4, TYPE_FLOAT, TYPE_VOID } }, { "min", TYPE_INT, { TYPE_INT, TYPE_INT, TYPE_VOID } }, { "min", TYPE_IVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID } }, + { "min", TYPE_IVEC2, { TYPE_IVEC2, TYPE_INT, TYPE_VOID } }, { "min", TYPE_IVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID } }, + { "min", TYPE_IVEC3, { TYPE_IVEC3, TYPE_INT, TYPE_VOID } }, { "min", TYPE_IVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID } }, + { "min", TYPE_IVEC4, { TYPE_IVEC4, TYPE_INT, TYPE_VOID } }, { "min", TYPE_UINT, { TYPE_UINT, TYPE_UINT, TYPE_VOID } }, { "min", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID } }, + { "min", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UINT, TYPE_VOID } }, { "min", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID } }, + { "min", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UINT, TYPE_VOID } }, { "min", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID } }, + { "min", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UINT, TYPE_VOID } }, { "max", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID } }, { "max", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID } }, + { "max", TYPE_VEC2, { TYPE_VEC2, TYPE_FLOAT, TYPE_VOID } }, { "max", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID } }, + { "max", TYPE_VEC3, { TYPE_VEC3, TYPE_FLOAT, TYPE_VOID } }, { "max", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID } }, + { "max", TYPE_VEC4, { TYPE_VEC4, TYPE_FLOAT, TYPE_VOID } }, { "max", TYPE_INT, { TYPE_INT, TYPE_INT, TYPE_VOID } }, { "max", TYPE_IVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID } }, + { "max", TYPE_IVEC2, { TYPE_IVEC2, TYPE_INT, TYPE_VOID } }, { "max", TYPE_IVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID } }, + { "max", TYPE_IVEC3, { TYPE_IVEC3, TYPE_INT, TYPE_VOID } }, { "max", TYPE_IVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID } }, + { "max", TYPE_IVEC4, { TYPE_IVEC4, TYPE_INT, TYPE_VOID } }, { "max", TYPE_UINT, { TYPE_UINT, TYPE_UINT, TYPE_VOID } }, { "max", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID } }, + { "max", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UINT, TYPE_VOID } }, { "max", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID } }, + { "max", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UINT, TYPE_VOID } }, { "max", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID } }, + { "max", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UINT, TYPE_VOID } }, { "clamp", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID } }, { "clamp", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID } }, @@ -2005,10 +2023,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, OperatorNode *p StringName name = static_cast<VariableNode *>(p_func->arguments[0])->name.operator String(); - bool all_const = true; for (int i = 1; i < p_func->arguments.size(); i++) { - if (p_func->arguments[i]->type != Node::TYPE_CONSTANT) - all_const = false; args.push_back(p_func->arguments[i]->get_datatype()); } @@ -2273,6 +2288,105 @@ bool ShaderLanguage::is_sampler_type(DataType p_type) { p_type == TYPE_SAMPLERCUBE; } +Variant ShaderLanguage::constant_value_to_variant(const Vector<ShaderLanguage::ConstantNode::Value> &p_value, DataType p_type) { + if (p_value.size() > 0) { + Variant value; + switch (p_type) { + case ShaderLanguage::TYPE_BOOL: + value = Variant(p_value[0].boolean); + break; + case ShaderLanguage::TYPE_BVEC2: + case ShaderLanguage::TYPE_BVEC3: + case ShaderLanguage::TYPE_BVEC4: + case ShaderLanguage::TYPE_INT: + value = Variant(p_value[0].sint); + break; + case ShaderLanguage::TYPE_IVEC2: + value = Variant(Vector2(p_value[0].sint, p_value[1].sint)); + break; + case ShaderLanguage::TYPE_IVEC3: + value = Variant(Vector3(p_value[0].sint, p_value[1].sint, p_value[2].sint)); + break; + case ShaderLanguage::TYPE_IVEC4: + value = Variant(Plane(p_value[0].sint, p_value[1].sint, p_value[2].sint, p_value[3].sint)); + break; + case ShaderLanguage::TYPE_UINT: + value = Variant(p_value[0].uint); + break; + case ShaderLanguage::TYPE_UVEC2: + value = Variant(Vector2(p_value[0].uint, p_value[1].uint)); + break; + case ShaderLanguage::TYPE_UVEC3: + value = Variant(Vector3(p_value[0].uint, p_value[1].uint, p_value[2].uint)); + break; + case ShaderLanguage::TYPE_UVEC4: + value = Variant(Plane(p_value[0].uint, p_value[1].uint, p_value[2].uint, p_value[3].uint)); + break; + case ShaderLanguage::TYPE_FLOAT: + value = Variant(p_value[0].real); + break; + case ShaderLanguage::TYPE_VEC2: + value = Variant(Vector2(p_value[0].real, p_value[1].real)); + break; + case ShaderLanguage::TYPE_VEC3: + value = Variant(Vector3(p_value[0].real, p_value[1].real, p_value[2].real)); + break; + case ShaderLanguage::TYPE_VEC4: + value = Variant(Plane(p_value[0].real, p_value[1].real, p_value[2].real, p_value[3].real)); + break; + case ShaderLanguage::TYPE_MAT2: + value = Variant(Transform2D(p_value[0].real, p_value[2].real, p_value[1].real, p_value[3].real, 0.0, 0.0)); + break; + case ShaderLanguage::TYPE_MAT3: { + Basis p; + p[0][0] = p_value[0].real; + p[0][1] = p_value[1].real; + p[0][2] = p_value[2].real; + p[1][0] = p_value[3].real; + p[1][1] = p_value[4].real; + p[1][2] = p_value[5].real; + p[2][0] = p_value[6].real; + p[2][1] = p_value[7].real; + p[2][2] = p_value[8].real; + value = Variant(p); + break; + } + case ShaderLanguage::TYPE_MAT4: { + Basis p; + p[0][0] = p_value[0].real; + p[0][1] = p_value[1].real; + p[0][2] = p_value[2].real; + p[1][0] = p_value[4].real; + p[1][1] = p_value[5].real; + p[1][2] = p_value[6].real; + p[2][0] = p_value[8].real; + p[2][1] = p_value[9].real; + p[2][2] = p_value[10].real; + Transform t = Transform(p, Vector3(p_value[3].real, p_value[7].real, p_value[11].real)); + value = Variant(t); + break; + } + case ShaderLanguage::TYPE_ISAMPLER2DARRAY: + case ShaderLanguage::TYPE_ISAMPLER2D: + case ShaderLanguage::TYPE_ISAMPLER3D: + case ShaderLanguage::TYPE_SAMPLER2DARRAY: + case ShaderLanguage::TYPE_SAMPLER2D: + case ShaderLanguage::TYPE_SAMPLER3D: + case ShaderLanguage::TYPE_USAMPLER2DARRAY: + case ShaderLanguage::TYPE_USAMPLER2D: + case ShaderLanguage::TYPE_USAMPLER3D: + case ShaderLanguage::TYPE_SAMPLERCUBE: { + // Texture types, likely not relevant here. + break; + } + case ShaderLanguage::TYPE_VOID: + break; + } + return value; + } + return Variant(); +} + void ShaderLanguage::get_keyword_list(List<String> *r_keywords) { Set<String> kws; @@ -2368,9 +2482,9 @@ int ShaderLanguage::get_cardinality(DataType p_type) { 2, 3, 4, - 2, - 3, 4, + 9, + 16, 1, 1, 1, @@ -2384,7 +2498,7 @@ bool ShaderLanguage::_get_completable_identifier(BlockNode *p_block, CompletionT identifier = StringName(); - TkPos pos; + TkPos pos = { 0, 0 }; Token tk = _get_token(); @@ -2906,6 +3020,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons case TYPE_IVEC2: member_type = TYPE_INT; break; case TYPE_UVEC2: member_type = TYPE_UINT; break; case TYPE_MAT2: member_type = TYPE_VEC2; break; + default: break; } break; @@ -2931,6 +3046,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons case TYPE_IVEC3: member_type = TYPE_INT; break; case TYPE_UVEC3: member_type = TYPE_UINT; break; case TYPE_MAT3: member_type = TYPE_VEC3; break; + default: break; } break; case TYPE_BVEC4: @@ -2955,6 +3071,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons case TYPE_IVEC4: member_type = TYPE_INT; break; case TYPE_UVEC4: member_type = TYPE_UINT; break; case TYPE_MAT4: member_type = TYPE_VEC4; break; + default: break; } break; default: { @@ -3307,7 +3424,9 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha ERR_FAIL_COND_V(op->arguments[0]->type != Node::TYPE_VARIABLE, p_node); - DataType base = get_scalar_type(op->get_datatype()); + DataType type = op->get_datatype(); + DataType base = get_scalar_type(type); + int cardinality = get_cardinality(type); Vector<ConstantNode::Value> values; @@ -3318,19 +3437,9 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha ConstantNode *cn = static_cast<ConstantNode *>(op->arguments[i]); if (get_scalar_type(cn->datatype) == base) { - - int cardinality = get_cardinality(op->arguments[i]->get_datatype()); - if (cn->values.size() == cardinality) { - - for (int j = 0; j < cn->values.size(); j++) { - values.push_back(cn->values[j]); - } - } else if (cn->values.size() == 1) { - - for (int j = 0; j < cardinality; j++) { - values.push_back(cn->values[0]); - } - } // else: should be filtered by the parser as it's an invalid constructor + for (int j = 0; j < cn->values.size(); j++) { + values.push_back(cn->values[j]); + } } else if (get_scalar_type(cn->datatype) == cn->datatype) { ConstantNode::Value v; @@ -3347,6 +3456,30 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha } } + if (values.size() == 1) { + if (type >= TYPE_MAT2 && type <= TYPE_MAT4) { + ConstantNode::Value value = values[0]; + ConstantNode::Value zero; + zero.real = 0.0f; + int size = 2 + (type - TYPE_MAT2); + + values.clear(); + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + values.push_back(i == j ? value : zero); + } + } + } else { + ConstantNode::Value value = values[0]; + for (int i = 1; i < cardinality; i++) { + values.push_back(value); + } + } + } else if (values.size() != cardinality) { + ERR_PRINT("Failed to reduce expression, values and cardinality mismatch."); + return p_node; + } + ConstantNode *cn = alloc_node<ConstantNode>(); cn->datatype = op->get_datatype(); cn->values = values; @@ -3373,6 +3506,7 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha nv.sint = -cn->values[i].sint; } break; case TYPE_UINT: { + // FIXME: This can't work on uint nv.uint = -cn->values[i].uint; } break; case TYPE_FLOAT: { diff --git a/servers/visual/shader_language.h b/servers/visual/shader_language.h index d68f233b2f..08c4d06992 100644 --- a/servers/visual/shader_language.h +++ b/servers/visual/shader_language.h @@ -31,12 +31,12 @@ #ifndef SHADER_LANGUAGE_H #define SHADER_LANGUAGE_H -#include "list.h" -#include "map.h" -#include "string_db.h" -#include "typedefs.h" -#include "ustring.h" -#include "variant.h" +#include "core/list.h" +#include "core/map.h" +#include "core/string_db.h" +#include "core/typedefs.h" +#include "core/ustring.h" +#include "core/variant.h" class ShaderLanguage { @@ -548,6 +548,7 @@ public: static int get_cardinality(DataType p_type); static bool is_scalar_type(DataType p_type); static bool is_sampler_type(DataType p_type); + static Variant constant_value_to_variant(const Vector<ShaderLanguage::ConstantNode::Value> &p_value, DataType p_type); static void get_keyword_list(List<String> *r_keywords); static void get_builtin_funcs(List<String> *r_keywords); diff --git a/servers/visual/shader_types.cpp b/servers/visual/shader_types.cpp index caa454b98e..baafe2f8d0 100644 --- a/servers/visual/shader_types.cpp +++ b/servers/visual/shader_types.cpp @@ -78,6 +78,7 @@ ShaderTypes::ShaderTypes() { shader_modes[VS::SHADER_SPATIAL].functions["vertex"].built_ins["INV_PROJECTION_MATRIX"] = ShaderLanguage::TYPE_MAT4; shader_modes[VS::SHADER_SPATIAL].functions["vertex"].built_ins["TIME"] = constt(ShaderLanguage::TYPE_FLOAT); shader_modes[VS::SHADER_SPATIAL].functions["vertex"].built_ins["VIEWPORT_SIZE"] = constt(ShaderLanguage::TYPE_VEC2); + shader_modes[VS::SHADER_SPATIAL].functions["vertex"].built_ins["OUTPUT_IS_SRGB"] = constt(ShaderLanguage::TYPE_BOOL); shader_modes[VS::SHADER_SPATIAL].functions["fragment"].built_ins["VERTEX"] = constt(ShaderLanguage::TYPE_VEC3); shader_modes[VS::SHADER_SPATIAL].functions["fragment"].built_ins["FRAGCOORD"] = constt(ShaderLanguage::TYPE_VEC4); @@ -108,10 +109,13 @@ ShaderTypes::ShaderTypes() { shader_modes[VS::SHADER_SPATIAL].functions["fragment"].built_ins["EMISSION"] = ShaderLanguage::TYPE_VEC3; shader_modes[VS::SHADER_SPATIAL].functions["fragment"].built_ins["SCREEN_TEXTURE"] = ShaderLanguage::TYPE_SAMPLER2D; shader_modes[VS::SHADER_SPATIAL].functions["fragment"].built_ins["DEPTH_TEXTURE"] = ShaderLanguage::TYPE_SAMPLER2D; + shader_modes[VS::SHADER_SPATIAL].functions["fragment"].built_ins["DEPTH"] = ShaderLanguage::TYPE_FLOAT; shader_modes[VS::SHADER_SPATIAL].functions["fragment"].built_ins["SCREEN_UV"] = ShaderLanguage::TYPE_VEC2; shader_modes[VS::SHADER_SPATIAL].functions["fragment"].built_ins["POINT_COORD"] = constt(ShaderLanguage::TYPE_VEC2); shader_modes[VS::SHADER_SPATIAL].functions["fragment"].built_ins["ALPHA_SCISSOR"] = ShaderLanguage::TYPE_FLOAT; + shader_modes[VS::SHADER_SPATIAL].functions["fragment"].built_ins["OUTPUT_IS_SRGB"] = constt(ShaderLanguage::TYPE_BOOL); + shader_modes[VS::SHADER_SPATIAL].functions["fragment"].built_ins["WORLD_MATRIX"] = constt(ShaderLanguage::TYPE_MAT4); shader_modes[VS::SHADER_SPATIAL].functions["fragment"].built_ins["INV_CAMERA_MATRIX"] = constt(ShaderLanguage::TYPE_MAT4); shader_modes[VS::SHADER_SPATIAL].functions["fragment"].built_ins["PROJECTION_MATRIX"] = constt(ShaderLanguage::TYPE_MAT4); @@ -138,6 +142,7 @@ ShaderTypes::ShaderTypes() { shader_modes[VS::SHADER_SPATIAL].functions["light"].built_ins["ROUGHNESS"] = constt(ShaderLanguage::TYPE_FLOAT); shader_modes[VS::SHADER_SPATIAL].functions["light"].built_ins["DIFFUSE_LIGHT"] = ShaderLanguage::TYPE_VEC3; shader_modes[VS::SHADER_SPATIAL].functions["light"].built_ins["SPECULAR_LIGHT"] = ShaderLanguage::TYPE_VEC3; + shader_modes[VS::SHADER_SPATIAL].functions["light"].built_ins["OUTPUT_IS_SRGB"] = constt(ShaderLanguage::TYPE_BOOL); shader_modes[VS::SHADER_SPATIAL].functions["light"].can_discard = true; @@ -194,6 +199,7 @@ ShaderTypes::ShaderTypes() { shader_modes[VS::SHADER_CANVAS_ITEM].functions["vertex"].built_ins["TIME"] = constt(ShaderLanguage::TYPE_FLOAT); shader_modes[VS::SHADER_CANVAS_ITEM].functions["vertex"].built_ins["INSTANCE_CUSTOM"] = constt(ShaderLanguage::TYPE_VEC4); shader_modes[VS::SHADER_CANVAS_ITEM].functions["vertex"].built_ins["AT_LIGHT_PASS"] = constt(ShaderLanguage::TYPE_BOOL); + shader_modes[VS::SHADER_CANVAS_ITEM].functions["vertex"].built_ins["TEXTURE_PIXEL_SIZE"] = constt(ShaderLanguage::TYPE_VEC2); shader_modes[VS::SHADER_CANVAS_ITEM].functions["vertex"].can_discard = false; shader_modes[VS::SHADER_CANVAS_ITEM].functions["fragment"].built_ins["FRAGCOORD"] = constt(ShaderLanguage::TYPE_VEC4); diff --git a/servers/visual/shader_types.h b/servers/visual/shader_types.h index 0680ec8242..e195b6ea20 100644 --- a/servers/visual/shader_types.h +++ b/servers/visual/shader_types.h @@ -31,7 +31,7 @@ #ifndef SHADERTYPES_H #define SHADERTYPES_H -#include "ordered_hash_map.h" +#include "core/ordered_hash_map.h" #include "servers/visual_server.h" #include "shader_language.h" diff --git a/servers/visual/visual_server_canvas.cpp b/servers/visual/visual_server_canvas.cpp index 0b4bbffddf..84b7504ad4 100644 --- a/servers/visual/visual_server_canvas.cpp +++ b/servers/visual/visual_server_canvas.cpp @@ -30,6 +30,7 @@ #include "visual_server_canvas.h" #include "visual_server_global.h" +#include "visual_server_raster.h" #include "visual_server_viewport.h" void VisualServerCanvas::_render_canvas_item_tree(Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, RasterizerCanvas::Light *p_lights) { @@ -119,6 +120,10 @@ void VisualServerCanvas::_render_canvas_item(Item *p_canvas_item, const Transfor ci->copy_back_buffer->screen_rect = xform.xform(ci->copy_back_buffer->rect).clip(p_clip_rect); } + if (ci->update_when_visible) { + VisualServerRaster::redraw_request(); + } + if ((!ci->commands.empty() && p_clip_rect.intersects(global_rect)) || ci->vp_render || ci->copy_back_buffer) { //something to draw? ci->final_transform = xform; @@ -390,6 +395,14 @@ void VisualServerCanvas::canvas_item_set_draw_behind_parent(RID p_item, bool p_e canvas_item->behind = p_enable; } +void VisualServerCanvas::canvas_item_set_update_when_visible(RID p_item, bool p_update) { + + Item *canvas_item = canvas_item_owner.getornull(p_item); + ERR_FAIL_COND(!canvas_item); + + canvas_item->update_when_visible = p_update; +} + void VisualServerCanvas::canvas_item_add_line(RID p_item, const Point2 &p_from, const Point2 &p_to, const Color &p_color, float p_width, bool p_antialiased) { Item *canvas_item = canvas_item_owner.getornull(p_item); @@ -676,7 +689,7 @@ void VisualServerCanvas::canvas_item_add_polygon(RID p_item, const Vector<Point2 if (indices.empty()) { ERR_EXPLAIN("Bad Polygon!"); - ERR_FAIL_V(); + ERR_FAIL(); } Item::CommandPolygon *polygon = memnew(Item::CommandPolygon); @@ -763,7 +776,7 @@ void VisualServerCanvas::canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID canvas_item->commands.push_back(m); } -void VisualServerCanvas::canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal, int p_h_frames, int p_v_frames) { +void VisualServerCanvas::canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal) { Item *canvas_item = canvas_item_owner.getornull(p_item); ERR_FAIL_COND(!canvas_item); @@ -773,8 +786,6 @@ void VisualServerCanvas::canvas_item_add_particles(RID p_item, RID p_particles, part->particles = p_particles; part->texture = p_texture; part->normal_map = p_normal; - part->h_frames = p_h_frames; - part->v_frames = p_v_frames; //take the chance and request processing for them, at least once until they become visible again VSG::storage->particles_request_process(p_particles); diff --git a/servers/visual/visual_server_canvas.h b/servers/visual/visual_server_canvas.h index 4d9398a17e..d12bd520bf 100644 --- a/servers/visual/visual_server_canvas.h +++ b/servers/visual/visual_server_canvas.h @@ -171,6 +171,8 @@ public: void canvas_item_set_draw_behind_parent(RID p_item, bool p_enable); + void canvas_item_set_update_when_visible(RID p_item, bool p_update); + void canvas_item_add_line(RID p_item, const Point2 &p_from, const Point2 &p_to, const Color &p_color, float p_width = 1.0, bool p_antialiased = false); void canvas_item_add_polyline(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width = 1.0, bool p_antialiased = false); void canvas_item_add_multiline(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width = 1.0, bool p_antialiased = false); @@ -184,7 +186,7 @@ public: void canvas_item_add_triangle_array(RID p_item, const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>(), RID p_texture = RID(), int p_count = -1, RID p_normal_map = RID()); void canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID p_texture = RID(), RID p_normal_map = RID()); void canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p_texture = RID(), RID p_normal_map = RID()); - void canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal, int p_h_frames, int p_v_frames); + void canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal); void canvas_item_add_set_transform(RID p_item, const Transform2D &p_transform); void canvas_item_add_clip_ignore(RID p_item, bool p_ignore); void canvas_item_set_sort_children_by_y(RID p_item, bool p_enable); diff --git a/servers/visual/visual_server_raster.cpp b/servers/visual/visual_server_raster.cpp index c7d33ec43c..677c323216 100644 --- a/servers/visual/visual_server_raster.cpp +++ b/servers/visual/visual_server_raster.cpp @@ -30,11 +30,10 @@ #include "visual_server_raster.h" -#include "default_mouse_cursor.xpm" -#include "io/marshalls.h" -#include "os/os.h" -#include "project_settings.h" -#include "sort.h" +#include "core/io/marshalls.h" +#include "core/os/os.h" +#include "core/project_settings.h" +#include "core/sort.h" #include "visual_server_canvas.h" #include "visual_server_global.h" #include "visual_server_scene.h" @@ -190,6 +189,9 @@ void VisualServerRaster::call_set_use_vsync(bool p_enable) { OS::get_singleton()->_set_use_vsync(p_enable); } +bool VisualServerRaster::is_low_end() const { + return VSG::rasterizer->is_low_end(); +} VisualServerRaster::VisualServerRaster() { VSG::canvas = memnew(VisualServerCanvas); diff --git a/servers/visual/visual_server_raster.h b/servers/visual/visual_server_raster.h index a00b364565..b33ef21e78 100644 --- a/servers/visual/visual_server_raster.h +++ b/servers/visual/visual_server_raster.h @@ -31,8 +31,8 @@ #ifndef VISUAL_SERVER_RASTER_H #define VISUAL_SERVER_RASTER_H -#include "allocators.h" -#include "octree.h" +#include "core/allocators.h" +#include "core/math/octree.h" #include "servers/visual/rasterizer.h" #include "servers/visual_server.h" #include "visual_server_canvas.h" @@ -59,7 +59,6 @@ class VisualServerRaster : public VisualServer { }; static int changes; - bool draw_extra_frame; RID test_cube; int black_margin[4]; @@ -203,6 +202,7 @@ public: BIND3(material_set_param, RID, const StringName &, const Variant &) BIND2RC(Variant, material_get_param, RID, const StringName &) + BIND2RC(Variant, material_get_param_default, RID, const StringName &) BIND2(material_set_render_priority, RID, int) BIND2(material_set_line_width, RID, float) @@ -336,6 +336,7 @@ public: BIND2(reflection_probe_set_enable_box_projection, RID, bool) BIND2(reflection_probe_set_enable_shadows, RID, bool) BIND2(reflection_probe_set_cull_mask, RID, uint32_t) + BIND2(reflection_probe_set_resolution, RID, int) /* BAKED LIGHT API */ @@ -473,7 +474,7 @@ public: BIND2(viewport_set_transparent_background, RID, bool) BIND2(viewport_set_global_canvas_transform, RID, const Transform2D &) - BIND3(viewport_set_canvas_layer, RID, RID, int) + BIND4(viewport_set_canvas_stacking, RID, RID, int, int) BIND2(viewport_set_shadow_atlas_size, RID, int) BIND3(viewport_set_shadow_atlas_quadrant_subdivision, RID, int, int) BIND2(viewport_set_msaa, RID, ViewportMSAA) @@ -574,6 +575,8 @@ public: BIND2(canvas_item_set_visible, RID, bool) BIND2(canvas_item_set_light_mask, RID, int) + BIND2(canvas_item_set_update_when_visible, RID, bool) + BIND2(canvas_item_set_transform, RID, const Transform2D &) BIND2(canvas_item_set_clip, RID, bool) BIND2(canvas_item_set_distance_field_mode, RID, bool) @@ -596,7 +599,7 @@ public: BIND10(canvas_item_add_triangle_array, RID, const Vector<int> &, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, const Vector<int> &, const Vector<float> &, RID, int, RID) BIND4(canvas_item_add_mesh, RID, const RID &, RID, RID) BIND4(canvas_item_add_multimesh, RID, RID, RID, RID) - BIND6(canvas_item_add_particles, RID, RID, RID, RID, int, int) + BIND4(canvas_item_add_particles, RID, RID, RID, RID) BIND2(canvas_item_add_set_transform, RID, const Transform2D &) BIND2(canvas_item_add_clip_ignore, RID, bool) BIND2(canvas_item_set_sort_children_by_y, RID, bool) @@ -686,6 +689,8 @@ public: virtual void call_set_use_vsync(bool p_enable); + virtual bool is_low_end() const; + VisualServerRaster(); ~VisualServerRaster(); diff --git a/servers/visual/visual_server_scene.cpp b/servers/visual/visual_server_scene.cpp index 1e255591f0..654994b83f 100644 --- a/servers/visual/visual_server_scene.cpp +++ b/servers/visual/visual_server_scene.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "visual_server_scene.h" -#include "os/os.h" +#include "core/os/os.h" #include "visual_server_global.h" #include "visual_server_raster.h" /* CAMERA API */ @@ -398,6 +398,7 @@ void VisualServerScene::instance_set_base(RID p_instance, RID p_base) { VSG::scene_render->free(gi_probe->probe_instance); } break; + default: {} } if (instance->base_data) { @@ -471,6 +472,7 @@ void VisualServerScene::instance_set_base(RID p_instance, RID p_base) { gi_probe->probe_instance = VSG::scene_render->gi_probe_instance_create(); } break; + default: {} } VSG::storage->instance_add_dependency(p_base, instance); @@ -518,6 +520,7 @@ void VisualServerScene::instance_set_scenario(RID p_instance, RID p_scenario) { gi_probe_update_list.remove(&gi_probe->update_element); } } break; + default: {} } instance->scenario = NULL; @@ -549,6 +552,7 @@ void VisualServerScene::instance_set_scenario(RID p_instance, RID p_scenario) { gi_probe_update_list.add(&gi_probe->update_element); } } break; + default: {} } _instance_queue_update(instance, true, true); @@ -649,6 +653,7 @@ void VisualServerScene::instance_set_visible(RID p_instance, bool p_visible) { } } break; + default: {} } } inline bool is_geometry_instance(VisualServer::InstanceType p_type) { @@ -825,6 +830,7 @@ void VisualServerScene::instance_geometry_set_flag(RID p_instance, VS::InstanceF instance->redraw_if_visible = p_enabled; } break; + default: {} } } void VisualServerScene::instance_geometry_set_cast_shadows_setting(RID p_instance, VS::ShadowCastingSetting p_shadow_casting_setting) { @@ -902,7 +908,7 @@ void VisualServerScene::_update_instance(Instance *p_instance) { _update_instance_lightmap_captures(p_instance); } else { if (!p_instance->lightmap_capture_data.empty()) { - !p_instance->lightmap_capture_data.resize(0); //not in use, clear capture data + p_instance->lightmap_capture_data.resize(0); //not in use, clear capture data } } } @@ -1016,7 +1022,6 @@ void VisualServerScene::_update_instance_aabb(Instance *p_instance) { new_aabb = VSG::storage->lightmap_capture_get_bounds(p_instance->base); } break; - default: {} } @@ -1378,9 +1383,12 @@ void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons float y_min = 0.f, y_max = 0.f; float z_min = 0.f, z_max = 0.f; + // FIXME: z_max_cam is defined, computed, but not used below when setting up + // ortho_camera. Commented out for now to fix warnings but should be investigated. float x_min_cam = 0.f, x_max_cam = 0.f; float y_min_cam = 0.f, y_max_cam = 0.f; - float z_min_cam = 0.f, z_max_cam = 0.f; + float z_min_cam = 0.f; + //float z_max_cam = 0.f; float bias_scale = 1.0; @@ -1442,7 +1450,7 @@ void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons x_min_cam = x_vec.dot(center) - radius; y_max_cam = y_vec.dot(center) + radius; y_min_cam = y_vec.dot(center) - radius; - z_max_cam = z_vec.dot(center) + radius; + //z_max_cam = z_vec.dot(center) + radius; z_min_cam = z_vec.dot(center) - radius; if (depth_range_mode == VS::LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_STABLE) { @@ -2133,6 +2141,8 @@ bool VisualServerScene::_render_reflection_probe_step(Instance *p_instance, int Scenario *scenario = p_instance->scenario; ERR_FAIL_COND_V(!scenario, true); + VisualServerRaster::redraw_request(); //update, so it updates in editor + if (p_step == 0) { if (!VSG::scene_render->reflection_probe_instance_begin_render(reflection_probe->instance, scenario->reflection_atlas)) { @@ -2821,7 +2831,7 @@ void VisualServerScene::_bake_gi_probe(Instance *p_gi_probe) { RID rid = E->key(); const InstanceGIProbeData::LightCache &lc = E->get(); - if ((!probe_data->dynamic.light_cache_changes.has(rid) || !(probe_data->dynamic.light_cache_changes[rid] == lc)) && lc.visible) { + if ((!probe_data->dynamic.light_cache_changes.has(rid) || probe_data->dynamic.light_cache_changes[rid] != lc) && lc.visible) { //erase light data _bake_gi_probe_light(header, cells, local_data, leaves, leaf_count, lc, -1); @@ -2834,7 +2844,7 @@ void VisualServerScene::_bake_gi_probe(Instance *p_gi_probe) { RID rid = E->key(); const InstanceGIProbeData::LightCache &lc = E->get(); - if ((!probe_data->dynamic.light_cache.has(rid) || !(probe_data->dynamic.light_cache[rid] == lc)) && lc.visible) { + if ((!probe_data->dynamic.light_cache.has(rid) || probe_data->dynamic.light_cache[rid] != lc) && lc.visible) { //add light data _bake_gi_probe_light(header, cells, local_data, leaves, leaf_count, lc, 1); @@ -3051,7 +3061,7 @@ bool VisualServerScene::_check_gi_probe(Instance *p_gi_probe) { lc.transform = probe_data->dynamic.light_to_cell_xform * E->get()->transform; lc.visible = E->get()->visible; - if (!probe_data->dynamic.light_cache.has(E->get()->self) || !(probe_data->dynamic.light_cache[E->get()->self] == lc)) { + if (!probe_data->dynamic.light_cache.has(E->get()->self) || probe_data->dynamic.light_cache[E->get()->self] != lc) { all_equal = false; } @@ -3071,7 +3081,7 @@ bool VisualServerScene::_check_gi_probe(Instance *p_gi_probe) { lc.transform = probe_data->dynamic.light_to_cell_xform * E->get()->transform; lc.visible = E->get()->visible; - if (!probe_data->dynamic.light_cache.has(E->get()->self) || !(probe_data->dynamic.light_cache[E->get()->self] == lc)) { + if (!probe_data->dynamic.light_cache.has(E->get()->self) || probe_data->dynamic.light_cache[E->get()->self] != lc) { all_equal = false; } @@ -3154,7 +3164,7 @@ void VisualServerScene::render_probes() { force_lighting = true; } - if (probe->invalid == false && probe->dynamic.enabled) { + if (!probe->invalid && probe->dynamic.enabled) { switch (probe->dynamic.updating_stage) { case GI_UPDATE_STAGE_CHECK: { diff --git a/servers/visual/visual_server_scene.h b/servers/visual/visual_server_scene.h index 12d732724a..0d4737f268 100644 --- a/servers/visual/visual_server_scene.h +++ b/servers/visual/visual_server_scene.h @@ -33,12 +33,12 @@ #include "servers/visual/rasterizer.h" -#include "allocators.h" -#include "geometry.h" -#include "octree.h" -#include "os/semaphore.h" -#include "os/thread.h" -#include "self_list.h" +#include "core/allocators.h" +#include "core/math/geometry.h" +#include "core/math/octree.h" +#include "core/os/semaphore.h" +#include "core/os/thread.h" +#include "core/self_list.h" #include "servers/arvr/arvr_interface.h" class VisualServerScene { @@ -355,6 +355,11 @@ public: visible == p_cache.visible); } + bool operator!=(const LightCache &p_cache) { + + return !operator==(p_cache); + } + LightCache() { type = VS::LIGHT_DIRECTIONAL; @@ -544,7 +549,7 @@ public: bool free(RID p_rid); VisualServerScene(); - ~VisualServerScene(); + virtual ~VisualServerScene(); }; #endif // VISUALSERVERSCENE_H diff --git a/servers/visual/visual_server_viewport.cpp b/servers/visual/visual_server_viewport.cpp index 90acba306a..571b71db85 100644 --- a/servers/visual/visual_server_viewport.cpp +++ b/servers/visual/visual_server_viewport.cpp @@ -30,7 +30,7 @@ #include "visual_server_viewport.h" -#include "project_settings.h" +#include "core/project_settings.h" #include "visual_server_canvas.h" #include "visual_server_global.h" #include "visual_server_scene.h" @@ -137,7 +137,7 @@ void VisualServerViewport::_draw_viewport(Viewport *p_viewport, ARVRInterface::E } } - canvas_map[Viewport::CanvasKey(E->key(), E->get().layer)] = &E->get(); + canvas_map[Viewport::CanvasKey(E->key(), E->get().layer, E->get().sublayer)] = &E->get(); } if (lights_with_shadow) { @@ -176,7 +176,7 @@ void VisualServerViewport::_draw_viewport(Viewport *p_viewport, ARVRInterface::E VSG::rasterizer->restore_render_target(); - if (scenario_draw_canvas_bg && canvas_map.front() && canvas_map.front()->key().layer > scenario_canvas_max_layer) { + if (scenario_draw_canvas_bg && canvas_map.front() && canvas_map.front()->key().get_layer() > scenario_canvas_max_layer) { Ref<ARVRInterface> arvr_interface = ARVRServer::get_singleton()->get_primary_interface(); if (!can_draw_3d) { @@ -209,7 +209,7 @@ void VisualServerViewport::_draw_viewport(Viewport *p_viewport, ARVRInterface::E VSG::canvas->render_canvas(canvas, xform, canvas_lights, lights_with_mask, clip_rect); i++; - if (scenario_draw_canvas_bg && E->key().layer >= scenario_canvas_max_layer) { + if (scenario_draw_canvas_bg && E->key().get_layer() >= scenario_canvas_max_layer) { Ref<ARVRInterface> arvr_interface = ARVRServer::get_singleton()->get_primary_interface(); if (!can_draw_3d) { @@ -294,7 +294,7 @@ void VisualServerViewport::draw_viewports() { arvr_interface->commit_for_eye(ARVRInterface::EYE_RIGHT, vp->render_target, vp->viewport_to_screen_rect); } - // and for our frame timing, mark when we've finished commiting our eyes + // and for our frame timing, mark when we've finished committing our eyes ARVRServer::get_singleton()->_mark_commit(); } else { VSG::rasterizer->set_current_render_target(vp->render_target); @@ -496,6 +496,7 @@ void VisualServerViewport::viewport_attach_canvas(RID p_viewport, RID p_canvas) canvas->viewports.insert(p_viewport); viewport->canvas_map[p_canvas] = Viewport::CanvasData(); viewport->canvas_map[p_canvas].layer = 0; + viewport->canvas_map[p_canvas].sublayer = 0; viewport->canvas_map[p_canvas].canvas = canvas; } @@ -534,13 +535,14 @@ void VisualServerViewport::viewport_set_global_canvas_transform(RID p_viewport, viewport->global_transform = p_transform; } -void VisualServerViewport::viewport_set_canvas_layer(RID p_viewport, RID p_canvas, int p_layer) { +void VisualServerViewport::viewport_set_canvas_stacking(RID p_viewport, RID p_canvas, int p_layer, int p_sublayer) { Viewport *viewport = viewport_owner.getornull(p_viewport); ERR_FAIL_COND(!viewport); ERR_FAIL_COND(!viewport->canvas_map.has(p_canvas)); viewport->canvas_map[p_canvas].layer = p_layer; + viewport->canvas_map[p_canvas].sublayer = p_sublayer; } void VisualServerViewport::viewport_set_shadow_atlas_size(RID p_viewport, int p_size) { diff --git a/servers/visual/visual_server_viewport.h b/servers/visual/visual_server_viewport.h index f915e26b81..66baa48458 100644 --- a/servers/visual/visual_server_viewport.h +++ b/servers/visual/visual_server_viewport.h @@ -31,8 +31,8 @@ #ifndef VISUALSERVERVIEWPORT_H #define VISUALSERVERVIEWPORT_H +#include "core/self_list.h" #include "rasterizer.h" -#include "self_list.h" #include "servers/arvr/arvr_interface.h" #include "servers/visual_server.h" @@ -78,17 +78,21 @@ public: struct CanvasKey { - int layer; + int64_t stacking; RID canvas; bool operator<(const CanvasKey &p_canvas) const { - if (layer == p_canvas.layer) return canvas < p_canvas.canvas; - return layer < p_canvas.layer; + if (stacking == p_canvas.stacking) + return canvas < p_canvas.canvas; + return stacking < p_canvas.stacking; + } + CanvasKey() { + stacking = 0; } - CanvasKey() { layer = 0; } - CanvasKey(const RID &p_canvas, int p_layer) { + CanvasKey(const RID &p_canvas, int p_layer, int p_sublayer) { canvas = p_canvas; - layer = p_layer; + stacking = ((int64_t)p_layer << 32) + p_sublayer; } + int get_layer() const { return stacking >> 32; } }; struct CanvasData { @@ -96,6 +100,7 @@ public: CanvasBase *canvas; Transform2D transform; int layer; + int sublayer; }; Transform2D global_transform; @@ -176,7 +181,7 @@ public: void viewport_set_transparent_background(RID p_viewport, bool p_enabled); void viewport_set_global_canvas_transform(RID p_viewport, const Transform2D &p_transform); - void viewport_set_canvas_layer(RID p_viewport, RID p_canvas, int p_layer); + void viewport_set_canvas_stacking(RID p_viewport, RID p_canvas, int p_layer, int p_sublayer); void viewport_set_shadow_atlas_size(RID p_viewport, int p_size); void viewport_set_shadow_atlas_quadrant_subdivision(RID p_viewport, int p_quadrant, int p_subdiv); @@ -194,6 +199,7 @@ public: bool free(RID p_rid); VisualServerViewport(); + virtual ~VisualServerViewport() {} }; #endif // VISUALSERVERVIEWPORT_H diff --git a/servers/visual/visual_server_wrap_mt.cpp b/servers/visual/visual_server_wrap_mt.cpp index 1cafc47685..f59d8af9cb 100644 --- a/servers/visual/visual_server_wrap_mt.cpp +++ b/servers/visual/visual_server_wrap_mt.cpp @@ -29,8 +29,8 @@ /*************************************************************************/ #include "visual_server_wrap_mt.h" -#include "os/os.h" -#include "project_settings.h" +#include "core/os/os.h" +#include "core/project_settings.h" void VisualServerWrapMT::thread_exit() { diff --git a/servers/visual/visual_server_wrap_mt.h b/servers/visual/visual_server_wrap_mt.h index 3a4d72c793..137fcb55f4 100644 --- a/servers/visual/visual_server_wrap_mt.h +++ b/servers/visual/visual_server_wrap_mt.h @@ -31,8 +31,8 @@ #ifndef VISUAL_SERVER_WRAP_MT_H #define VISUAL_SERVER_WRAP_MT_H -#include "command_queue_mt.h" -#include "os/thread.h" +#include "core/command_queue_mt.h" +#include "core/os/thread.h" #include "servers/visual_server.h" /** @@ -137,6 +137,7 @@ public: FUNC3(material_set_param, RID, const StringName &, const Variant &) FUNC2RC(Variant, material_get_param, RID, const StringName &) + FUNC2RC(Variant, material_get_param_default, RID, const StringName &) FUNC2(material_set_render_priority, RID, int) FUNC2(material_set_line_width, RID, float) @@ -270,6 +271,7 @@ public: FUNC2(reflection_probe_set_enable_box_projection, RID, bool) FUNC2(reflection_probe_set_enable_shadows, RID, bool) FUNC2(reflection_probe_set_cull_mask, RID, uint32_t) + FUNC2(reflection_probe_set_resolution, RID, int) /* BAKED LIGHT API */ @@ -398,7 +400,7 @@ public: FUNC2(viewport_set_transparent_background, RID, bool) FUNC2(viewport_set_global_canvas_transform, RID, const Transform2D &) - FUNC3(viewport_set_canvas_layer, RID, RID, int) + FUNC4(viewport_set_canvas_stacking, RID, RID, int, int) FUNC2(viewport_set_shadow_atlas_size, RID, int) FUNC3(viewport_set_shadow_atlas_quadrant_subdivision, RID, int, int) FUNC2(viewport_set_msaa, RID, ViewportMSAA) @@ -490,6 +492,8 @@ public: FUNC2(canvas_item_set_visible, RID, bool) FUNC2(canvas_item_set_light_mask, RID, int) + FUNC2(canvas_item_set_update_when_visible, RID, bool) + FUNC2(canvas_item_set_transform, RID, const Transform2D &) FUNC2(canvas_item_set_clip, RID, bool) FUNC2(canvas_item_set_distance_field_mode, RID, bool) @@ -512,7 +516,7 @@ public: FUNC10(canvas_item_add_triangle_array, RID, const Vector<int> &, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, const Vector<int> &, const Vector<float> &, RID, int, RID) FUNC4(canvas_item_add_mesh, RID, const RID &, RID, RID) FUNC4(canvas_item_add_multimesh, RID, RID, RID, RID) - FUNC6(canvas_item_add_particles, RID, RID, RID, RID, int, int) + FUNC4(canvas_item_add_particles, RID, RID, RID, RID) FUNC2(canvas_item_add_set_transform, RID, const Transform2D &) FUNC2(canvas_item_add_clip_ignore, RID, bool) FUNC2(canvas_item_set_sort_children_by_y, RID, bool) @@ -605,6 +609,10 @@ public: static void set_use_vsync_callback(bool p_enable); + virtual bool is_low_end() const { + return visual_server->is_low_end(); + } + VisualServerWrapMT(VisualServer *p_contained, bool p_create_thread); ~VisualServerWrapMT(); diff --git a/servers/visual_server.cpp b/servers/visual_server.cpp index 18a04e9a4b..4b0e5cd28d 100644 --- a/servers/visual_server.cpp +++ b/servers/visual_server.cpp @@ -30,8 +30,8 @@ #include "visual_server.h" -#include "method_bind_ext.gen.inc" -#include "project_settings.h" +#include "core/method_bind_ext.gen.inc" +#include "core/project_settings.h" VisualServer *VisualServer::singleton = NULL; VisualServer *(*VisualServer::create_func)() = NULL; @@ -1695,6 +1695,7 @@ void VisualServer::_bind_methods() { ClassDB::bind_method(D_METHOD("material_get_shader", "shader_material"), &VisualServer::material_get_shader); ClassDB::bind_method(D_METHOD("material_set_param", "material", "parameter", "value"), &VisualServer::material_set_param); ClassDB::bind_method(D_METHOD("material_get_param", "material", "parameter"), &VisualServer::material_get_param); + ClassDB::bind_method(D_METHOD("material_get_param_default", "material", "parameter"), &VisualServer::material_get_param_default); ClassDB::bind_method(D_METHOD("material_set_render_priority", "material", "priority"), &VisualServer::material_set_render_priority); ClassDB::bind_method(D_METHOD("material_set_line_width", "material", "width"), &VisualServer::material_set_line_width); ClassDB::bind_method(D_METHOD("material_set_next_pass", "material", "next_material"), &VisualServer::material_set_next_pass); @@ -1889,7 +1890,7 @@ void VisualServer::_bind_methods() { ClassDB::bind_method(D_METHOD("viewport_set_canvas_transform", "viewport", "canvas", "offset"), &VisualServer::viewport_set_canvas_transform); ClassDB::bind_method(D_METHOD("viewport_set_transparent_background", "viewport", "enabled"), &VisualServer::viewport_set_transparent_background); ClassDB::bind_method(D_METHOD("viewport_set_global_canvas_transform", "viewport", "transform"), &VisualServer::viewport_set_global_canvas_transform); - ClassDB::bind_method(D_METHOD("viewport_set_canvas_layer", "viewport", "canvas", "layer"), &VisualServer::viewport_set_canvas_layer); + ClassDB::bind_method(D_METHOD("viewport_set_canvas_stacking", "viewport", "canvas", "layer", "sublayer"), &VisualServer::viewport_set_canvas_stacking); ClassDB::bind_method(D_METHOD("viewport_set_shadow_atlas_size", "viewport", "size"), &VisualServer::viewport_set_shadow_atlas_size); ClassDB::bind_method(D_METHOD("viewport_set_shadow_atlas_quadrant_subdivision", "viewport", "quadrant", "subdivision"), &VisualServer::viewport_set_shadow_atlas_quadrant_subdivision); ClassDB::bind_method(D_METHOD("viewport_set_msaa", "viewport", "msaa"), &VisualServer::viewport_set_msaa); @@ -1977,7 +1978,7 @@ void VisualServer::_bind_methods() { ClassDB::bind_method(D_METHOD("canvas_item_add_triangle_array", "item", "indices", "points", "colors", "uvs", "bones", "weights", "texture", "count", "normal_map"), &VisualServer::canvas_item_add_triangle_array, DEFVAL(Vector<Point2>()), DEFVAL(Vector<int>()), DEFVAL(Vector<float>()), DEFVAL(RID()), DEFVAL(-1), DEFVAL(RID())); ClassDB::bind_method(D_METHOD("canvas_item_add_mesh", "item", "mesh", "texture", "normal_map"), &VisualServer::canvas_item_add_mesh, DEFVAL(RID())); ClassDB::bind_method(D_METHOD("canvas_item_add_multimesh", "item", "mesh", "texture", "normal_map"), &VisualServer::canvas_item_add_multimesh, DEFVAL(RID())); - ClassDB::bind_method(D_METHOD("canvas_item_add_particles", "item", "particles", "texture", "normal_map", "h_frames", "v_frames"), &VisualServer::canvas_item_add_particles); + ClassDB::bind_method(D_METHOD("canvas_item_add_particles", "item", "particles", "texture", "normal_map"), &VisualServer::canvas_item_add_particles); ClassDB::bind_method(D_METHOD("canvas_item_add_set_transform", "item", "transform"), &VisualServer::canvas_item_add_set_transform); ClassDB::bind_method(D_METHOD("canvas_item_add_clip_ignore", "item", "ignore"), &VisualServer::canvas_item_add_clip_ignore); ClassDB::bind_method(D_METHOD("canvas_item_set_sort_children_by_y", "item", "enabled"), &VisualServer::canvas_item_set_sort_children_by_y); @@ -2282,7 +2283,7 @@ void VisualServer::_bind_methods() { BIND_ENUM_CONSTANT(GLOW_BLEND_MODE_REPLACE); BIND_ENUM_CONSTANT(ENV_TONE_MAPPER_LINEAR); - BIND_ENUM_CONSTANT(ENV_TONE_MAPPER_REINHARDT); + BIND_ENUM_CONSTANT(ENV_TONE_MAPPER_REINHARD); BIND_ENUM_CONSTANT(ENV_TONE_MAPPER_FILMIC); BIND_ENUM_CONSTANT(ENV_TONE_MAPPER_ACES); @@ -2369,6 +2370,7 @@ VisualServer::VisualServer() { GLOBAL_DEF("rendering/quality/directional_shadow/size", 4096); GLOBAL_DEF("rendering/quality/directional_shadow/size.mobile", 2048); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/directional_shadow/size", PropertyInfo(Variant::INT, "rendering/quality/directional_shadow/size", PROPERTY_HINT_RANGE, "256,16384")); GLOBAL_DEF("rendering/quality/shadow_atlas/size", 4096); GLOBAL_DEF("rendering/quality/shadow_atlas/size.mobile", 2048); ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/shadow_atlas/size", PropertyInfo(Variant::INT, "rendering/quality/shadow_atlas/size", PROPERTY_HINT_RANGE, "256,16384")); @@ -2392,6 +2394,10 @@ VisualServer::VisualServer() { GLOBAL_DEF("rendering/quality/shading/force_vertex_shading", false); GLOBAL_DEF("rendering/quality/shading/force_vertex_shading.mobile", true); + GLOBAL_DEF("rendering/quality/shading/force_lambert_over_burley", false); + GLOBAL_DEF("rendering/quality/shading/force_lambert_over_burley.mobile", true); + GLOBAL_DEF("rendering/quality/shading/force_blinn_over_ggx", false); + GLOBAL_DEF("rendering/quality/shading/force_blinn_over_ggx.mobile", true); GLOBAL_DEF("rendering/quality/depth_prepass/enable", true); GLOBAL_DEF("rendering/quality/depth_prepass/disable_for_vendors", "PowerVR,Mali,Adreno"); diff --git a/servers/visual_server.h b/servers/visual_server.h index fd7f96339e..5af10ded28 100644 --- a/servers/visual_server.h +++ b/servers/visual_server.h @@ -31,13 +31,13 @@ #ifndef VISUAL_SERVER_H #define VISUAL_SERVER_H -#include "bsp_tree.h" -#include "geometry.h" -#include "image.h" -#include "object.h" -#include "rid.h" -#include "transform_2d.h" -#include "variant.h" +#include "core/image.h" +#include "core/math/bsp_tree.h" +#include "core/math/geometry.h" +#include "core/math/transform_2d.h" +#include "core/object.h" +#include "core/rid.h" +#include "core/variant.h" /** @author Juan Linietsky <reduzio@gmail.com> @@ -209,6 +209,7 @@ public: virtual void material_set_param(RID p_material, const StringName &p_param, const Variant &p_value) = 0; virtual Variant material_get_param(RID p_material, const StringName &p_param) const = 0; + virtual Variant material_get_param_default(RID p_material, const StringName &p_param) const = 0; virtual void material_set_render_priority(RID p_material, int priority) = 0; @@ -489,6 +490,7 @@ public: virtual void reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable) = 0; virtual void reflection_probe_set_enable_shadows(RID p_probe, bool p_enable) = 0; virtual void reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) = 0; + virtual void reflection_probe_set_resolution(RID p_probe, int p_resolution) = 0; /* GI PROBE API */ @@ -642,7 +644,7 @@ public: virtual void viewport_set_transparent_background(RID p_viewport, bool p_enabled) = 0; virtual void viewport_set_global_canvas_transform(RID p_viewport, const Transform2D &p_transform) = 0; - virtual void viewport_set_canvas_layer(RID p_viewport, RID p_canvas, int p_layer) = 0; + virtual void viewport_set_canvas_stacking(RID p_viewport, RID p_canvas, int p_layer, int p_sublayer) = 0; virtual void viewport_set_shadow_atlas_size(RID p_viewport, int p_size) = 0; virtual void viewport_set_shadow_atlas_quadrant_subdivision(RID p_viewport, int p_quadrant, int p_subdiv) = 0; @@ -735,7 +737,7 @@ public: enum EnvironmentToneMapper { ENV_TONE_MAPPER_LINEAR, - ENV_TONE_MAPPER_REINHARDT, + ENV_TONE_MAPPER_REINHARD, ENV_TONE_MAPPER_FILMIC, ENV_TONE_MAPPER_ACES }; @@ -863,6 +865,8 @@ public: virtual void canvas_item_set_visible(RID p_item, bool p_visible) = 0; virtual void canvas_item_set_light_mask(RID p_item, int p_mask) = 0; + virtual void canvas_item_set_update_when_visible(RID p_item, bool p_update) = 0; + virtual void canvas_item_set_transform(RID p_item, const Transform2D &p_transform) = 0; virtual void canvas_item_set_clip(RID p_item, bool p_clip) = 0; virtual void canvas_item_set_distance_field_mode(RID p_item, bool p_enable) = 0; @@ -891,7 +895,7 @@ public: virtual void canvas_item_add_triangle_array(RID p_item, const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>(), RID p_texture = RID(), int p_count = -1, RID p_normal_map = RID()) = 0; virtual void canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID p_texture = RID(), RID p_normal_map = RID()) = 0; virtual void canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p_texture = RID(), RID p_normal_map = RID()) = 0; - virtual void canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal_map, int p_h_frames, int p_v_frames) = 0; + virtual void canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal_map) = 0; virtual void canvas_item_add_set_transform(RID p_item, const Transform2D &p_transform) = 0; virtual void canvas_item_add_clip_ignore(RID p_item, bool p_ignore) = 0; virtual void canvas_item_set_sort_children_by_y(RID p_item, bool p_enable) = 0; @@ -1033,6 +1037,8 @@ public: virtual void call_set_use_vsync(bool p_enable) = 0; + virtual bool is_low_end() const = 0; + VisualServer(); virtual ~VisualServer(); }; diff --git a/thirdparty/README.md b/thirdparty/README.md index 9da3d857a6..1e1fea85e5 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -19,7 +19,7 @@ comments. ## bullet - Upstream: https://github.com/bulletphysics/bullet3 -- Version: git (d05ad4b, 2017) +- Version: git (12409f1118a7c7a266f9071350c70789dfe73bb9, Commits on Sep 6, 2018 ) - License: zlib Files extracted from upstream source: @@ -264,8 +264,8 @@ File extracted from upstream source: - From `roles/ws` exclude `ext` folder. - From `tls` exclude `openssl` folder. - Also copy `win32helpers/` from `win32port/` inside `thirdparty/libwebsockets` -- A small fix has been added in `libwebsockets/libwebsockets.h` to `#include <sys/socket.h>` for the BSD family. - This change has been PRed upstream, and should be merged before the next update. Remember to check and remove this line. +- A fix has been added to allow building for 32-bits UWP, replacing `GetFileSize[Ex]` and `CreateFileW` with supported functions. + There is a diff for this change in `thirdparty/libwebsockets/uwp_fixes.diff` Important: `lws_config.h` and `lws_config_private.h` contains custom Godot build configurations, check them out when updating. @@ -354,6 +354,11 @@ Collection of single-file libraries used in Godot components. * Upstream: https://github.com/ivanfratric/polypartition (`src/polypartition.cpp`) * Version: TBD, class was renamed * License: MIT +- `open-simplex-noise.{c,h}` + * Upstream: https://github.com/smcameron/open-simplex-noise-in-c + * Version: git (0d555e7, 2015) + * License: Unlicense + ### modules @@ -368,6 +373,10 @@ Collection of single-file libraries used in Godot components. ### scene +- `easing_equations.cpp` + * Upstream: http://robertpenner.com/easing/ via https://github.com/jesusgollonet/ofpennereasing (modified to fit Godot types) + * Version: git (af72c14, 2008) + Godot types and style changes + * License: BSD-3-Clause - `mikktspace.{c,h}` * Upstream: https://wiki.blender.org/index.php/Dev:Shading/Tangent_Space_Normal_Maps * Version: 1.0 @@ -494,7 +503,7 @@ changes are marked with `// -- GODOT --` comments. ## tinyexr - Upstream: https://github.com/syoyo/tinyexr -- Version: git (e385dad, 2018) +- Version: git (2d5375f, 2018) - License: BSD-3-Clause Files extracted from upstream source: @@ -523,3 +532,5 @@ Files extracted from upstream source: - lib/{common/,compress/,decompress/,zstd.h} - LICENSE + +- Applied the patch in `thirdparty/zstd/1314.diff` (PR 1314 upstream, already merged). Needed to build on UWP ARM. Can be removed when a new version is released with the patch. diff --git a/thirdparty/b2d_convexdecomp/b2Glue.h b/thirdparty/b2d_convexdecomp/b2Glue.h index 10c2f62361..175f75be75 100644 --- a/thirdparty/b2d_convexdecomp/b2Glue.h +++ b/thirdparty/b2d_convexdecomp/b2Glue.h @@ -19,7 +19,8 @@ #ifndef B2GLUE_H #define B2GLUE_H -#include "vector2.h" +#include "core/math/vector2.h" + #include <limits.h> namespace b2ConvexDecomp { diff --git a/thirdparty/bullet/Bullet3Common/b3AlignedObjectArray.h b/thirdparty/bullet/Bullet3Common/b3AlignedObjectArray.h index 947362d08e..ef71016565 100644 --- a/thirdparty/bullet/Bullet3Common/b3AlignedObjectArray.h +++ b/thirdparty/bullet/Bullet3Common/b3AlignedObjectArray.h @@ -528,6 +528,14 @@ protected: otherArray.copy(0, otherSize, m_data); } + void removeAtIndex(int index) + { + if (index<size()) + { + swap( index,size()-1); + pop_back(); + } + } }; #endif //B3_OBJECT_ARRAY__ diff --git a/thirdparty/bullet/Bullet3Common/b3FileUtils.h b/thirdparty/bullet/Bullet3Common/b3FileUtils.h index 1a331029ea..b5e8225cf0 100644 --- a/thirdparty/bullet/Bullet3Common/b3FileUtils.h +++ b/thirdparty/bullet/Bullet3Common/b3FileUtils.h @@ -36,7 +36,7 @@ struct b3FileUtils for (int i=0;!f && i<numPrefixes;i++) { -#ifdef _WIN32 +#ifdef _MSC_VER sprintf_s(relativeFileName,maxRelativeFileNameMaxLen,"%s%s",prefix[i],orgFileName); #else sprintf(relativeFileName,"%s%s",prefix[i],orgFileName); diff --git a/thirdparty/bullet/Bullet3Dynamics/ConstraintSolver/b3Generic6DofConstraint.cpp b/thirdparty/bullet/Bullet3Dynamics/ConstraintSolver/b3Generic6DofConstraint.cpp index 168a773d56..3ae2922e58 100644 --- a/thirdparty/bullet/Bullet3Dynamics/ConstraintSolver/b3Generic6DofConstraint.cpp +++ b/thirdparty/bullet/Bullet3Dynamics/ConstraintSolver/b3Generic6DofConstraint.cpp @@ -599,8 +599,8 @@ int b3Generic6DofConstraint::get_limit_motor_info2( tag_vel, info->fps * limot->m_stopERP); info->m_constraintError[srow] += mot_fact * limot->m_targetVelocity; - info->m_lowerLimit[srow] = -limot->m_maxMotorForce; - info->m_upperLimit[srow] = limot->m_maxMotorForce; + info->m_lowerLimit[srow] = -limot->m_maxMotorForce / info->fps; + info->m_upperLimit[srow] = limot->m_maxMotorForce / info->fps; } } if(limit) diff --git a/thirdparty/bullet/Bullet3Dynamics/ConstraintSolver/b3Generic6DofConstraint.h b/thirdparty/bullet/Bullet3Dynamics/ConstraintSolver/b3Generic6DofConstraint.h index 084d36055c..169b1b94ad 100644 --- a/thirdparty/bullet/Bullet3Dynamics/ConstraintSolver/b3Generic6DofConstraint.h +++ b/thirdparty/bullet/Bullet3Dynamics/ConstraintSolver/b3Generic6DofConstraint.h @@ -69,7 +69,7 @@ public: { m_accumulatedImpulse = 0.f; m_targetVelocity = 0; - m_maxMotorForce = 0.1f; + m_maxMotorForce = 6.0f; m_maxLimitForce = 300.0f; m_loLimit = 1.0f; m_hiLimit = -1.0f; diff --git a/thirdparty/bullet/Bullet3OpenCL/Initialize/b3OpenCLUtils.cpp b/thirdparty/bullet/Bullet3OpenCL/Initialize/b3OpenCLUtils.cpp index dd194fc7ba..896191c89c 100644 --- a/thirdparty/bullet/Bullet3OpenCL/Initialize/b3OpenCLUtils.cpp +++ b/thirdparty/bullet/Bullet3OpenCL/Initialize/b3OpenCLUtils.cpp @@ -619,7 +619,7 @@ cl_program b3OpenCLUtils_compileCLProgramFromString(cl_context clContext, cl_dev strippedName = strip2(clFileNameForCaching,"\\"); strippedName = strip2(strippedName,"/"); -#ifdef _MSVC_VER +#ifdef _MSC_VER sprintf_s(binaryFileName,B3_MAX_STRING_LENGTH,"%s/%s.%s.%s.bin",sCachedBinaryPath,strippedName, deviceName,driverVersion ); #else sprintf(binaryFileName,"%s/%s.%s.%s.bin",sCachedBinaryPath,strippedName, deviceName,driverVersion ); diff --git a/thirdparty/bullet/BulletCollision/BroadphaseCollision/btAxisSweep3Internal.h b/thirdparty/bullet/BulletCollision/BroadphaseCollision/btAxisSweep3Internal.h index 2c4d41bc04..323aa96dca 100644 --- a/thirdparty/bullet/BulletCollision/BroadphaseCollision/btAxisSweep3Internal.h +++ b/thirdparty/bullet/BulletCollision/BroadphaseCollision/btAxisSweep3Internal.h @@ -628,7 +628,6 @@ void btAxisSweep3Internal<BP_FP_INT_TYPE>::resetPool(btDispatcher* /*dispatcher* } -extern int gOverlappingPairs; //#include <stdio.h> template <typename BP_FP_INT_TYPE> @@ -695,10 +694,9 @@ void btAxisSweep3Internal<BP_FP_INT_TYPE>::calculateOverlappingPairs(btDispatche pair.m_pProxy0 = 0; pair.m_pProxy1 = 0; m_invalidPair++; - gOverlappingPairs--; - } - - } + } + + } ///if you don't like to skip the invalid pairs in the array, execute following code: #define CLEAN_INVALID_PAIRS 1 diff --git a/thirdparty/bullet/BulletCollision/BroadphaseCollision/btBroadphaseProxy.h b/thirdparty/bullet/BulletCollision/BroadphaseCollision/btBroadphaseProxy.h index adaf083a21..f6e1202a69 100644 --- a/thirdparty/bullet/BulletCollision/BroadphaseCollision/btBroadphaseProxy.h +++ b/thirdparty/bullet/BulletCollision/BroadphaseCollision/btBroadphaseProxy.h @@ -66,6 +66,7 @@ CONCAVE_SHAPES_START_HERE, EMPTY_SHAPE_PROXYTYPE, STATIC_PLANE_PROXYTYPE, CUSTOM_CONCAVE_SHAPE_TYPE, + SDF_SHAPE_PROXYTYPE=CUSTOM_CONCAVE_SHAPE_TYPE, CONCAVE_SHAPES_END_HERE, COMPOUND_SHAPE_PROXYTYPE, diff --git a/thirdparty/bullet/BulletCollision/BroadphaseCollision/btDbvtBroadphase.cpp b/thirdparty/bullet/BulletCollision/BroadphaseCollision/btDbvtBroadphase.cpp index 4d12b1c9c7..14cd1a31ea 100644 --- a/thirdparty/bullet/BulletCollision/BroadphaseCollision/btDbvtBroadphase.cpp +++ b/thirdparty/bullet/BulletCollision/BroadphaseCollision/btDbvtBroadphase.cpp @@ -17,7 +17,7 @@ subject to the following restrictions: #include "btDbvtBroadphase.h" #include "LinearMath/btThreads.h" - +btScalar gDbvtMargin = btScalar(0.05); // // Profiling // @@ -332,12 +332,9 @@ void btDbvtBroadphase::setAabb( btBroadphaseProxy* absproxy, if(delta[0]<0) velocity[0]=-velocity[0]; if(delta[1]<0) velocity[1]=-velocity[1]; if(delta[2]<0) velocity[2]=-velocity[2]; - if ( -#ifdef DBVT_BP_MARGIN - m_sets[0].update(proxy->leaf,aabb,velocity,DBVT_BP_MARGIN) -#else - m_sets[0].update(proxy->leaf,aabb,velocity) -#endif + if ( + m_sets[0].update(proxy->leaf, aabb, velocity, gDbvtMargin) + ) { ++m_updates_done; diff --git a/thirdparty/bullet/BulletCollision/BroadphaseCollision/btDbvtBroadphase.h b/thirdparty/bullet/BulletCollision/BroadphaseCollision/btDbvtBroadphase.h index 8feb95d51f..90b333d846 100644 --- a/thirdparty/bullet/BulletCollision/BroadphaseCollision/btDbvtBroadphase.h +++ b/thirdparty/bullet/BulletCollision/BroadphaseCollision/btDbvtBroadphase.h @@ -29,7 +29,8 @@ subject to the following restrictions: #define DBVT_BP_PREVENTFALSEUPDATE 0 #define DBVT_BP_ACCURATESLEEPING 0 #define DBVT_BP_ENABLE_BENCHMARK 0 -#define DBVT_BP_MARGIN (btScalar)0.05 +//#define DBVT_BP_MARGIN (btScalar)0.05 +extern btScalar gDbvtMargin; #if DBVT_BP_PROFILE #define DBVT_BP_PROFILING_RATE 256 diff --git a/thirdparty/bullet/BulletCollision/BroadphaseCollision/btDispatcher.h b/thirdparty/bullet/BulletCollision/BroadphaseCollision/btDispatcher.h index 7b0f9489af..a0e4c18927 100644 --- a/thirdparty/bullet/BulletCollision/BroadphaseCollision/btDispatcher.h +++ b/thirdparty/bullet/BulletCollision/BroadphaseCollision/btDispatcher.h @@ -46,7 +46,8 @@ struct btDispatcherInfo m_useEpa(true), m_allowedCcdPenetration(btScalar(0.04)), m_useConvexConservativeDistanceUtil(false), - m_convexConservativeDistanceThreshold(0.0f) + m_convexConservativeDistanceThreshold(0.0f), + m_deterministicOverlappingPairs(false) { } @@ -62,6 +63,7 @@ struct btDispatcherInfo btScalar m_allowedCcdPenetration; bool m_useConvexConservativeDistanceUtil; btScalar m_convexConservativeDistanceThreshold; + bool m_deterministicOverlappingPairs; }; enum ebtDispatcherQueryType diff --git a/thirdparty/bullet/BulletCollision/BroadphaseCollision/btOverlappingPairCache.cpp b/thirdparty/bullet/BulletCollision/BroadphaseCollision/btOverlappingPairCache.cpp index 55ebf06f1e..9e3337c5f6 100644 --- a/thirdparty/bullet/BulletCollision/BroadphaseCollision/btOverlappingPairCache.cpp +++ b/thirdparty/bullet/BulletCollision/BroadphaseCollision/btOverlappingPairCache.cpp @@ -23,11 +23,6 @@ subject to the following restrictions: #include <stdio.h> -int gOverlappingPairs = 0; - -int gRemovePairs =0; -int gAddedPairs =0; -int gFindPairs =0; @@ -134,13 +129,12 @@ void btHashedOverlappingPairCache::removeOverlappingPairsContainingProxy(btBroad btBroadphasePair* btHashedOverlappingPairCache::findPair(btBroadphaseProxy* proxy0, btBroadphaseProxy* proxy1) { - gFindPairs++; - if(proxy0->m_uniqueId>proxy1->m_uniqueId) + if(proxy0->m_uniqueId>proxy1->m_uniqueId) btSwap(proxy0,proxy1); int proxyId1 = proxy0->getUid(); int proxyId2 = proxy1->getUid(); - /*if (proxyId1 > proxyId2) + /*if (proxyId1 > proxyId2) btSwap(proxyId1, proxyId2);*/ int hash = static_cast<int>(getHash(static_cast<unsigned int>(proxyId1), static_cast<unsigned int>(proxyId2)) & (m_overlappingPairArray.capacity()-1)); @@ -271,13 +265,12 @@ btBroadphasePair* btHashedOverlappingPairCache::internalAddPair(btBroadphaseProx void* btHashedOverlappingPairCache::removeOverlappingPair(btBroadphaseProxy* proxy0, btBroadphaseProxy* proxy1,btDispatcher* dispatcher) { - gRemovePairs++; - if(proxy0->m_uniqueId>proxy1->m_uniqueId) + if(proxy0->m_uniqueId>proxy1->m_uniqueId) btSwap(proxy0,proxy1); int proxyId1 = proxy0->getUid(); int proxyId2 = proxy1->getUid(); - /*if (proxyId1 > proxyId2) + /*if (proxyId1 > proxyId2) btSwap(proxyId1, proxyId2);*/ int hash = static_cast<int>(getHash(static_cast<unsigned int>(proxyId1),static_cast<unsigned int>(proxyId2)) & (m_overlappingPairArray.capacity()-1)); @@ -386,8 +379,6 @@ void btHashedOverlappingPairCache::processAllOverlappingPairs(btOverlapCallback* if (callback->processOverlap(*pair)) { removeOverlappingPair(pair->m_pProxy0,pair->m_pProxy1,dispatcher); - - gOverlappingPairs--; } else { i++; @@ -395,6 +386,70 @@ void btHashedOverlappingPairCache::processAllOverlappingPairs(btOverlapCallback* } } +struct MyPairIndex +{ + int m_orgIndex; + int m_uidA0; + int m_uidA1; +}; + +class MyPairIndeSortPredicate +{ +public: + + bool operator() ( const MyPairIndex& a, const MyPairIndex& b ) const + { + const int uidA0 = a.m_uidA0; + const int uidB0 = b.m_uidA0; + const int uidA1 = a.m_uidA1; + const int uidB1 = b.m_uidA1; + return uidA0 > uidB0 || (uidA0 == uidB0 && uidA1 > uidB1); + } +}; + +void btHashedOverlappingPairCache::processAllOverlappingPairs(btOverlapCallback* callback,btDispatcher* dispatcher, const struct btDispatcherInfo& dispatchInfo) +{ + if (dispatchInfo.m_deterministicOverlappingPairs) + { + btBroadphasePairArray& pa = getOverlappingPairArray(); + btAlignedObjectArray<MyPairIndex> indices; + { + BT_PROFILE("sortOverlappingPairs"); + indices.resize(pa.size()); + for (int i=0;i<indices.size();i++) + { + const btBroadphasePair& p = pa[i]; + const int uidA0 = p.m_pProxy0 ? p.m_pProxy0->m_uniqueId : -1; + const int uidA1 = p.m_pProxy1 ? p.m_pProxy1->m_uniqueId : -1; + + indices[i].m_uidA0 = uidA0; + indices[i].m_uidA1 = uidA1; + indices[i].m_orgIndex = i; + } + indices.quickSort(MyPairIndeSortPredicate()); + } + { + BT_PROFILE("btHashedOverlappingPairCache::processAllOverlappingPairs"); + int i; + for (i=0;i<indices.size();) + { + btBroadphasePair* pair = &pa[indices[i].m_orgIndex]; + if (callback->processOverlap(*pair)) + { + removeOverlappingPair(pair->m_pProxy0,pair->m_pProxy1,dispatcher); + } else + { + i++; + } + } + } + } else + { + processAllOverlappingPairs(callback, dispatcher); + } +} + + void btHashedOverlappingPairCache::sortOverlappingPairs(btDispatcher* dispatcher) { ///need to keep hashmap in sync with pair address, so rebuild all @@ -435,7 +490,6 @@ void* btSortedOverlappingPairCache::removeOverlappingPair(btBroadphaseProxy* pro int findIndex = m_overlappingPairArray.findLinearSearch(findPair); if (findIndex < m_overlappingPairArray.size()) { - gOverlappingPairs--; btBroadphasePair& pair = m_overlappingPairArray[findIndex]; void* userData = pair.m_internalInfo1; cleanOverlappingPair(pair,dispatcher); @@ -468,11 +522,8 @@ btBroadphasePair* btSortedOverlappingPairCache::addOverlappingPair(btBroadphaseP void* mem = &m_overlappingPairArray.expandNonInitializing(); btBroadphasePair* pair = new (mem) btBroadphasePair(*proxy0,*proxy1); - - gOverlappingPairs++; - gAddedPairs++; - - if (m_ghostPairCallback) + + if (m_ghostPairCallback) m_ghostPairCallback->addOverlappingPair(proxy0, proxy1); return pair; @@ -526,7 +577,6 @@ void btSortedOverlappingPairCache::processAllOverlappingPairs(btOverlapCallback* pair->m_pProxy1 = 0; m_overlappingPairArray.swap(i,m_overlappingPairArray.size()-1); m_overlappingPairArray.pop_back(); - gOverlappingPairs--; } else { i++; @@ -559,7 +609,6 @@ void btSortedOverlappingPairCache::cleanOverlappingPair(btBroadphasePair& pair,b pair.m_algorithm->~btCollisionAlgorithm(); dispatcher->freeCollisionAlgorithm(pair.m_algorithm); pair.m_algorithm=0; - gRemovePairs--; } } } diff --git a/thirdparty/bullet/BulletCollision/BroadphaseCollision/btOverlappingPairCache.h b/thirdparty/bullet/BulletCollision/BroadphaseCollision/btOverlappingPairCache.h index f7be7d45b3..7a38d34f05 100644 --- a/thirdparty/bullet/BulletCollision/BroadphaseCollision/btOverlappingPairCache.h +++ b/thirdparty/bullet/BulletCollision/BroadphaseCollision/btOverlappingPairCache.h @@ -49,10 +49,6 @@ struct btOverlapFilterCallback -extern int gRemovePairs; -extern int gAddedPairs; -extern int gFindPairs; - const int BT_NULL_PAIR=0xffffffff; ///The btOverlappingPairCache provides an interface for overlapping pair management (add, remove, storage), used by the btBroadphaseInterface broadphases. @@ -78,6 +74,10 @@ public: virtual void processAllOverlappingPairs(btOverlapCallback*,btDispatcher* dispatcher) = 0; + virtual void processAllOverlappingPairs(btOverlapCallback* callback,btDispatcher* dispatcher, const struct btDispatcherInfo& dispatchInfo) + { + processAllOverlappingPairs(callback, dispatcher); + } virtual btBroadphasePair* findPair(btBroadphaseProxy* proxy0, btBroadphaseProxy* proxy1) = 0; virtual bool hasDeferredRemoval() = 0; @@ -129,8 +129,6 @@ public: // no new pair is created and the old one is returned. virtual btBroadphasePair* addOverlappingPair(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1) { - gAddedPairs++; - if (!needsBroadphaseCollision(proxy0,proxy1)) return 0; @@ -144,6 +142,8 @@ public: virtual void processAllOverlappingPairs(btOverlapCallback*,btDispatcher* dispatcher); + virtual void processAllOverlappingPairs(btOverlapCallback* callback,btDispatcher* dispatcher, const struct btDispatcherInfo& dispatchInfo); + virtual btBroadphasePair* getOverlappingPairArrayPtr() { return &m_overlappingPairArray[0]; diff --git a/thirdparty/bullet/BulletCollision/BroadphaseCollision/btSimpleBroadphase.cpp b/thirdparty/bullet/BulletCollision/BroadphaseCollision/btSimpleBroadphase.cpp index f1d5f5476e..5f89f960e8 100644 --- a/thirdparty/bullet/BulletCollision/BroadphaseCollision/btSimpleBroadphase.cpp +++ b/thirdparty/bullet/BulletCollision/BroadphaseCollision/btSimpleBroadphase.cpp @@ -24,8 +24,6 @@ subject to the following restrictions: #include <new> -extern int gOverlappingPairs; - void btSimpleBroadphase::validate() { for (int i=0;i<m_numHandles;i++) @@ -315,8 +313,7 @@ void btSimpleBroadphase::calculateOverlappingPairs(btDispatcher* dispatcher) pair.m_pProxy0 = 0; pair.m_pProxy1 = 0; m_invalidPair++; - gOverlappingPairs--; - } + } } diff --git a/thirdparty/bullet/BulletCollision/CollisionDispatch/SphereTriangleDetector.cpp b/thirdparty/bullet/BulletCollision/CollisionDispatch/SphereTriangleDetector.cpp index c81af95672..e5bac8438e 100644 --- a/thirdparty/bullet/BulletCollision/CollisionDispatch/SphereTriangleDetector.cpp +++ b/thirdparty/bullet/BulletCollision/CollisionDispatch/SphereTriangleDetector.cpp @@ -132,6 +132,7 @@ bool SphereTriangleDetector::collide(const btVector3& sphereCenter,btVector3 &po else { // Could be inside one of the contact capsules btScalar contactCapsuleRadiusSqr = radiusWithThreshold*radiusWithThreshold; + btScalar minDistSqr = contactCapsuleRadiusSqr; btVector3 nearestOnEdge; for (int i = 0; i < m_triangle->getNumEdges(); i++) { @@ -141,8 +142,9 @@ bool SphereTriangleDetector::collide(const btVector3& sphereCenter,btVector3 &po m_triangle->getEdge(i, pa, pb); btScalar distanceSqr = SegmentSqrDistance(pa, pb, sphereCenter, nearestOnEdge); - if (distanceSqr < contactCapsuleRadiusSqr) { - // Yep, we're inside a capsule + if (distanceSqr < minDistSqr) { + // Yep, we're inside a capsule, and record the capsule with smallest distance + minDistSqr = distanceSqr; hasContact = true; contactPoint = nearestOnEdge; } diff --git a/thirdparty/bullet/BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.cpp b/thirdparty/bullet/BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.cpp index 2c36277821..cabbb0bf6a 100644 --- a/thirdparty/bullet/BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.cpp +++ b/thirdparty/bullet/BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.cpp @@ -151,8 +151,8 @@ static btScalar EdgeSeparation(const btBox2dShape* poly1, const btTransform& xf1 int index = 0; btScalar minDot = BT_LARGE_FLOAT; - if( count2 > 0 ) - index = (int) normal1.minDot( vertices2, count2, minDot); + if( count2 > 0 ) + index = (int) normal1.minDot( vertices2, count2, minDot); btVector3 v1 = b2Mul(xf1, vertices1[edge1]); btVector3 v2 = b2Mul(xf2, vertices2[index]); @@ -175,8 +175,8 @@ static btScalar FindMaxSeparation(int* edgeIndex, // Find edge normal on poly1 that has the largest projection onto d. int edge = 0; btScalar maxDot; - if( count1 > 0 ) - edge = (int) dLocal1.maxDot( normals1, count1, maxDot); + if( count1 > 0 ) + edge = (int) dLocal1.maxDot( normals1, count1, maxDot); // Get the separation for the edge normal. btScalar s = EdgeSeparation(poly1, xf1, edge, poly2, xf2); diff --git a/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionDispatcher.cpp b/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionDispatcher.cpp index 5739a1ef01..f8794dec47 100644 --- a/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionDispatcher.cpp +++ b/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionDispatcher.cpp @@ -27,8 +27,6 @@ subject to the following restrictions: #include "BulletCollision/CollisionDispatch/btCollisionConfiguration.h" #include "BulletCollision/CollisionDispatch/btCollisionObjectWrapper.h" -int gNumManifold = 0; - #ifdef BT_DEBUG #include <stdio.h> #endif @@ -77,8 +75,6 @@ btCollisionDispatcher::~btCollisionDispatcher() btPersistentManifold* btCollisionDispatcher::getNewManifold(const btCollisionObject* body0,const btCollisionObject* body1) { - gNumManifold++; - //btAssert(gNumManifold < 65535); @@ -121,7 +117,6 @@ void btCollisionDispatcher::clearManifold(btPersistentManifold* manifold) void btCollisionDispatcher::releaseManifold(btPersistentManifold* manifold) { - gNumManifold--; //printf("releaseManifold: gNumManifold %d\n",gNumManifold); clearManifold(manifold); @@ -246,13 +241,17 @@ public: + void btCollisionDispatcher::dispatchAllCollisionPairs(btOverlappingPairCache* pairCache,const btDispatcherInfo& dispatchInfo,btDispatcher* dispatcher) { //m_blockedForChanges = true; btCollisionPairCallback collisionCallback(dispatchInfo,this); - pairCache->processAllOverlappingPairs(&collisionCallback,dispatcher); + { + BT_PROFILE("processAllOverlappingPairs"); + pairCache->processAllOverlappingPairs(&collisionCallback,dispatcher, dispatchInfo); + } //m_blockedForChanges = false; diff --git a/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionObject.cpp b/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionObject.cpp index b595c56bc5..05f96a14bc 100644 --- a/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionObject.cpp +++ b/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionObject.cpp @@ -16,6 +16,7 @@ subject to the following restrictions: #include "btCollisionObject.h" #include "LinearMath/btSerializer.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" btCollisionObject::btCollisionObject() : m_interpolationLinearVelocity(0.f, 0.f, 0.f), @@ -38,7 +39,7 @@ btCollisionObject::btCollisionObject() m_rollingFriction(0.0f), m_spinningFriction(0.f), m_contactDamping(.1), - m_contactStiffness(1e4), + m_contactStiffness(BT_LARGE_FLOAT), m_internalType(CO_COLLISION_OBJECT), m_userObjectPointer(0), m_userIndex2(-1), @@ -114,10 +115,18 @@ const char* btCollisionObject::serialize(void* dataBuffer, btSerializer* seriali dataOut->m_ccdSweptSphereRadius = m_ccdSweptSphereRadius; dataOut->m_ccdMotionThreshold = m_ccdMotionThreshold; dataOut->m_checkCollideWith = m_checkCollideWith; - - // Fill padding with zeros to appease msan. - memset(dataOut->m_padding, 0, sizeof(dataOut->m_padding)); - + if (m_broadphaseHandle) + { + dataOut->m_collisionFilterGroup = m_broadphaseHandle->m_collisionFilterGroup; + dataOut->m_collisionFilterMask = m_broadphaseHandle->m_collisionFilterMask; + dataOut->m_uniqueId = m_broadphaseHandle->m_uniqueId; + } + else + { + dataOut->m_collisionFilterGroup = 0; + dataOut->m_collisionFilterMask = 0; + dataOut->m_uniqueId = -1; + } return btCollisionObjectDataName; } diff --git a/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionObject.h b/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionObject.h index fec831bffc..135f8a033c 100644 --- a/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionObject.h +++ b/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionObject.h @@ -621,7 +621,6 @@ struct btCollisionObjectDoubleData double m_hitFraction; double m_ccdSweptSphereRadius; double m_ccdMotionThreshold; - int m_hasAnisotropicFriction; int m_collisionFlags; int m_islandTag1; @@ -629,8 +628,9 @@ struct btCollisionObjectDoubleData int m_activationState1; int m_internalType; int m_checkCollideWith; - - char m_padding[4]; + int m_collisionFilterGroup; + int m_collisionFilterMask; + int m_uniqueId;//m_uniqueId is introduced for paircache. could get rid of this, by calculating the address offset etc. }; ///do not change those serialization structures, it requires an updated sBulletDNAstr/sBulletDNAstr64 @@ -650,13 +650,12 @@ struct btCollisionObjectFloatData float m_deactivationTime; float m_friction; float m_rollingFriction; - float m_contactDamping; + float m_contactDamping; float m_contactStiffness; float m_restitution; float m_hitFraction; float m_ccdSweptSphereRadius; float m_ccdMotionThreshold; - int m_hasAnisotropicFriction; int m_collisionFlags; int m_islandTag1; @@ -664,7 +663,9 @@ struct btCollisionObjectFloatData int m_activationState1; int m_internalType; int m_checkCollideWith; - char m_padding[4]; + int m_collisionFilterGroup; + int m_collisionFilterMask; + int m_uniqueId; }; diff --git a/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionWorld.cpp b/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionWorld.cpp index c3e912fdca..3de8d6995e 100644 --- a/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionWorld.cpp +++ b/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionWorld.cpp @@ -1646,7 +1646,7 @@ void btCollisionWorld::serializeCollisionObjects(btSerializer* serializer) for (i=0;i<m_collisionObjects.size();i++) { btCollisionObject* colObj = m_collisionObjects[i]; - if ((colObj->getInternalType() == btCollisionObject::CO_COLLISION_OBJECT) || (colObj->getInternalType() == btCollisionObject::CO_FEATHERSTONE_LINK)) + if (colObj->getInternalType() == btCollisionObject::CO_COLLISION_OBJECT) { colObj->serializeSingleObject(serializer); } @@ -1654,12 +1654,36 @@ void btCollisionWorld::serializeCollisionObjects(btSerializer* serializer) } + +void btCollisionWorld::serializeContactManifolds(btSerializer* serializer) +{ + if (serializer->getSerializationFlags() & BT_SERIALIZE_CONTACT_MANIFOLDS) + { + int numManifolds = getDispatcher()->getNumManifolds(); + for (int i = 0; i < numManifolds; i++) + { + const btPersistentManifold* manifold = getDispatcher()->getInternalManifoldPointer()[i]; + //don't serialize empty manifolds, they just take space + //(may have to do it anyway if it destroys determinism) + if (manifold->getNumContacts() == 0) + continue; + + btChunk* chunk = serializer->allocate(manifold->calculateSerializeBufferSize(), 1); + const char* structType = manifold->serialize(manifold, chunk->m_oldPtr, serializer); + serializer->finalizeChunk(chunk, structType, BT_CONTACTMANIFOLD_CODE, (void*)manifold); + } + } +} + + void btCollisionWorld::serialize(btSerializer* serializer) { serializer->startSerialization(); serializeCollisionObjects(serializer); + + serializeContactManifolds(serializer); serializer->finishSerialization(); } diff --git a/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionWorld.h b/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionWorld.h index eede2b28ca..886476e8ad 100644 --- a/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionWorld.h +++ b/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionWorld.h @@ -107,6 +107,9 @@ protected: void serializeCollisionObjects(btSerializer* serializer); + void serializeContactManifolds(btSerializer* serializer); + + public: //this constructor doesn't own the dispatcher and paircache/broadphase diff --git a/thirdparty/bullet/BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.cpp b/thirdparty/bullet/BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.cpp index 7f4dea1c6d..91b7809c17 100644 --- a/thirdparty/bullet/BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.cpp +++ b/thirdparty/bullet/BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.cpp @@ -156,10 +156,12 @@ public: btCollisionObjectWrapper compoundWrap(this->m_compoundColObjWrap,childShape,m_compoundColObjWrap->getCollisionObject(),newChildWorldTrans,-1,index); btCollisionAlgorithm* algo = 0; + bool allocatedAlgorithm = false; if (m_resultOut->m_closestPointDistanceThreshold > 0) { algo = m_dispatcher->findAlgorithm(&compoundWrap, m_otherObjWrap, 0, BT_CLOSEST_POINT_ALGORITHMS); + allocatedAlgorithm = true; } else { @@ -204,7 +206,11 @@ public: { m_resultOut->setBody1Wrap(tmpWrap); } - + if(allocatedAlgorithm) + { + algo->~btCollisionAlgorithm(); + m_dispatcher->freeCollisionAlgorithm(algo); + } } } void Process(const btDbvtNode* leaf) @@ -253,9 +259,9 @@ void btCompoundCollisionAlgorithm::processCollision (const btCollisionObjectWrap m_compoundShapeRevision = compoundShape->getUpdateRevision(); } - if (m_childCollisionAlgorithms.size()==0) - return; - + if (m_childCollisionAlgorithms.size()==0) + return; + const btDbvt* tree = compoundShape->getDynamicAabbTree(); //use a dynamic aabb tree to cull potential child-overlaps btCompoundLeafCallback callback(colObjWrap,otherObjWrap,m_dispatcher,dispatchInfo,resultOut,&m_childCollisionAlgorithms[0],m_sharedManifold); diff --git a/thirdparty/bullet/BulletCollision/CollisionDispatch/btCompoundCompoundCollisionAlgorithm.cpp b/thirdparty/bullet/BulletCollision/CollisionDispatch/btCompoundCompoundCollisionAlgorithm.cpp index d4a1aa78e4..20b542f670 100644 --- a/thirdparty/bullet/BulletCollision/CollisionDispatch/btCompoundCompoundCollisionAlgorithm.cpp +++ b/thirdparty/bullet/BulletCollision/CollisionDispatch/btCompoundCompoundCollisionAlgorithm.cpp @@ -181,11 +181,12 @@ struct btCompoundCompoundLeafCallback : btDbvt::ICollide btSimplePair* pair = m_childCollisionAlgorithmCache->findPair(childIndex0,childIndex1); - + bool removePair = false; btCollisionAlgorithm* colAlgo = 0; if (m_resultOut->m_closestPointDistanceThreshold > 0) { colAlgo = m_dispatcher->findAlgorithm(&compoundWrap0, &compoundWrap1, 0, BT_CLOSEST_POINT_ALGORITHMS); + removePair = true; } else { @@ -223,7 +224,11 @@ struct btCompoundCompoundLeafCallback : btDbvt::ICollide m_resultOut->setBody0Wrap(tmpWrap0); m_resultOut->setBody1Wrap(tmpWrap1); - + if (removePair) + { + colAlgo->~btCollisionAlgorithm(); + m_dispatcher->freeCollisionAlgorithm(colAlgo); + } } } @@ -396,32 +401,24 @@ void btCompoundCompoundCollisionAlgorithm::processCollision (const btCollisionOb btCollisionAlgorithm* algo = (btCollisionAlgorithm*)pairs[i].m_userPointer; { - btTransform orgTrans0; const btCollisionShape* childShape0 = 0; btTransform newChildWorldTrans0; - btTransform orgInterpolationTrans0; childShape0 = compoundShape0->getChildShape(pairs[i].m_indexA); - orgTrans0 = col0ObjWrap->getWorldTransform(); - orgInterpolationTrans0 = col0ObjWrap->getWorldTransform(); const btTransform& childTrans0 = compoundShape0->getChildTransform(pairs[i].m_indexA); - newChildWorldTrans0 = orgTrans0*childTrans0 ; + newChildWorldTrans0 = col0ObjWrap->getWorldTransform()*childTrans0 ; childShape0->getAabb(newChildWorldTrans0,aabbMin0,aabbMax0); } btVector3 thresholdVec(resultOut->m_closestPointDistanceThreshold, resultOut->m_closestPointDistanceThreshold, resultOut->m_closestPointDistanceThreshold); aabbMin0 -= thresholdVec; aabbMax0 += thresholdVec; { - btTransform orgInterpolationTrans1; const btCollisionShape* childShape1 = 0; - btTransform orgTrans1; btTransform newChildWorldTrans1; childShape1 = compoundShape1->getChildShape(pairs[i].m_indexB); - orgTrans1 = col1ObjWrap->getWorldTransform(); - orgInterpolationTrans1 = col1ObjWrap->getWorldTransform(); const btTransform& childTrans1 = compoundShape1->getChildTransform(pairs[i].m_indexB); - newChildWorldTrans1 = orgTrans1*childTrans1 ; + newChildWorldTrans1 = col1ObjWrap->getWorldTransform()*childTrans1 ; childShape1->getAabb(newChildWorldTrans1,aabbMin1,aabbMax1); } diff --git a/thirdparty/bullet/BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.cpp b/thirdparty/bullet/BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.cpp index 39ff7934d9..d8cbe96142 100644 --- a/thirdparty/bullet/BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.cpp +++ b/thirdparty/bullet/BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.cpp @@ -27,6 +27,7 @@ subject to the following restrictions: #include "LinearMath/btIDebugDraw.h" #include "BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.h" #include "BulletCollision/CollisionDispatch/btCollisionObjectWrapper.h" +#include "BulletCollision/CollisionShapes/btSdfCollisionShape.h" btConvexConcaveCollisionAlgorithm::btConvexConcaveCollisionAlgorithm( const btCollisionAlgorithmConstructionInfo& ci, const btCollisionObjectWrapper* body0Wrap,const btCollisionObjectWrapper* body1Wrap,bool isSwapped) : btActivatingCollisionAlgorithm(ci,body0Wrap,body1Wrap), @@ -152,7 +153,7 @@ partId, int triangleIndex) { m_resultOut->setBody1Wrap(tmpWrap); } - + colAlgo->~btCollisionAlgorithm(); @@ -163,7 +164,7 @@ partId, int triangleIndex) -void btConvexTriangleCallback::setTimeStepAndCounters(btScalar collisionMarginTriangle,const btDispatcherInfo& dispatchInfo,const btCollisionObjectWrapper* convexBodyWrap, const btCollisionObjectWrapper* triBodyWrap, btManifoldResult* resultOut) +void btConvexTriangleCallback::setTimeStepAndCounters(btScalar collisionMarginTriangle, const btDispatcherInfo& dispatchInfo, const btCollisionObjectWrapper* convexBodyWrap, const btCollisionObjectWrapper* triBodyWrap, btManifoldResult* resultOut) { m_convexBodyWrap = convexBodyWrap; m_triBodyWrap = triBodyWrap; @@ -177,14 +178,14 @@ void btConvexTriangleCallback::setTimeStepAndCounters(btScalar collisionMarginTr convexInTriangleSpace = m_triBodyWrap->getWorldTransform().inverse() * m_convexBodyWrap->getWorldTransform(); const btCollisionShape* convexShape = static_cast<const btCollisionShape*>(m_convexBodyWrap->getCollisionShape()); //CollisionShape* triangleShape = static_cast<btCollisionShape*>(triBody->m_collisionShape); - convexShape->getAabb(convexInTriangleSpace,m_aabbMin,m_aabbMax); - btScalar extraMargin = collisionMarginTriangle+ resultOut->m_closestPointDistanceThreshold; - - btVector3 extra(extraMargin,extraMargin,extraMargin); + convexShape->getAabb(convexInTriangleSpace, m_aabbMin, m_aabbMax); + btScalar extraMargin = collisionMarginTriangle + resultOut->m_closestPointDistanceThreshold; + + btVector3 extra(extraMargin, extraMargin, extraMargin); m_aabbMax += extra; m_aabbMin -= extra; - + } void btConvexConcaveCollisionAlgorithm::clearCache() @@ -193,35 +194,99 @@ void btConvexConcaveCollisionAlgorithm::clearCache() } -void btConvexConcaveCollisionAlgorithm::processCollision (const btCollisionObjectWrapper* body0Wrap,const btCollisionObjectWrapper* body1Wrap,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +void btConvexConcaveCollisionAlgorithm::processCollision (const btCollisionObjectWrapper* body0Wrap, const btCollisionObjectWrapper* body1Wrap, const btDispatcherInfo& dispatchInfo, btManifoldResult* resultOut) { BT_PROFILE("btConvexConcaveCollisionAlgorithm::processCollision"); - + const btCollisionObjectWrapper* convexBodyWrap = m_isSwapped ? body1Wrap : body0Wrap; const btCollisionObjectWrapper* triBodyWrap = m_isSwapped ? body0Wrap : body1Wrap; if (triBodyWrap->getCollisionShape()->isConcave()) { + if (triBodyWrap->getCollisionShape()->getShapeType() == SDF_SHAPE_PROXYTYPE) + { + btSdfCollisionShape* sdfShape = (btSdfCollisionShape*)triBodyWrap->getCollisionShape(); + if (convexBodyWrap->getCollisionShape()->isConvex()) + { + btConvexShape* convex = (btConvexShape*)convexBodyWrap->getCollisionShape(); + btAlignedObjectArray<btVector3> queryVertices; + + if (convex->isPolyhedral()) + { + btPolyhedralConvexShape* poly = (btPolyhedralConvexShape*)convex; + for (int v = 0; v < poly->getNumVertices(); v++) + { + btVector3 vtx; + poly->getVertex(v, vtx); + queryVertices.push_back(vtx); + } + } + btScalar maxDist = SIMD_EPSILON; + + if (convex->getShapeType() == SPHERE_SHAPE_PROXYTYPE) + { + queryVertices.push_back(btVector3(0, 0, 0)); + btSphereShape* sphere = (btSphereShape*)convex; + maxDist = sphere->getRadius() + SIMD_EPSILON; + + } + if (queryVertices.size()) + { + resultOut->setPersistentManifold(m_btConvexTriangleCallback.m_manifoldPtr); + //m_btConvexTriangleCallback.m_manifoldPtr->clearManifold(); + + btPolyhedralConvexShape* poly = (btPolyhedralConvexShape*)convex; + for (int v = 0; v < queryVertices.size(); v++) + { + const btVector3& vtx = queryVertices[v]; + btVector3 vtxWorldSpace = convexBodyWrap->getWorldTransform()*vtx; + btVector3 vtxInSdf = triBodyWrap->getWorldTransform().invXform(vtxWorldSpace); + + btVector3 normalLocal; + btScalar dist; + if (sdfShape->queryPoint(vtxInSdf, dist, normalLocal)) + { + if (dist <= maxDist) + { + normalLocal.safeNormalize(); + btVector3 normal = triBodyWrap->getWorldTransform().getBasis()*normalLocal; + + if (convex->getShapeType() == SPHERE_SHAPE_PROXYTYPE) + { + btSphereShape* sphere = (btSphereShape*)convex; + dist -= sphere->getRadius(); + vtxWorldSpace -= sphere->getRadius()*normal; + + } + resultOut->addContactPoint(normal,vtxWorldSpace-normal*dist, dist); + } + } + } + resultOut->refreshContactPoints(); + } - - const btConcaveShape* concaveShape = static_cast<const btConcaveShape*>( triBodyWrap->getCollisionShape()); - - if (convexBodyWrap->getCollisionShape()->isConvex()) + } + } else { - btScalar collisionMarginTriangle = concaveShape->getMargin(); + const btConcaveShape* concaveShape = static_cast<const btConcaveShape*>( triBodyWrap->getCollisionShape()); + + if (convexBodyWrap->getCollisionShape()->isConvex()) + { + btScalar collisionMarginTriangle = concaveShape->getMargin(); - resultOut->setPersistentManifold(m_btConvexTriangleCallback.m_manifoldPtr); - m_btConvexTriangleCallback.setTimeStepAndCounters(collisionMarginTriangle,dispatchInfo,convexBodyWrap,triBodyWrap,resultOut); + resultOut->setPersistentManifold(m_btConvexTriangleCallback.m_manifoldPtr); + m_btConvexTriangleCallback.setTimeStepAndCounters(collisionMarginTriangle,dispatchInfo,convexBodyWrap,triBodyWrap,resultOut); - m_btConvexTriangleCallback.m_manifoldPtr->setBodies(convexBodyWrap->getCollisionObject(),triBodyWrap->getCollisionObject()); + m_btConvexTriangleCallback.m_manifoldPtr->setBodies(convexBodyWrap->getCollisionObject(),triBodyWrap->getCollisionObject()); - concaveShape->processAllTriangles( &m_btConvexTriangleCallback,m_btConvexTriangleCallback.getAabbMin(),m_btConvexTriangleCallback.getAabbMax()); + concaveShape->processAllTriangles( &m_btConvexTriangleCallback,m_btConvexTriangleCallback.getAabbMin(),m_btConvexTriangleCallback.getAabbMax()); - resultOut->refreshContactPoints(); + resultOut->refreshContactPoints(); - m_btConvexTriangleCallback.clearWrapperData(); + m_btConvexTriangleCallback.clearWrapperData(); + } } } diff --git a/thirdparty/bullet/BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.cpp b/thirdparty/bullet/BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.cpp index b54bd48932..3e8bc6e426 100644 --- a/thirdparty/bullet/BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.cpp +++ b/thirdparty/bullet/BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.cpp @@ -28,6 +28,7 @@ subject to the following restrictions: #include "BulletCollision/CollisionShapes/btConvexShape.h" #include "BulletCollision/CollisionShapes/btCapsuleShape.h" #include "BulletCollision/CollisionShapes/btTriangleShape.h" +#include "BulletCollision/CollisionShapes/btConvexPolyhedron.h" @@ -259,7 +260,7 @@ struct btPerturbedContactResult : public btManifoldResult btVector3 endPtOrg = pointInWorld + normalOnBInWorld*orgDepth; endPt = (m_unPerturbedTransform*m_transformA.inverse())(endPtOrg); newDepth = (endPt - pointInWorld).dot(normalOnBInWorld); - startPt = endPt+normalOnBInWorld*newDepth; + startPt = endPt - normalOnBInWorld*newDepth; } else { endPt = pointInWorld + normalOnBInWorld*orgDepth; @@ -442,10 +443,26 @@ void btConvexConvexAlgorithm ::processCollision (const btCollisionObjectWrapper* struct btDummyResult : public btDiscreteCollisionDetectorInterface::Result { + btVector3 m_normalOnBInWorld; + btVector3 m_pointInWorld; + btScalar m_depth; + bool m_hasContact; + + + btDummyResult() + : m_hasContact(false) + { + } + + virtual void setShapeIdentifiersA(int partId0,int index0){} virtual void setShapeIdentifiersB(int partId1,int index1){} virtual void addContactPoint(const btVector3& normalOnBInWorld,const btVector3& pointInWorld,btScalar depth) { + m_hasContact = true; + m_normalOnBInWorld = normalOnBInWorld; + m_pointInWorld = pointInWorld; + m_depth = depth; } }; @@ -560,15 +577,18 @@ void btConvexConvexAlgorithm ::processCollision (const btCollisionObjectWrapper* } else { + + //we can also deal with convex versus triangle (without connectivity data) - if (polyhedronA->getConvexPolyhedron() && polyhedronB->getShapeType()==TRIANGLE_SHAPE_PROXYTYPE) + if (dispatchInfo.m_enableSatConvex && polyhedronA->getConvexPolyhedron() && polyhedronB->getShapeType()==TRIANGLE_SHAPE_PROXYTYPE) { - btVertexArray vertices; + + btVertexArray worldSpaceVertices; btTriangleShape* tri = (btTriangleShape*)polyhedronB; - vertices.push_back( body1Wrap->getWorldTransform()*tri->m_vertices1[0]); - vertices.push_back( body1Wrap->getWorldTransform()*tri->m_vertices1[1]); - vertices.push_back( body1Wrap->getWorldTransform()*tri->m_vertices1[2]); + worldSpaceVertices.push_back( body1Wrap->getWorldTransform()*tri->m_vertices1[0]); + worldSpaceVertices.push_back( body1Wrap->getWorldTransform()*tri->m_vertices1[1]); + worldSpaceVertices.push_back( body1Wrap->getWorldTransform()*tri->m_vertices1[2]); //tri->initializePolyhedralFeatures(); @@ -579,17 +599,99 @@ void btConvexConvexAlgorithm ::processCollision (const btCollisionObjectWrapper* btScalar maxDist = threshold; bool foundSepAxis = false; - if (0) + bool useSatSepNormal = true; + + if (useSatSepNormal) { - polyhedronB->initializePolyhedralFeatures(); +#if 0 + if (0) + { + //initializePolyhedralFeatures performs a convex hull computation, not needed for a single triangle + polyhedronB->initializePolyhedralFeatures(); + } else +#endif + { + + btVector3 uniqueEdges[3] = {tri->m_vertices1[1]-tri->m_vertices1[0], + tri->m_vertices1[2]-tri->m_vertices1[1], + tri->m_vertices1[0]-tri->m_vertices1[2]}; + + uniqueEdges[0].normalize(); + uniqueEdges[1].normalize(); + uniqueEdges[2].normalize(); + + btConvexPolyhedron polyhedron; + polyhedron.m_vertices.push_back(tri->m_vertices1[2]); + polyhedron.m_vertices.push_back(tri->m_vertices1[0]); + polyhedron.m_vertices.push_back(tri->m_vertices1[1]); + + + { + btFace combinedFaceA; + combinedFaceA.m_indices.push_back(0); + combinedFaceA.m_indices.push_back(1); + combinedFaceA.m_indices.push_back(2); + btVector3 faceNormal = uniqueEdges[0].cross(uniqueEdges[1]); + faceNormal.normalize(); + btScalar planeEq=1e30f; + for (int v=0;v<combinedFaceA.m_indices.size();v++) + { + btScalar eq = tri->m_vertices1[combinedFaceA.m_indices[v]].dot(faceNormal); + if (planeEq>eq) + { + planeEq=eq; + } + } + combinedFaceA.m_plane[0] = faceNormal[0]; + combinedFaceA.m_plane[1] = faceNormal[1]; + combinedFaceA.m_plane[2] = faceNormal[2]; + combinedFaceA.m_plane[3] = -planeEq; + polyhedron.m_faces.push_back(combinedFaceA); + } + { + btFace combinedFaceB; + combinedFaceB.m_indices.push_back(0); + combinedFaceB.m_indices.push_back(2); + combinedFaceB.m_indices.push_back(1); + btVector3 faceNormal = -uniqueEdges[0].cross(uniqueEdges[1]); + faceNormal.normalize(); + btScalar planeEq=1e30f; + for (int v=0;v<combinedFaceB.m_indices.size();v++) + { + btScalar eq = tri->m_vertices1[combinedFaceB.m_indices[v]].dot(faceNormal); + if (planeEq>eq) + { + planeEq=eq; + } + } + + combinedFaceB.m_plane[0] = faceNormal[0]; + combinedFaceB.m_plane[1] = faceNormal[1]; + combinedFaceB.m_plane[2] = faceNormal[2]; + combinedFaceB.m_plane[3] = -planeEq; + polyhedron.m_faces.push_back(combinedFaceB); + } + + + polyhedron.m_uniqueEdges.push_back(uniqueEdges[0]); + polyhedron.m_uniqueEdges.push_back(uniqueEdges[1]); + polyhedron.m_uniqueEdges.push_back(uniqueEdges[2]); + polyhedron.initialize2(); + + polyhedronB->setPolyhedralFeatures(polyhedron); + } + + + foundSepAxis = btPolyhedralContactClipping::findSeparatingAxis( - *polyhedronA->getConvexPolyhedron(), *polyhedronB->getConvexPolyhedron(), - body0Wrap->getWorldTransform(), - body1Wrap->getWorldTransform(), - sepNormalWorldSpace,*resultOut); + *polyhedronA->getConvexPolyhedron(), *polyhedronB->getConvexPolyhedron(), + body0Wrap->getWorldTransform(), + body1Wrap->getWorldTransform(), + sepNormalWorldSpace,*resultOut); // printf("sepNormalWorldSpace=%f,%f,%f\n",sepNormalWorldSpace.getX(),sepNormalWorldSpace.getY(),sepNormalWorldSpace.getZ()); - } else + } + else { #ifdef ZERO_MARGIN gjkPairDetector.setIgnoreMargin(true); @@ -598,6 +700,24 @@ void btConvexConvexAlgorithm ::processCollision (const btCollisionObjectWrapper* gjkPairDetector.getClosestPoints(input,dummy,dispatchInfo.m_debugDraw); #endif//ZERO_MARGIN + if (dummy.m_hasContact && dummy.m_depth<0) + { + + if (foundSepAxis) + { + if (dummy.m_normalOnBInWorld.dot(sepNormalWorldSpace)<0.99) + { + printf("?\n"); + } + } else + { + printf("!\n"); + } + sepNormalWorldSpace.setValue(0,0,1);// = dummy.m_normalOnBInWorld; + //minDist = dummy.m_depth; + foundSepAxis = true; + } +#if 0 btScalar l2 = gjkPairDetector.getCachedSeparatingAxis().length2(); if (l2>SIMD_EPSILON) { @@ -607,6 +727,7 @@ void btConvexConvexAlgorithm ::processCollision (const btCollisionObjectWrapper* minDist = gjkPairDetector.getCachedSeparatingDistance()-min0->getMargin()-min1->getMargin(); foundSepAxis = true; } +#endif } @@ -614,7 +735,7 @@ void btConvexConvexAlgorithm ::processCollision (const btCollisionObjectWrapper* { worldVertsB2.resize(0); btPolyhedralContactClipping::clipFaceAgainstHull(sepNormalWorldSpace, *polyhedronA->getConvexPolyhedron(), - body0Wrap->getWorldTransform(), vertices, worldVertsB2,minDist-threshold, maxDist, *resultOut); + body0Wrap->getWorldTransform(), worldSpaceVertices, worldVertsB2,minDist-threshold, maxDist, *resultOut); } @@ -625,6 +746,7 @@ void btConvexConvexAlgorithm ::processCollision (const btCollisionObjectWrapper* return; } + } diff --git a/thirdparty/bullet/BulletCollision/CollisionDispatch/btHashedSimplePairCache.cpp b/thirdparty/bullet/BulletCollision/CollisionDispatch/btHashedSimplePairCache.cpp index 8c8a7c3c1e..8271981b29 100644 --- a/thirdparty/bullet/BulletCollision/CollisionDispatch/btHashedSimplePairCache.cpp +++ b/thirdparty/bullet/BulletCollision/CollisionDispatch/btHashedSimplePairCache.cpp @@ -20,11 +20,12 @@ subject to the following restrictions: #include <stdio.h> +#ifdef BT_DEBUG_COLLISION_PAIRS int gOverlappingSimplePairs = 0; int gRemoveSimplePairs =0; int gAddedSimplePairs =0; int gFindSimplePairs =0; - +#endif //BT_DEBUG_COLLISION_PAIRS @@ -61,7 +62,9 @@ void btHashedSimplePairCache::removeAllPairs() btSimplePair* btHashedSimplePairCache::findPair(int indexA, int indexB) { +#ifdef BT_DEBUG_COLLISION_PAIRS gFindSimplePairs++; +#endif /*if (indexA > indexB) @@ -172,7 +175,9 @@ btSimplePair* btHashedSimplePairCache::internalAddPair(int indexA, int indexB) void* btHashedSimplePairCache::removeOverlappingPair(int indexA, int indexB) { +#ifdef BT_DEBUG_COLLISION_PAIRS gRemoveSimplePairs++; +#endif /*if (indexA > indexB) diff --git a/thirdparty/bullet/BulletCollision/CollisionDispatch/btHashedSimplePairCache.h b/thirdparty/bullet/BulletCollision/CollisionDispatch/btHashedSimplePairCache.h index 2aaf6201f3..318981cda1 100644 --- a/thirdparty/bullet/BulletCollision/CollisionDispatch/btHashedSimplePairCache.h +++ b/thirdparty/bullet/BulletCollision/CollisionDispatch/btHashedSimplePairCache.h @@ -43,12 +43,12 @@ struct btSimplePair typedef btAlignedObjectArray<btSimplePair> btSimplePairArray; - +#ifdef BT_DEBUG_COLLISION_PAIRS extern int gOverlappingSimplePairs; extern int gRemoveSimplePairs; extern int gAddedSimplePairs; extern int gFindSimplePairs; - +#endif //BT_DEBUG_COLLISION_PAIRS @@ -75,7 +75,9 @@ public: // no new pair is created and the old one is returned. virtual btSimplePair* addOverlappingPair(int indexA,int indexB) { +#ifdef BT_DEBUG_COLLISION_PAIRS gAddedSimplePairs++; +#endif return internalAddPair(indexA,indexB); } diff --git a/thirdparty/bullet/BulletCollision/CollisionDispatch/btInternalEdgeUtility.cpp b/thirdparty/bullet/BulletCollision/CollisionDispatch/btInternalEdgeUtility.cpp index 6cba442ca5..898320ee1a 100644 --- a/thirdparty/bullet/BulletCollision/CollisionDispatch/btInternalEdgeUtility.cpp +++ b/thirdparty/bullet/BulletCollision/CollisionDispatch/btInternalEdgeUtility.cpp @@ -455,11 +455,20 @@ void btAdjustInternalEdgeContacts(btManifoldPoint& cp, const btCollisionObjectWr btBvhTriangleMeshShape* trimesh = 0; if( colObj0Wrap->getCollisionObject()->getCollisionShape()->getShapeType() == SCALED_TRIANGLE_MESH_SHAPE_PROXYTYPE ) - trimesh = ((btScaledBvhTriangleMeshShape*)colObj0Wrap->getCollisionObject()->getCollisionShape())->getChildShape(); - else - trimesh = (btBvhTriangleMeshShape*)colObj0Wrap->getCollisionObject()->getCollisionShape(); - - btTriangleInfoMap* triangleInfoMapPtr = (btTriangleInfoMap*) trimesh->getTriangleInfoMap(); + { + trimesh = ((btScaledBvhTriangleMeshShape*)colObj0Wrap->getCollisionObject()->getCollisionShape())->getChildShape(); + } + else + { + if (colObj0Wrap->getCollisionObject()->getCollisionShape()->getShapeType()==TRIANGLE_MESH_SHAPE_PROXYTYPE) + { + trimesh = (btBvhTriangleMeshShape*)colObj0Wrap->getCollisionObject()->getCollisionShape(); + } + } + if (trimesh==0) + return; + + btTriangleInfoMap* triangleInfoMapPtr = (btTriangleInfoMap*) trimesh->getTriangleInfoMap(); if (!triangleInfoMapPtr) return; diff --git a/thirdparty/bullet/BulletCollision/CollisionDispatch/btSimulationIslandManager.cpp b/thirdparty/bullet/BulletCollision/CollisionDispatch/btSimulationIslandManager.cpp index 1344782257..91c76a8dac 100644 --- a/thirdparty/bullet/BulletCollision/CollisionDispatch/btSimulationIslandManager.cpp +++ b/thirdparty/bullet/BulletCollision/CollisionDispatch/btSimulationIslandManager.cpp @@ -199,6 +199,22 @@ class btPersistentManifoldSortPredicate } }; +class btPersistentManifoldSortPredicateDeterministic +{ +public: + + SIMD_FORCE_INLINE bool operator() (const btPersistentManifold* lhs, const btPersistentManifold* rhs) const + { + return ( + (getIslandId(lhs) < getIslandId(rhs)) + || ((getIslandId(lhs) == getIslandId(rhs)) && lhs->getBody0()->getBroadphaseHandle()->m_uniqueId < rhs->getBody0()->getBroadphaseHandle()->m_uniqueId) + ||((getIslandId(lhs) == getIslandId(rhs)) && (lhs->getBody0()->getBroadphaseHandle()->m_uniqueId == rhs->getBody0()->getBroadphaseHandle()->m_uniqueId) && + (lhs->getBody1()->getBroadphaseHandle()->m_uniqueId < rhs->getBody1()->getBroadphaseHandle()->m_uniqueId)) + ); + + } +}; + void btSimulationIslandManager::buildIslands(btDispatcher* dispatcher,btCollisionWorld* collisionWorld) { @@ -245,13 +261,11 @@ void btSimulationIslandManager::buildIslands(btDispatcher* dispatcher,btCollisio btAssert((colObj0->getIslandTag() == islandId) || (colObj0->getIslandTag() == -1)); if (colObj0->getIslandTag() == islandId) { - if (colObj0->getActivationState()== ACTIVE_TAG) - { - allSleeping = false; - } - if (colObj0->getActivationState()== DISABLE_DEACTIVATION) + if (colObj0->getActivationState()== ACTIVE_TAG || + colObj0->getActivationState()== DISABLE_DEACTIVATION) { allSleeping = false; + break; } } } @@ -318,7 +332,12 @@ void btSimulationIslandManager::buildIslands(btDispatcher* dispatcher,btCollisio for (i=0;i<maxNumManifolds ;i++) { btPersistentManifold* manifold = dispatcher->getManifoldByIndexInternal(i); - + if (collisionWorld->getDispatchInfo().m_deterministicOverlappingPairs) + { + if (manifold->getNumContacts() == 0) + continue; + } + const btCollisionObject* colObj0 = static_cast<const btCollisionObject*>(manifold->getBody0()); const btCollisionObject* colObj1 = static_cast<const btCollisionObject*>(manifold->getBody1()); @@ -379,7 +398,16 @@ void btSimulationIslandManager::buildAndProcessIslands(btDispatcher* dispatcher, //tried a radix sort, but quicksort/heapsort seems still faster //@todo rewrite island management - m_islandmanifold.quickSort(btPersistentManifoldSortPredicate()); + //btPersistentManifoldSortPredicateDeterministic sorts contact manifolds based on islandid, + //but also based on object0 unique id and object1 unique id + if (collisionWorld->getDispatchInfo().m_deterministicOverlappingPairs) + { + m_islandmanifold.quickSort(btPersistentManifoldSortPredicateDeterministic()); + } else + { + m_islandmanifold.quickSort(btPersistentManifoldSortPredicate()); + } + //m_islandmanifold.heapSort(btPersistentManifoldSortPredicate()); //now process all active islands (sets of manifolds for now) diff --git a/thirdparty/bullet/BulletCollision/CollisionShapes/btCapsuleShape.h b/thirdparty/bullet/BulletCollision/CollisionShapes/btCapsuleShape.h index 7d64b46abf..5a3362834a 100644 --- a/thirdparty/bullet/BulletCollision/CollisionShapes/btCapsuleShape.h +++ b/thirdparty/bullet/BulletCollision/CollisionShapes/btCapsuleShape.h @@ -49,6 +49,7 @@ public: virtual void setMargin(btScalar collisionMargin) { //don't override the margin for capsules, their entire radius == margin + (void)collisionMargin; } virtual void getAabb (const btTransform& t, btVector3& aabbMin, btVector3& aabbMax) const diff --git a/thirdparty/bullet/BulletCollision/CollisionShapes/btCompoundShape.cpp b/thirdparty/bullet/BulletCollision/CollisionShapes/btCompoundShape.cpp index e8c8c336cd..85572da307 100644 --- a/thirdparty/bullet/BulletCollision/CollisionShapes/btCompoundShape.cpp +++ b/thirdparty/bullet/BulletCollision/CollisionShapes/btCompoundShape.cpp @@ -213,7 +213,7 @@ void btCompoundShape::calculateLocalInertia(btScalar mass,btVector3& inertia) co -void btCompoundShape::calculatePrincipalAxisTransform(btScalar* masses, btTransform& principal, btVector3& inertia) const +void btCompoundShape::calculatePrincipalAxisTransform(const btScalar* masses, btTransform& principal, btVector3& inertia) const { int n = m_children.size(); diff --git a/thirdparty/bullet/BulletCollision/CollisionShapes/btCompoundShape.h b/thirdparty/bullet/BulletCollision/CollisionShapes/btCompoundShape.h index 4eef8dba30..2cbcd1bfca 100644 --- a/thirdparty/bullet/BulletCollision/CollisionShapes/btCompoundShape.h +++ b/thirdparty/bullet/BulletCollision/CollisionShapes/btCompoundShape.h @@ -160,7 +160,7 @@ public: ///"principal" has to be applied inversely to all children transforms in order for the local coordinate system of the compound ///shape to be centered at the center of mass and to coincide with the principal axes. This also necessitates a correction of the world transform ///of the collision object by the principal transform. - void calculatePrincipalAxisTransform(btScalar* masses, btTransform& principal, btVector3& inertia) const; + void calculatePrincipalAxisTransform(const btScalar* masses, btTransform& principal, btVector3& inertia) const; int getUpdateRevision() const { diff --git a/thirdparty/bullet/BulletCollision/CollisionShapes/btConvexPolyhedron.cpp b/thirdparty/bullet/BulletCollision/CollisionShapes/btConvexPolyhedron.cpp index 4f45319a83..0fea00df5c 100644 --- a/thirdparty/bullet/BulletCollision/CollisionShapes/btConvexPolyhedron.cpp +++ b/thirdparty/bullet/BulletCollision/CollisionShapes/btConvexPolyhedron.cpp @@ -104,9 +104,9 @@ void btConvexPolyhedron::initialize() btHashMap<btInternalVertexPair,btInternalEdge> edges; - btScalar TotalArea = 0.0f; - m_localCenter.setValue(0, 0, 0); + + for(int i=0;i<m_faces.size();i++) { int numVertices = m_faces[i].m_indices.size(); @@ -172,6 +172,13 @@ void btConvexPolyhedron::initialize() } #endif//USE_CONNECTED_FACES + initialize2(); +} + +void btConvexPolyhedron::initialize2() +{ + m_localCenter.setValue(0, 0, 0); + btScalar TotalArea = 0.0f; for(int i=0;i<m_faces.size();i++) { int numVertices = m_faces[i].m_indices.size(); @@ -274,7 +281,6 @@ void btConvexPolyhedron::initialize() } #endif } - void btConvexPolyhedron::project(const btTransform& trans, const btVector3& dir, btScalar& minProj, btScalar& maxProj, btVector3& witnesPtMin,btVector3& witnesPtMax) const { minProj = FLT_MAX; diff --git a/thirdparty/bullet/BulletCollision/CollisionShapes/btConvexPolyhedron.h b/thirdparty/bullet/BulletCollision/CollisionShapes/btConvexPolyhedron.h index d3cd066ac8..c5aa20f453 100644 --- a/thirdparty/bullet/BulletCollision/CollisionShapes/btConvexPolyhedron.h +++ b/thirdparty/bullet/BulletCollision/CollisionShapes/btConvexPolyhedron.h @@ -54,6 +54,7 @@ ATTRIBUTE_ALIGNED16(class) btConvexPolyhedron btVector3 mE; void initialize(); + void initialize2(); bool testContainment() const; void project(const btTransform& trans, const btVector3& dir, btScalar& minProj, btScalar& maxProj, btVector3& witnesPtMin,btVector3& witnesPtMax) const; diff --git a/thirdparty/bullet/BulletCollision/CollisionShapes/btConvexShape.cpp b/thirdparty/bullet/BulletCollision/CollisionShapes/btConvexShape.cpp index 8d7fb054d6..2f84858598 100644 --- a/thirdparty/bullet/BulletCollision/CollisionShapes/btConvexShape.cpp +++ b/thirdparty/bullet/BulletCollision/CollisionShapes/btConvexShape.cpp @@ -118,9 +118,13 @@ static btVector3 convexHullSupport (const btVector3& localDirOrg, const btVector return supVec; #else - btScalar maxDot; - long ptIndex = vec.maxDot( points, numPoints, maxDot); + btScalar maxDot; + long ptIndex = vec.maxDot( points, numPoints, maxDot); btAssert(ptIndex >= 0); + if (ptIndex<0) + { + ptIndex = 0; + } btVector3 supVec = points[ptIndex] * localScaling; return supVec; #endif //__SPU__ diff --git a/thirdparty/bullet/BulletCollision/CollisionShapes/btMiniSDF.cpp b/thirdparty/bullet/BulletCollision/CollisionShapes/btMiniSDF.cpp new file mode 100644 index 0000000000..afe45e1d2d --- /dev/null +++ b/thirdparty/bullet/BulletCollision/CollisionShapes/btMiniSDF.cpp @@ -0,0 +1,532 @@ +#include "btMiniSDF.h" + +// +//Based on code from DiscreGrid, https://github.com/InteractiveComputerGraphics/Discregrid +//example: +//GenerateSDF.exe -r "32 32 32" -d "-1.6 -1.6 -.6 1.6 1.6 .6" concave_box.obj +//The MIT License (MIT) +// +//Copyright (c) 2017 Dan Koschier +// + +#include <limits.h> +#include <string.h> //memcpy + +struct btSdfDataStream +{ + const char* m_data; + int m_size; + + int m_currentOffset; + + btSdfDataStream(const char* data, int size) + :m_data(data), + m_size(size), + m_currentOffset(0) + { + + } + + template<class T> bool read(T& val) + { + int bytes = sizeof(T); + if (m_currentOffset+bytes<=m_size) + { + char* dest = (char*)&val; + memcpy(dest,&m_data[m_currentOffset],bytes); + m_currentOffset+=bytes; + return true; + } + btAssert(0); + return false; + } +}; + + +bool btMiniSDF::load(const char* data, int size) +{ + int fileSize = -1; + + btSdfDataStream ds(data,size); + { + double buf[6]; + ds.read(buf); + m_domain.m_min[0] = buf[0]; + m_domain.m_min[1] = buf[1]; + m_domain.m_min[2] = buf[2]; + m_domain.m_min[3] = 0; + m_domain.m_max[0] = buf[3]; + m_domain.m_max[1] = buf[4]; + m_domain.m_max[2] = buf[5]; + m_domain.m_max[3] = 0; + } + { + unsigned int buf2[3]; + ds.read(buf2); + m_resolution[0] = buf2[0]; + m_resolution[1] = buf2[1]; + m_resolution[2] = buf2[2]; + } + { + double buf[3]; + ds.read(buf); + m_cell_size[0] = buf[0]; + m_cell_size[1] = buf[1]; + m_cell_size[2] = buf[2]; + } + { + double buf[3]; + ds.read(buf); + m_inv_cell_size[0] = buf[0]; + m_inv_cell_size[1] = buf[1]; + m_inv_cell_size[2] = buf[2]; + } + { + unsigned long long int cells; + ds.read(cells); + m_n_cells = cells; + } + { + unsigned long long int fields; + ds.read(fields); + m_n_fields = fields; + } + + + unsigned long long int nodes0; + std::size_t n_nodes0; + ds.read(nodes0); + n_nodes0 = nodes0; + if (n_nodes0 > 1024 * 1024 * 1024) + { + return m_isValid; + } + m_nodes.resize(n_nodes0); + for (unsigned int i=0;i<n_nodes0;i++) + { + unsigned long long int n_nodes1; + ds.read(n_nodes1); + btAlignedObjectArray<double>& nodes = m_nodes[i]; + nodes.resize(n_nodes1); + for ( int j=0;j<nodes.size();j++) + { + double& node = nodes[j]; + ds.read(node); + } + } + + unsigned long long int n_cells0; + ds.read(n_cells0); + m_cells.resize(n_cells0); + for (int i=0;i<n_cells0;i++) + { + unsigned long long int n_cells1; + btAlignedObjectArray<btCell32 >& cells = m_cells[i]; + ds.read(n_cells1); + cells.resize(n_cells1); + for (int j=0;j<n_cells1;j++) + { + btCell32& cell = cells[j]; + ds.read(cell); + } + } + + { + unsigned long long int n_cell_maps0; + ds.read(n_cell_maps0); + + m_cell_map.resize(n_cell_maps0); + for (int i=0;i<n_cell_maps0;i++) + { + unsigned long long int n_cell_maps1; + btAlignedObjectArray<unsigned int>& cell_maps = m_cell_map[i]; + ds.read(n_cell_maps1); + cell_maps.resize(n_cell_maps1); + for (int j=0;j<n_cell_maps1;j++) + { + unsigned int& cell_map = cell_maps[j]; + ds.read(cell_map); + } + } + } + + m_isValid = (ds.m_currentOffset == ds.m_size); + return m_isValid; +} + + +unsigned int btMiniSDF::multiToSingleIndex(btMultiIndex const & ijk) const +{ + return m_resolution[1] * m_resolution[0] * ijk.ijk[2] + m_resolution[0] * ijk.ijk[1] + ijk.ijk[0]; +} + +btAlignedBox3d +btMiniSDF::subdomain(btMultiIndex const& ijk) const +{ + btAssert(m_isValid); + btVector3 tmp; + tmp.m_floats[0] = m_cell_size[0]*(double)ijk.ijk[0]; + tmp.m_floats[1] = m_cell_size[1]*(double)ijk.ijk[1]; + tmp.m_floats[2] = m_cell_size[2]*(double)ijk.ijk[2]; + + + btVector3 origin = m_domain.min() + tmp; + + btAlignedBox3d box = btAlignedBox3d (origin, origin + m_cell_size); + return box; +} + +btMultiIndex +btMiniSDF::singleToMultiIndex(unsigned int l) const +{ + btAssert(m_isValid); + unsigned int n01 = m_resolution[0] * m_resolution[1]; + unsigned int k = l / n01; + unsigned int temp = l % n01; + unsigned int j = temp / m_resolution[0]; + unsigned int i = temp % m_resolution[0]; + btMultiIndex mi; + mi.ijk[0] = i; + mi.ijk[1] = j; + mi.ijk[2] = k; + return mi; +} + +btAlignedBox3d +btMiniSDF::subdomain(unsigned int l) const +{ + btAssert(m_isValid); + return subdomain(singleToMultiIndex(l)); +} + + +btShapeMatrix +btMiniSDF::shape_function_(btVector3 const& xi, btShapeGradients* gradient) const +{ + btAssert(m_isValid); + btShapeMatrix res; + + btScalar x = xi[0]; + btScalar y = xi[1]; + btScalar z = xi[2]; + + btScalar x2 = x*x; + btScalar y2 = y*y; + btScalar z2 = z*z; + + btScalar _1mx = 1.0 - x; + btScalar _1my = 1.0 - y; + btScalar _1mz = 1.0 - z; + + btScalar _1px = 1.0 + x; + btScalar _1py = 1.0 + y; + btScalar _1pz = 1.0 + z; + + btScalar _1m3x = 1.0 - 3.0 * x; + btScalar _1m3y = 1.0 - 3.0 * y; + btScalar _1m3z = 1.0 - 3.0 * z; + + btScalar _1p3x = 1.0 + 3.0 * x; + btScalar _1p3y = 1.0 + 3.0 * y; + btScalar _1p3z = 1.0 + 3.0 * z; + + btScalar _1mxt1my = _1mx * _1my; + btScalar _1mxt1py = _1mx * _1py; + btScalar _1pxt1my = _1px * _1my; + btScalar _1pxt1py = _1px * _1py; + + btScalar _1mxt1mz = _1mx * _1mz; + btScalar _1mxt1pz = _1mx * _1pz; + btScalar _1pxt1mz = _1px * _1mz; + btScalar _1pxt1pz = _1px * _1pz; + + btScalar _1myt1mz = _1my * _1mz; + btScalar _1myt1pz = _1my * _1pz; + btScalar _1pyt1mz = _1py * _1mz; + btScalar _1pyt1pz = _1py * _1pz; + + btScalar _1mx2 = 1.0 - x2; + btScalar _1my2 = 1.0 - y2; + btScalar _1mz2 = 1.0 - z2; + + + // Corner nodes. + btScalar fac = 1.0 / 64.0 * (9.0 * (x2 + y2 + z2) - 19.0); + res[0] = fac * _1mxt1my * _1mz; + res[1] = fac * _1pxt1my * _1mz; + res[2] = fac * _1mxt1py * _1mz; + res[3] = fac * _1pxt1py * _1mz; + res[4] = fac * _1mxt1my * _1pz; + res[5] = fac * _1pxt1my * _1pz; + res[6] = fac * _1mxt1py * _1pz; + res[7] = fac * _1pxt1py * _1pz; + + // Edge nodes. + + fac = 9.0 / 64.0 * _1mx2; + btScalar fact1m3x = fac * _1m3x; + btScalar fact1p3x = fac * _1p3x; + res[ 8] = fact1m3x * _1myt1mz; + res[ 9] = fact1p3x * _1myt1mz; + res[10] = fact1m3x * _1myt1pz; + res[11] = fact1p3x * _1myt1pz; + res[12] = fact1m3x * _1pyt1mz; + res[13] = fact1p3x * _1pyt1mz; + res[14] = fact1m3x * _1pyt1pz; + res[15] = fact1p3x * _1pyt1pz; + + fac = 9.0 / 64.0 * _1my2; + btScalar fact1m3y = fac * _1m3y; + btScalar fact1p3y = fac * _1p3y; + res[16] = fact1m3y * _1mxt1mz; + res[17] = fact1p3y * _1mxt1mz; + res[18] = fact1m3y * _1pxt1mz; + res[19] = fact1p3y * _1pxt1mz; + res[20] = fact1m3y * _1mxt1pz; + res[21] = fact1p3y * _1mxt1pz; + res[22] = fact1m3y * _1pxt1pz; + res[23] = fact1p3y * _1pxt1pz; + + fac = 9.0 / 64.0 * _1mz2; + btScalar fact1m3z = fac * _1m3z; + btScalar fact1p3z = fac * _1p3z; + res[24] = fact1m3z * _1mxt1my; + res[25] = fact1p3z * _1mxt1my; + res[26] = fact1m3z * _1mxt1py; + res[27] = fact1p3z * _1mxt1py; + res[28] = fact1m3z * _1pxt1my; + res[29] = fact1p3z * _1pxt1my; + res[30] = fact1m3z * _1pxt1py; + res[31] = fact1p3z * _1pxt1py; + + if (gradient) + { + btShapeGradients& dN = *gradient; + + btScalar _9t3x2py2pz2m19 = 9.0 * (3.0 * x2 + y2 + z2) - 19.0; + btScalar _9tx2p3y2pz2m19 = 9.0 * (x2 + 3.0 * y2 + z2) - 19.0; + btScalar _9tx2py2p3z2m19 = 9.0 * (x2 + y2 + 3.0 * z2) - 19.0; + btScalar _18x = 18.0 * x; + btScalar _18y = 18.0 * y; + btScalar _18z = 18.0 * z; + + btScalar _3m9x2 = 3.0 - 9.0 * x2; + btScalar _3m9y2 = 3.0 - 9.0 * y2; + btScalar _3m9z2 = 3.0 - 9.0 * z2; + + btScalar _2x = 2.0 * x; + btScalar _2y = 2.0 * y; + btScalar _2z = 2.0 * z; + + btScalar _18xm9t3x2py2pz2m19 = _18x - _9t3x2py2pz2m19; + btScalar _18xp9t3x2py2pz2m19 = _18x + _9t3x2py2pz2m19; + btScalar _18ym9tx2p3y2pz2m19 = _18y - _9tx2p3y2pz2m19; + btScalar _18yp9tx2p3y2pz2m19 = _18y + _9tx2p3y2pz2m19; + btScalar _18zm9tx2py2p3z2m19 = _18z - _9tx2py2p3z2m19; + btScalar _18zp9tx2py2p3z2m19 = _18z + _9tx2py2p3z2m19; + + dN(0,0) =_18xm9t3x2py2pz2m19 * _1myt1mz; + dN(0,1) =_1mxt1mz * _18ym9tx2p3y2pz2m19; + dN(0,2) =_1mxt1my * _18zm9tx2py2p3z2m19; + dN(1,0) =_18xp9t3x2py2pz2m19 * _1myt1mz; + dN(1,1) =_1pxt1mz * _18ym9tx2p3y2pz2m19; + dN(1,2) =_1pxt1my * _18zm9tx2py2p3z2m19; + dN(2,0) =_18xm9t3x2py2pz2m19 * _1pyt1mz; + dN(2,1) =_1mxt1mz * _18yp9tx2p3y2pz2m19; + dN(2,2) =_1mxt1py * _18zm9tx2py2p3z2m19; + dN(3,0) =_18xp9t3x2py2pz2m19 * _1pyt1mz; + dN(3,1) =_1pxt1mz * _18yp9tx2p3y2pz2m19; + dN(3,2) =_1pxt1py * _18zm9tx2py2p3z2m19; + dN(4,0) =_18xm9t3x2py2pz2m19 * _1myt1pz; + dN(4,1) =_1mxt1pz * _18ym9tx2p3y2pz2m19; + dN(4,2) =_1mxt1my * _18zp9tx2py2p3z2m19; + dN(5,0) =_18xp9t3x2py2pz2m19 * _1myt1pz; + dN(5,1) =_1pxt1pz * _18ym9tx2p3y2pz2m19; + dN(5,2) =_1pxt1my * _18zp9tx2py2p3z2m19; + dN(6,0) =_18xm9t3x2py2pz2m19 * _1pyt1pz; + dN(6,1) =_1mxt1pz * _18yp9tx2p3y2pz2m19; + dN(6,2) =_1mxt1py * _18zp9tx2py2p3z2m19; + dN(7,0) =_18xp9t3x2py2pz2m19 * _1pyt1pz; + dN(7,1) =_1pxt1pz * _18yp9tx2p3y2pz2m19; + dN(7,2) =_1pxt1py * _18zp9tx2py2p3z2m19; + + dN.topRowsDivide(8, 64.0); + + btScalar _m3m9x2m2x = -_3m9x2 - _2x; + btScalar _p3m9x2m2x = _3m9x2 - _2x; + btScalar _1mx2t1m3x = _1mx2 * _1m3x; + btScalar _1mx2t1p3x = _1mx2 * _1p3x; + dN( 8,0) = _m3m9x2m2x * _1myt1mz, + dN( 8,1) = -_1mx2t1m3x * _1mz, + dN( 8,2) = -_1mx2t1m3x * _1my; + dN( 9,0) = _p3m9x2m2x * _1myt1mz, + dN( 9,1) = -_1mx2t1p3x * _1mz, + dN( 9,2) = -_1mx2t1p3x * _1my; + dN(10,0) = _m3m9x2m2x * _1myt1pz, + dN(10,1) = -_1mx2t1m3x * _1pz, + dN(10,2) = _1mx2t1m3x * _1my; + dN(11,0) = _p3m9x2m2x * _1myt1pz, + dN(11,1) = -_1mx2t1p3x * _1pz, + dN(11,2) = _1mx2t1p3x * _1my; + dN(12,0) = _m3m9x2m2x * _1pyt1mz, + dN(12,1) = _1mx2t1m3x * _1mz, + dN(12,2) = -_1mx2t1m3x * _1py; + dN(13,0) = _p3m9x2m2x * _1pyt1mz, + dN(13,1) = _1mx2t1p3x * _1mz, + dN(13,2) = -_1mx2t1p3x * _1py; + dN(14,0) = _m3m9x2m2x * _1pyt1pz, + dN(14,1) = _1mx2t1m3x * _1pz, + dN(14,2) = _1mx2t1m3x * _1py; + dN(15,0) = _p3m9x2m2x * _1pyt1pz, + dN(15,1) = _1mx2t1p3x * _1pz, + dN(15,2) = _1mx2t1p3x * _1py; + + btScalar _m3m9y2m2y = -_3m9y2 - _2y; + btScalar _p3m9y2m2y = _3m9y2 - _2y; + btScalar _1my2t1m3y = _1my2 * _1m3y; + btScalar _1my2t1p3y = _1my2 * _1p3y; + dN(16,0) = -_1my2t1m3y * _1mz, + dN(16,1) = _m3m9y2m2y * _1mxt1mz, + dN(16,2) = -_1my2t1m3y * _1mx; + dN(17,0) = -_1my2t1p3y * _1mz, + dN(17,1) = _p3m9y2m2y * _1mxt1mz, + dN(17,2) = -_1my2t1p3y * _1mx; + dN(18,0) = _1my2t1m3y * _1mz, + dN(18,1) = _m3m9y2m2y * _1pxt1mz, + dN(18,2) = -_1my2t1m3y * _1px; + dN(19,0) = _1my2t1p3y * _1mz, + dN(19,1) = _p3m9y2m2y * _1pxt1mz, + dN(19,2) = -_1my2t1p3y * _1px; + dN(20,0) = -_1my2t1m3y * _1pz, + dN(20,1) = _m3m9y2m2y * _1mxt1pz, + dN(20,2) = _1my2t1m3y * _1mx; + dN(21,0) = -_1my2t1p3y * _1pz, + dN(21,1) = _p3m9y2m2y * _1mxt1pz, + dN(21,2) = _1my2t1p3y * _1mx; + dN(22,0) = _1my2t1m3y * _1pz, + dN(22,1) = _m3m9y2m2y * _1pxt1pz, + dN(22,2) = _1my2t1m3y * _1px; + dN(23,0) = _1my2t1p3y * _1pz, + dN(23,1) = _p3m9y2m2y * _1pxt1pz, + dN(23,2) = _1my2t1p3y * _1px; + + + btScalar _m3m9z2m2z = -_3m9z2 - _2z; + btScalar _p3m9z2m2z = _3m9z2 - _2z; + btScalar _1mz2t1m3z = _1mz2 * _1m3z; + btScalar _1mz2t1p3z = _1mz2 * _1p3z; + dN(24,0) = -_1mz2t1m3z * _1my, + dN(24,1) = -_1mz2t1m3z * _1mx, + dN(24,2) = _m3m9z2m2z * _1mxt1my; + dN(25,0) = -_1mz2t1p3z * _1my, + dN(25,1) = -_1mz2t1p3z * _1mx, + dN(25,2) = _p3m9z2m2z * _1mxt1my; + dN(26,0) = -_1mz2t1m3z * _1py, + dN(26,1) = _1mz2t1m3z * _1mx, + dN(26,2) = _m3m9z2m2z * _1mxt1py; + dN(27,0) = -_1mz2t1p3z * _1py, + dN(27,1) = _1mz2t1p3z * _1mx, + dN(27,2) = _p3m9z2m2z * _1mxt1py; + dN(28,0) = _1mz2t1m3z * _1my, + dN(28,1) = -_1mz2t1m3z * _1px, + dN(28,2) = _m3m9z2m2z * _1pxt1my; + dN(29,0) = _1mz2t1p3z * _1my, + dN(29,1) = -_1mz2t1p3z * _1px, + dN(29,2) = _p3m9z2m2z * _1pxt1my; + dN(30,0) = _1mz2t1m3z * _1py, + dN(30,1) = _1mz2t1m3z * _1px, + dN(30,2) = _m3m9z2m2z * _1pxt1py; + dN(31,0) = _1mz2t1p3z * _1py, + dN(31,1) = _1mz2t1p3z * _1px, + dN(31,2) = _p3m9z2m2z * _1pxt1py; + + dN.bottomRowsMul(32u - 8u, 9.0 / 64.0); + + } + + return res; +} + + + +bool btMiniSDF::interpolate(unsigned int field_id, double& dist, btVector3 const& x, + btVector3* gradient) const +{ + btAssert(m_isValid); + if (!m_isValid) + return false; + + if (!m_domain.contains(x)) + return false; + + btVector3 tmpmi = ((x - m_domain.min())*(m_inv_cell_size));//.cast<unsigned int>().eval(); + unsigned int mi[3] = {(unsigned int )tmpmi[0],(unsigned int )tmpmi[1],(unsigned int )tmpmi[2]}; + if (mi[0] >= m_resolution[0]) + mi[0] = m_resolution[0]-1; + if (mi[1] >= m_resolution[1]) + mi[1] = m_resolution[1]-1; + if (mi[2] >= m_resolution[2]) + mi[2] = m_resolution[2]-1; + btMultiIndex mui; + mui.ijk[0] = mi[0]; + mui.ijk[1] = mi[1]; + mui.ijk[2] = mi[2]; + int i = multiToSingleIndex(mui); + unsigned int i_ = m_cell_map[field_id][i]; + if (i_ == UINT_MAX) + return false; + + btAlignedBox3d sd = subdomain(i); + i = i_; + btVector3 d = sd.m_max-sd.m_min;//.diagonal().eval(); + + btVector3 denom = (sd.max() - sd.min()); + btVector3 c0 = btVector3(2.0,2.0,2.0)/denom; + btVector3 c1 = (sd.max() + sd.min())/denom; + btVector3 xi = (c0*x - c1); + + btCell32 const& cell = m_cells[field_id][i]; + if (!gradient) + { + //auto phi = m_coefficients[field_id][i].dot(shape_function_(xi, 0)); + double phi = 0.0; + btShapeMatrix N = shape_function_(xi, 0); + for (unsigned int j = 0u; j < 32u; ++j) + { + unsigned int v = cell.m_cells[j]; + double c = m_nodes[field_id][v]; + if (c == DBL_MAX) + { + return false;; + } + phi += c * N[j]; + } + + dist = phi; + return true; + } + + btShapeGradients dN; + btShapeMatrix N = shape_function_(xi, &dN); + + double phi = 0.0; + gradient->setZero(); + for (unsigned int j = 0u; j < 32u; ++j) + { + unsigned int v = cell.m_cells[j]; + double c = m_nodes[field_id][v]; + if (c == DBL_MAX) + { + gradient->setZero(); + return false; + } + phi += c * N[j]; + (*gradient)[0] += c * dN(j, 0); + (*gradient)[1] += c * dN(j, 1); + (*gradient)[2] += c * dN(j, 2); + } + (*gradient) *= c0; + dist = phi; + return true; +} + diff --git a/thirdparty/bullet/BulletCollision/CollisionShapes/btMiniSDF.h b/thirdparty/bullet/BulletCollision/CollisionShapes/btMiniSDF.h new file mode 100644 index 0000000000..3de90e4f8a --- /dev/null +++ b/thirdparty/bullet/BulletCollision/CollisionShapes/btMiniSDF.h @@ -0,0 +1,134 @@ +#ifndef MINISDF_H +#define MINISDF_H + +#include "LinearMath/btVector3.h" +#include "LinearMath/btAabbUtil2.h" +#include "LinearMath/btAlignedObjectArray.h" + + +struct btMultiIndex +{ + unsigned int ijk[3]; +}; + +struct btAlignedBox3d +{ + btVector3 m_min; + btVector3 m_max; + + const btVector3& min() const + { + return m_min; + } + + const btVector3& max() const + { + return m_max; + } + + + bool contains(const btVector3& x) const + { + return TestPointAgainstAabb2(m_min, m_max, x); + } + + btAlignedBox3d(const btVector3& mn, const btVector3& mx) + :m_min(mn), + m_max(mx) + { + } + + btAlignedBox3d() + { + } +}; + +struct btShapeMatrix +{ + double m_vec[32]; + + inline double& operator[](int i) + { + return m_vec[i]; + } + + inline const double& operator[](int i) const + { + return m_vec[i]; + } + +}; + +struct btShapeGradients +{ + btVector3 m_vec[32]; + + void topRowsDivide(int row, double denom) + { + for (int i=0;i<row;i++) + { + m_vec[i] /= denom; + } + } + + void bottomRowsMul(int row, double val) + { + for (int i=32-row;i<32;i++) + { + m_vec[i] *= val; + } + } + + inline btScalar& operator()(int i, int j) + { + return m_vec[i][j]; + } +}; + +struct btCell32 +{ + unsigned int m_cells[32]; +}; + +struct btMiniSDF +{ + + btAlignedBox3d m_domain; + unsigned int m_resolution[3]; + btVector3 m_cell_size; + btVector3 m_inv_cell_size; + std::size_t m_n_cells; + std::size_t m_n_fields; + bool m_isValid; + + + btAlignedObjectArray<btAlignedObjectArray<double> > m_nodes; + btAlignedObjectArray<btAlignedObjectArray<btCell32 > > m_cells; + btAlignedObjectArray<btAlignedObjectArray<unsigned int> > m_cell_map; + + btMiniSDF() + :m_isValid(false) + { + } + bool load(const char* data, int size); + bool isValid() const + { + return m_isValid; + } + unsigned int multiToSingleIndex(btMultiIndex const & ijk) const; + + btAlignedBox3d subdomain(btMultiIndex const& ijk) const; + + btMultiIndex singleToMultiIndex(unsigned int l) const; + + btAlignedBox3d subdomain(unsigned int l) const; + + + btShapeMatrix + shape_function_(btVector3 const& xi, btShapeGradients* gradient = 0) const; + + bool interpolate(unsigned int field_id, double& dist, btVector3 const& x, btVector3* gradient) const; +}; + + +#endif //MINISDF_H diff --git a/thirdparty/bullet/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp b/thirdparty/bullet/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp index 4854f370f7..d51b6760fc 100644 --- a/thirdparty/bullet/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp +++ b/thirdparty/bullet/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp @@ -39,6 +39,17 @@ btPolyhedralConvexShape::~btPolyhedralConvexShape() } } +void btPolyhedralConvexShape::setPolyhedralFeatures(btConvexPolyhedron& polyhedron) +{ + if (m_polyhedron) + { + *m_polyhedron = polyhedron; + } else + { + void* mem = btAlignedAlloc(sizeof(btConvexPolyhedron),16); + m_polyhedron = new (mem) btConvexPolyhedron(polyhedron); + } +} bool btPolyhedralConvexShape::initializePolyhedralFeatures(int shiftVerticesByMargin) { @@ -87,8 +98,72 @@ bool btPolyhedralConvexShape::initializePolyhedralFeatures(int shiftVerticesByMa conv.compute(&orgVertices[0].getX(), sizeof(btVector3),orgVertices.size(),0.f,0.f); } +#ifndef BT_RECONSTRUCT_FACES + + int numVertices = conv.vertices.size(); + m_polyhedron->m_vertices.resize(numVertices); + for (int p=0;p<numVertices;p++) + { + m_polyhedron->m_vertices[p] = conv.vertices[p]; + } + + int v0, v1; + for (int j = 0; j < conv.faces.size(); j++) + { + btVector3 edges[3]; + int numEdges = 0; + btFace combinedFace; + const btConvexHullComputer::Edge* edge = &conv.edges[conv.faces[j]]; + v0 = edge->getSourceVertex(); + int prevVertex=v0; + combinedFace.m_indices.push_back(v0); + v1 = edge->getTargetVertex(); + while (v1 != v0) + { + + btVector3 wa = conv.vertices[prevVertex]; + btVector3 wb = conv.vertices[v1]; + btVector3 newEdge = wb-wa; + newEdge.normalize(); + if (numEdges<2) + edges[numEdges++] = newEdge; + //face->addIndex(v1); + combinedFace.m_indices.push_back(v1); + edge = edge->getNextEdgeOfFace(); + prevVertex = v1; + int v01 = edge->getSourceVertex(); + v1 = edge->getTargetVertex(); + + } + + btAssert(combinedFace.m_indices.size() > 2); + + btVector3 faceNormal = edges[0].cross(edges[1]); + faceNormal.normalize(); + + btScalar planeEq=1e30f; + + for (int v=0;v<combinedFace.m_indices.size();v++) + { + btScalar eq = m_polyhedron->m_vertices[combinedFace.m_indices[v]].dot(faceNormal); + if (planeEq>eq) + { + planeEq=eq; + } + } + combinedFace.m_plane[0] = faceNormal.getX(); + combinedFace.m_plane[1] = faceNormal.getY(); + combinedFace.m_plane[2] = faceNormal.getZ(); + combinedFace.m_plane[3] = -planeEq; + + m_polyhedron->m_faces.push_back(combinedFace); + } + + +#else//BT_RECONSTRUCT_FACES + btAlignedObjectArray<btVector3> faceNormals; int numFaces = conv.faces.size(); faceNormals.resize(numFaces); @@ -311,7 +386,9 @@ bool btPolyhedralConvexShape::initializePolyhedralFeatures(int shiftVerticesByMa } - + +#endif //BT_RECONSTRUCT_FACES + m_polyhedron->initialize(); return true; diff --git a/thirdparty/bullet/BulletCollision/CollisionShapes/btPolyhedralConvexShape.h b/thirdparty/bullet/BulletCollision/CollisionShapes/btPolyhedralConvexShape.h index 7bf8e01c1f..b7ddb6e060 100644 --- a/thirdparty/bullet/BulletCollision/CollisionShapes/btPolyhedralConvexShape.h +++ b/thirdparty/bullet/BulletCollision/CollisionShapes/btPolyhedralConvexShape.h @@ -43,6 +43,9 @@ public: ///experimental/work-in-progress virtual bool initializePolyhedralFeatures(int shiftVerticesByMargin=0); + virtual void setPolyhedralFeatures(btConvexPolyhedron& polyhedron); + + const btConvexPolyhedron* getConvexPolyhedron() const { return m_polyhedron; diff --git a/thirdparty/bullet/BulletCollision/CollisionShapes/btSdfCollisionShape.cpp b/thirdparty/bullet/BulletCollision/CollisionShapes/btSdfCollisionShape.cpp new file mode 100644 index 0000000000..828acda470 --- /dev/null +++ b/thirdparty/bullet/BulletCollision/CollisionShapes/btSdfCollisionShape.cpp @@ -0,0 +1,99 @@ +#include "btSdfCollisionShape.h" +#include "btMiniSDF.h" +#include "LinearMath/btAabbUtil2.h" + +struct btSdfCollisionShapeInternalData +{ + btVector3 m_localScaling; + btScalar m_margin; + btMiniSDF m_sdf; + + btSdfCollisionShapeInternalData() + :m_localScaling(1,1,1), + m_margin(0) + { + + } +}; + +bool btSdfCollisionShape::initializeSDF(const char* sdfData, int sizeInBytes) +{ + bool valid = m_data->m_sdf.load(sdfData, sizeInBytes); + return valid; +} +btSdfCollisionShape::btSdfCollisionShape() +{ + m_shapeType = SDF_SHAPE_PROXYTYPE; + m_data = new btSdfCollisionShapeInternalData(); + + + + //"E:/develop/bullet3/data/toys/ground_hole64_64_8.cdf");//ground_cube.cdf"); + /*unsigned int field_id=0; + Eigen::Vector3d x (1,10,1); + Eigen::Vector3d gradient; + double dist = m_data->m_sdf.interpolate(field_id, x, &gradient); + printf("dist=%g\n", dist); + */ + +} +btSdfCollisionShape::~btSdfCollisionShape() +{ + delete m_data; +} + +void btSdfCollisionShape::getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const +{ + btAssert(m_data->m_sdf.isValid()); + btVector3 localAabbMin = m_data->m_sdf.m_domain.m_min; + btVector3 localAabbMax = m_data->m_sdf.m_domain.m_max; + btScalar margin(0); + btTransformAabb(localAabbMin,localAabbMax,margin,t,aabbMin,aabbMax); + +} + + +void btSdfCollisionShape::setLocalScaling(const btVector3& scaling) +{ + m_data->m_localScaling = scaling; +} +const btVector3& btSdfCollisionShape::getLocalScaling() const +{ + return m_data->m_localScaling; +} +void btSdfCollisionShape::calculateLocalInertia(btScalar mass,btVector3& inertia) const +{ + inertia.setValue(0,0,0); +} +const char* btSdfCollisionShape::getName()const +{ + return "btSdfCollisionShape"; +} +void btSdfCollisionShape::setMargin(btScalar margin) +{ + m_data->m_margin = margin; +} +btScalar btSdfCollisionShape::getMargin() const +{ + return m_data->m_margin; +} + +void btSdfCollisionShape::processAllTriangles(btTriangleCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const +{ + //not yet +} + + +bool btSdfCollisionShape::queryPoint(const btVector3& ptInSDF, btScalar& distOut, btVector3& normal) +{ + int field = 0; + btVector3 grad; + double dist; + bool hasResult = m_data->m_sdf.interpolate(field,dist, ptInSDF,&grad); + if (hasResult) + { + normal.setValue(grad[0],grad[1],grad[2]); + distOut= dist; + } + return hasResult; +} diff --git a/thirdparty/bullet/BulletCollision/CollisionShapes/btSdfCollisionShape.h b/thirdparty/bullet/BulletCollision/CollisionShapes/btSdfCollisionShape.h new file mode 100644 index 0000000000..6e32db9cd8 --- /dev/null +++ b/thirdparty/bullet/BulletCollision/CollisionShapes/btSdfCollisionShape.h @@ -0,0 +1,30 @@ +#ifndef BT_SDF_COLLISION_SHAPE_H +#define BT_SDF_COLLISION_SHAPE_H + +#include "btConcaveShape.h" + +class btSdfCollisionShape : public btConcaveShape +{ + struct btSdfCollisionShapeInternalData* m_data; + +public: + + btSdfCollisionShape(); + virtual ~btSdfCollisionShape(); + + bool initializeSDF(const char* sdfData, int sizeInBytes); + + virtual void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const; + virtual void setLocalScaling(const btVector3& scaling); + virtual const btVector3& getLocalScaling() const; + virtual void calculateLocalInertia(btScalar mass,btVector3& inertia) const; + virtual const char* getName()const; + virtual void setMargin(btScalar margin); + virtual btScalar getMargin() const; + + virtual void processAllTriangles(btTriangleCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const; + + bool queryPoint(const btVector3& ptInSDF, btScalar& distOut, btVector3& normal); +}; + +#endif //BT_SDF_COLLISION_SHAPE_H diff --git a/thirdparty/bullet/BulletCollision/CollisionShapes/btShapeHull.cpp b/thirdparty/bullet/BulletCollision/CollisionShapes/btShapeHull.cpp index 3beaf86580..9f712fe555 100644 --- a/thirdparty/bullet/BulletCollision/CollisionShapes/btShapeHull.cpp +++ b/thirdparty/bullet/BulletCollision/CollisionShapes/btShapeHull.cpp @@ -20,6 +20,8 @@ subject to the following restrictions: #include "LinearMath/btConvexHull.h" #define NUM_UNITSPHERE_POINTS 42 +#define NUM_UNITSPHERE_POINTS_HIGHRES 256 + btShapeHull::btShapeHull (const btConvexShape* shape) { @@ -36,9 +38,9 @@ btShapeHull::~btShapeHull () } bool -btShapeHull::buildHull (btScalar /*margin*/) +btShapeHull::buildHull (btScalar /*margin*/, int highres) { - int numSampleDirections = NUM_UNITSPHERE_POINTS; + int numSampleDirections = highres? NUM_UNITSPHERE_POINTS_HIGHRES:NUM_UNITSPHERE_POINTS; { int numPDA = m_shape->getNumPreferredPenetrationDirections(); if (numPDA) @@ -47,17 +49,17 @@ btShapeHull::buildHull (btScalar /*margin*/) { btVector3 norm; m_shape->getPreferredPenetrationDirection(i,norm); - getUnitSpherePoints()[numSampleDirections] = norm; + getUnitSpherePoints(highres)[numSampleDirections] = norm; numSampleDirections++; } } } - btVector3 supportPoints[NUM_UNITSPHERE_POINTS+MAX_PREFERRED_PENETRATION_DIRECTIONS*2]; + btVector3 supportPoints[NUM_UNITSPHERE_POINTS_HIGHRES+MAX_PREFERRED_PENETRATION_DIRECTIONS*2]; int i; for (i = 0; i < numSampleDirections; i++) { - supportPoints[i] = m_shape->localGetSupportingVertex(getUnitSpherePoints()[i]); + supportPoints[i] = m_shape->localGetSupportingVertex(getUnitSpherePoints(highres)[i]); } HullDesc hd; @@ -118,9 +120,268 @@ btShapeHull::numIndices () const } -btVector3* btShapeHull::getUnitSpherePoints() +btVector3* btShapeHull::getUnitSpherePoints(int highres) { - static btVector3 sUnitSpherePoints[NUM_UNITSPHERE_POINTS+MAX_PREFERRED_PENETRATION_DIRECTIONS*2] = + static btVector3 sUnitSpherePointsHighres[NUM_UNITSPHERE_POINTS_HIGHRES + MAX_PREFERRED_PENETRATION_DIRECTIONS * 2] = + { + btVector3(btScalar(0.997604), btScalar(0.067004), btScalar(0.017144)), + btVector3(btScalar(0.984139), btScalar(-0.086784), btScalar(-0.154427)), + btVector3(btScalar(0.971065), btScalar(0.124164), btScalar(-0.203224)), + btVector3(btScalar(0.955844), btScalar(0.291173), btScalar(-0.037704)), + btVector3(btScalar(0.957405), btScalar(0.212238), btScalar(0.195157)), + btVector3(btScalar(0.971650), btScalar(-0.012709), btScalar(0.235561)), + btVector3(btScalar(0.984920), btScalar(-0.161831), btScalar(0.059695)), + btVector3(btScalar(0.946673), btScalar(-0.299288), btScalar(-0.117536)), + btVector3(btScalar(0.922670), btScalar(-0.219186), btScalar(-0.317019)), + btVector3(btScalar(0.928134), btScalar(-0.007265), btScalar(-0.371867)), + btVector3(btScalar(0.875642), btScalar(0.198434), btScalar(-0.439988)), + btVector3(btScalar(0.908035), btScalar(0.325975), btScalar(-0.262562)), + btVector3(btScalar(0.864519), btScalar(0.488706), btScalar(-0.116755)), + btVector3(btScalar(0.893009), btScalar(0.428046), btScalar(0.137185)), + btVector3(btScalar(0.857494), btScalar(0.362137), btScalar(0.364776)), + btVector3(btScalar(0.900815), btScalar(0.132524), btScalar(0.412987)), + btVector3(btScalar(0.934964), btScalar(-0.241739), btScalar(0.259179)), + btVector3(btScalar(0.894570), btScalar(-0.103504), btScalar(0.434263)), + btVector3(btScalar(0.922085), btScalar(-0.376668), btScalar(0.086241)), + btVector3(btScalar(0.862177), btScalar(-0.499154), btScalar(-0.085330)), + btVector3(btScalar(0.861982), btScalar(-0.420218), btScalar(-0.282861)), + btVector3(btScalar(0.818076), btScalar(-0.328256), btScalar(-0.471804)), + btVector3(btScalar(0.762657), btScalar(-0.179329), btScalar(-0.621124)), + btVector3(btScalar(0.826857), btScalar(0.019760), btScalar(-0.561786)), + btVector3(btScalar(0.731434), btScalar(0.206599), btScalar(-0.649817)), + btVector3(btScalar(0.769486), btScalar(0.379052), btScalar(-0.513770)), + btVector3(btScalar(0.796806), btScalar(0.507176), btScalar(-0.328145)), + btVector3(btScalar(0.679722), btScalar(0.684101), btScalar(-0.264123)), + btVector3(btScalar(0.786854), btScalar(0.614886), btScalar(0.050912)), + btVector3(btScalar(0.769486), btScalar(0.571141), btScalar(0.285139)), + btVector3(btScalar(0.707432), btScalar(0.492789), btScalar(0.506288)), + btVector3(btScalar(0.774560), btScalar(0.268037), btScalar(0.572652)), + btVector3(btScalar(0.796220), btScalar(0.031230), btScalar(0.604077)), + btVector3(btScalar(0.837395), btScalar(-0.320285), btScalar(0.442461)), + btVector3(btScalar(0.848127), btScalar(-0.450548), btScalar(0.278307)), + btVector3(btScalar(0.775536), btScalar(-0.206354), btScalar(0.596465)), + btVector3(btScalar(0.816320), btScalar(-0.567007), btScalar(0.109469)), + btVector3(btScalar(0.741191), btScalar(-0.668690), btScalar(-0.056832)), + btVector3(btScalar(0.755632), btScalar(-0.602975), btScalar(-0.254949)), + btVector3(btScalar(0.720311), btScalar(-0.521318), btScalar(-0.457165)), + btVector3(btScalar(0.670746), btScalar(-0.386583), btScalar(-0.632835)), + btVector3(btScalar(0.587031), btScalar(-0.219769), btScalar(-0.778836)), + btVector3(btScalar(0.676015), btScalar(-0.003182), btScalar(-0.736676)), + btVector3(btScalar(0.566932), btScalar(0.186963), btScalar(-0.802064)), + btVector3(btScalar(0.618254), btScalar(0.398105), btScalar(-0.677533)), + btVector3(btScalar(0.653964), btScalar(0.575224), btScalar(-0.490933)), + btVector3(btScalar(0.525367), btScalar(0.743205), btScalar(-0.414028)), + btVector3(btScalar(0.506439), btScalar(0.836528), btScalar(-0.208885)), + btVector3(btScalar(0.651427), btScalar(0.756426), btScalar(-0.056247)), + btVector3(btScalar(0.641670), btScalar(0.745149), btScalar(0.180908)), + btVector3(btScalar(0.602643), btScalar(0.687211), btScalar(0.405180)), + btVector3(btScalar(0.516586), btScalar(0.596999), btScalar(0.613447)), + btVector3(btScalar(0.602252), btScalar(0.387801), btScalar(0.697573)), + btVector3(btScalar(0.646549), btScalar(0.153911), btScalar(0.746956)), + btVector3(btScalar(0.650842), btScalar(-0.087756), btScalar(0.753983)), + btVector3(btScalar(0.740411), btScalar(-0.497404), btScalar(0.451830)), + btVector3(btScalar(0.726946), btScalar(-0.619890), btScalar(0.295093)), + btVector3(btScalar(0.637768), btScalar(-0.313092), btScalar(0.703624)), + btVector3(btScalar(0.678942), btScalar(-0.722934), btScalar(0.126645)), + btVector3(btScalar(0.489072), btScalar(-0.867195), btScalar(-0.092942)), + btVector3(btScalar(0.622742), btScalar(-0.757541), btScalar(-0.194636)), + btVector3(btScalar(0.596788), btScalar(-0.693576), btScalar(-0.403098)), + btVector3(btScalar(0.550150), btScalar(-0.582172), btScalar(-0.598287)), + btVector3(btScalar(0.474436), btScalar(-0.429745), btScalar(-0.768101)), + btVector3(btScalar(0.372574), btScalar(-0.246016), btScalar(-0.894583)), + btVector3(btScalar(0.480095), btScalar(-0.026513), btScalar(-0.876626)), + btVector3(btScalar(0.352474), btScalar(0.177242), btScalar(-0.918787)), + btVector3(btScalar(0.441848), btScalar(0.374386), btScalar(-0.814946)), + btVector3(btScalar(0.492389), btScalar(0.582223), btScalar(-0.646693)), + btVector3(btScalar(0.343498), btScalar(0.866080), btScalar(-0.362693)), + btVector3(btScalar(0.362036), btScalar(0.745149), btScalar(-0.559639)), + btVector3(btScalar(0.334131), btScalar(0.937044), btScalar(-0.099774)), + btVector3(btScalar(0.486925), btScalar(0.871718), btScalar(0.052473)), + btVector3(btScalar(0.452776), btScalar(0.845665), btScalar(0.281820)), + btVector3(btScalar(0.399503), btScalar(0.771785), btScalar(0.494576)), + btVector3(btScalar(0.296469), btScalar(0.673018), btScalar(0.677469)), + btVector3(btScalar(0.392088), btScalar(0.479179), btScalar(0.785213)), + btVector3(btScalar(0.452190), btScalar(0.252094), btScalar(0.855286)), + btVector3(btScalar(0.478339), btScalar(0.013149), btScalar(0.877928)), + btVector3(btScalar(0.481656), btScalar(-0.219380), btScalar(0.848259)), + btVector3(btScalar(0.615327), btScalar(-0.494293), btScalar(0.613837)), + btVector3(btScalar(0.594642), btScalar(-0.650414), btScalar(0.472325)), + btVector3(btScalar(0.562249), btScalar(-0.771345), btScalar(0.297631)), + btVector3(btScalar(0.467411), btScalar(-0.437133), btScalar(0.768231)), + btVector3(btScalar(0.519513), btScalar(-0.847947), btScalar(0.103808)), + btVector3(btScalar(0.297640), btScalar(-0.938159), btScalar(-0.176288)), + btVector3(btScalar(0.446727), btScalar(-0.838615), btScalar(-0.311359)), + btVector3(btScalar(0.331790), btScalar(-0.942437), btScalar(0.040762)), + btVector3(btScalar(0.413358), btScalar(-0.748403), btScalar(-0.518259)), + btVector3(btScalar(0.347596), btScalar(-0.621640), btScalar(-0.701737)), + btVector3(btScalar(0.249831), btScalar(-0.456186), btScalar(-0.853984)), + btVector3(btScalar(0.131772), btScalar(-0.262931), btScalar(-0.955678)), + btVector3(btScalar(0.247099), btScalar(-0.042261), btScalar(-0.967975)), + btVector3(btScalar(0.113624), btScalar(0.165965), btScalar(-0.979491)), + btVector3(btScalar(0.217438), btScalar(0.374580), btScalar(-0.901220)), + btVector3(btScalar(0.307983), btScalar(0.554615), btScalar(-0.772786)), + btVector3(btScalar(0.166702), btScalar(0.953181), btScalar(-0.252021)), + btVector3(btScalar(0.172751), btScalar(0.844499), btScalar(-0.506743)), + btVector3(btScalar(0.177630), btScalar(0.711125), btScalar(-0.679876)), + btVector3(btScalar(0.120064), btScalar(0.992260), btScalar(-0.030482)), + btVector3(btScalar(0.289640), btScalar(0.949098), btScalar(0.122546)), + btVector3(btScalar(0.239879), btScalar(0.909047), btScalar(0.340377)), + btVector3(btScalar(0.181142), btScalar(0.821363), btScalar(0.540641)), + btVector3(btScalar(0.066986), btScalar(0.719097), btScalar(0.691327)), + btVector3(btScalar(0.156750), btScalar(0.545478), btScalar(0.823079)), + btVector3(btScalar(0.236172), btScalar(0.342306), btScalar(0.909353)), + btVector3(btScalar(0.277541), btScalar(0.112693), btScalar(0.953856)), + btVector3(btScalar(0.295299), btScalar(-0.121974), btScalar(0.947415)), + btVector3(btScalar(0.287883), btScalar(-0.349254), btScalar(0.891591)), + btVector3(btScalar(0.437165), btScalar(-0.634666), btScalar(0.636869)), + btVector3(btScalar(0.407113), btScalar(-0.784954), btScalar(0.466664)), + btVector3(btScalar(0.375111), btScalar(-0.888193), btScalar(0.264839)), + btVector3(btScalar(0.275394), btScalar(-0.560591), btScalar(0.780723)), + btVector3(btScalar(0.122015), btScalar(-0.992209), btScalar(-0.024821)), + btVector3(btScalar(0.087866), btScalar(-0.966156), btScalar(-0.241676)), + btVector3(btScalar(0.239489), btScalar(-0.885665), btScalar(-0.397437)), + btVector3(btScalar(0.167287), btScalar(-0.965184), btScalar(0.200817)), + btVector3(btScalar(0.201632), btScalar(-0.776789), btScalar(-0.596335)), + btVector3(btScalar(0.122015), btScalar(-0.637971), btScalar(-0.760098)), + btVector3(btScalar(0.008054), btScalar(-0.464741), btScalar(-0.885214)), + btVector3(btScalar(-0.116054), btScalar(-0.271096), btScalar(-0.955482)), + btVector3(btScalar(-0.000727), btScalar(-0.056065), btScalar(-0.998424)), + btVector3(btScalar(-0.134007), btScalar(0.152939), btScalar(-0.978905)), + btVector3(btScalar(-0.025900), btScalar(0.366026), btScalar(-0.930108)), + btVector3(btScalar(0.081231), btScalar(0.557337), btScalar(-0.826072)), + btVector3(btScalar(-0.002874), btScalar(0.917213), btScalar(-0.398023)), + btVector3(btScalar(-0.050683), btScalar(0.981761), btScalar(-0.182534)), + btVector3(btScalar(-0.040536), btScalar(0.710153), btScalar(-0.702713)), + btVector3(btScalar(-0.139081), btScalar(0.827973), btScalar(-0.543048)), + btVector3(btScalar(-0.101029), btScalar(0.994010), btScalar(0.041152)), + btVector3(btScalar(0.069328), btScalar(0.978067), btScalar(0.196133)), + btVector3(btScalar(0.023860), btScalar(0.911380), btScalar(0.410645)), + btVector3(btScalar(-0.153521), btScalar(0.736789), btScalar(0.658145)), + btVector3(btScalar(-0.070002), btScalar(0.591750), btScalar(0.802780)), + btVector3(btScalar(0.002590), btScalar(0.312948), btScalar(0.949562)), + btVector3(btScalar(0.090988), btScalar(-0.020680), btScalar(0.995627)), + btVector3(btScalar(0.088842), btScalar(-0.250099), btScalar(0.964006)), + btVector3(btScalar(0.083378), btScalar(-0.470185), btScalar(0.878318)), + btVector3(btScalar(0.240074), btScalar(-0.749764), btScalar(0.616374)), + btVector3(btScalar(0.210803), btScalar(-0.885860), btScalar(0.412987)), + btVector3(btScalar(0.077524), btScalar(-0.660524), btScalar(0.746565)), + btVector3(btScalar(-0.096736), btScalar(-0.990070), btScalar(-0.100945)), + btVector3(btScalar(-0.052634), btScalar(-0.990264), btScalar(0.127426)), + btVector3(btScalar(-0.106102), btScalar(-0.938354), btScalar(-0.328340)), + btVector3(btScalar(0.013323), btScalar(-0.863112), btScalar(-0.504596)), + btVector3(btScalar(-0.002093), btScalar(-0.936993), btScalar(0.349161)), + btVector3(btScalar(-0.106297), btScalar(-0.636610), btScalar(-0.763612)), + btVector3(btScalar(-0.229430), btScalar(-0.463769), btScalar(-0.855546)), + btVector3(btScalar(-0.245236), btScalar(-0.066175), btScalar(-0.966999)), + btVector3(btScalar(-0.351587), btScalar(-0.270513), btScalar(-0.896145)), + btVector3(btScalar(-0.370906), btScalar(0.133108), btScalar(-0.918982)), + btVector3(btScalar(-0.264360), btScalar(0.346000), btScalar(-0.900049)), + btVector3(btScalar(-0.151375), btScalar(0.543728), btScalar(-0.825291)), + btVector3(btScalar(-0.218697), btScalar(0.912741), btScalar(-0.344346)), + btVector3(btScalar(-0.274507), btScalar(0.953764), btScalar(-0.121635)), + btVector3(btScalar(-0.259677), btScalar(0.692266), btScalar(-0.673044)), + btVector3(btScalar(-0.350416), btScalar(0.798810), btScalar(-0.488786)), + btVector3(btScalar(-0.320170), btScalar(0.941127), btScalar(0.108297)), + btVector3(btScalar(-0.147667), btScalar(0.952792), btScalar(0.265034)), + btVector3(btScalar(-0.188061), btScalar(0.860636), btScalar(0.472910)), + btVector3(btScalar(-0.370906), btScalar(0.739900), btScalar(0.560941)), + btVector3(btScalar(-0.297143), btScalar(0.585334), btScalar(0.754178)), + btVector3(btScalar(-0.189622), btScalar(0.428241), btScalar(0.883393)), + btVector3(btScalar(-0.091272), btScalar(0.098695), btScalar(0.990747)), + btVector3(btScalar(-0.256945), btScalar(0.228375), btScalar(0.938827)), + btVector3(btScalar(-0.111761), btScalar(-0.133251), btScalar(0.984696)), + btVector3(btScalar(-0.118006), btScalar(-0.356253), btScalar(0.926725)), + btVector3(btScalar(-0.119372), btScalar(-0.563896), btScalar(0.817029)), + btVector3(btScalar(0.041228), btScalar(-0.833949), btScalar(0.550010)), + btVector3(btScalar(-0.121909), btScalar(-0.736543), btScalar(0.665172)), + btVector3(btScalar(-0.307681), btScalar(-0.931160), btScalar(-0.195026)), + btVector3(btScalar(-0.283679), btScalar(-0.957990), btScalar(0.041348)), + btVector3(btScalar(-0.227284), btScalar(-0.935243), btScalar(0.270890)), + btVector3(btScalar(-0.293436), btScalar(-0.858252), btScalar(-0.420860)), + btVector3(btScalar(-0.175767), btScalar(-0.780677), btScalar(-0.599262)), + btVector3(btScalar(-0.170108), btScalar(-0.858835), btScalar(0.482865)), + btVector3(btScalar(-0.332854), btScalar(-0.635055), btScalar(-0.696857)), + btVector3(btScalar(-0.447791), btScalar(-0.445299), btScalar(-0.775128)), + btVector3(btScalar(-0.470622), btScalar(-0.074146), btScalar(-0.879164)), + btVector3(btScalar(-0.639417), btScalar(-0.340505), btScalar(-0.689049)), + btVector3(btScalar(-0.598438), btScalar(0.104722), btScalar(-0.794256)), + btVector3(btScalar(-0.488575), btScalar(0.307699), btScalar(-0.816313)), + btVector3(btScalar(-0.379882), btScalar(0.513592), btScalar(-0.769077)), + btVector3(btScalar(-0.425740), btScalar(0.862775), btScalar(-0.272516)), + btVector3(btScalar(-0.480769), btScalar(0.875412), btScalar(-0.048439)), + btVector3(btScalar(-0.467890), btScalar(0.648716), btScalar(-0.600043)), + btVector3(btScalar(-0.543799), btScalar(0.730956), btScalar(-0.411881)), + btVector3(btScalar(-0.516284), btScalar(0.838277), btScalar(0.174076)), + btVector3(btScalar(-0.353343), btScalar(0.876384), btScalar(0.326519)), + btVector3(btScalar(-0.572875), btScalar(0.614497), btScalar(0.542007)), + btVector3(btScalar(-0.503600), btScalar(0.497261), btScalar(0.706161)), + btVector3(btScalar(-0.530920), btScalar(0.754870), btScalar(0.384685)), + btVector3(btScalar(-0.395884), btScalar(0.366414), btScalar(0.841818)), + btVector3(btScalar(-0.300656), btScalar(0.001678), btScalar(0.953661)), + btVector3(btScalar(-0.461060), btScalar(0.146912), btScalar(0.875000)), + btVector3(btScalar(-0.315486), btScalar(-0.232212), btScalar(0.919893)), + btVector3(btScalar(-0.323682), btScalar(-0.449187), btScalar(0.832644)), + btVector3(btScalar(-0.318999), btScalar(-0.639527), btScalar(0.699134)), + btVector3(btScalar(-0.496771), btScalar(-0.866029), btScalar(-0.055271)), + btVector3(btScalar(-0.496771), btScalar(-0.816257), btScalar(-0.294377)), + btVector3(btScalar(-0.456377), btScalar(-0.869528), btScalar(0.188130)), + btVector3(btScalar(-0.380858), btScalar(-0.827144), btScalar(0.412792)), + btVector3(btScalar(-0.449352), btScalar(-0.727405), btScalar(-0.518259)), + btVector3(btScalar(-0.570533), btScalar(-0.551064), btScalar(-0.608632)), + btVector3(btScalar(-0.656394), btScalar(-0.118280), btScalar(-0.744874)), + btVector3(btScalar(-0.756696), btScalar(-0.438105), btScalar(-0.484882)), + btVector3(btScalar(-0.801773), btScalar(-0.204798), btScalar(-0.561005)), + btVector3(btScalar(-0.785186), btScalar(0.038618), btScalar(-0.617805)), + btVector3(btScalar(-0.709082), btScalar(0.262399), btScalar(-0.654306)), + btVector3(btScalar(-0.583412), btScalar(0.462265), btScalar(-0.667383)), + btVector3(btScalar(-0.616001), btScalar(0.761286), btScalar(-0.201272)), + btVector3(btScalar(-0.660687), btScalar(0.750204), btScalar(0.020072)), + btVector3(btScalar(-0.744987), btScalar(0.435823), btScalar(-0.504791)), + btVector3(btScalar(-0.713765), btScalar(0.605554), btScalar(-0.351373)), + btVector3(btScalar(-0.686251), btScalar(0.687600), btScalar(0.236927)), + btVector3(btScalar(-0.680201), btScalar(0.429407), btScalar(0.593732)), + btVector3(btScalar(-0.733474), btScalar(0.546450), btScalar(0.403814)), + btVector3(btScalar(-0.591023), btScalar(0.292923), btScalar(0.751445)), + btVector3(btScalar(-0.500283), btScalar(-0.080757), btScalar(0.861922)), + btVector3(btScalar(-0.643710), btScalar(0.070115), btScalar(0.761985)), + btVector3(btScalar(-0.506332), btScalar(-0.308425), btScalar(0.805122)), + btVector3(btScalar(-0.503015), btScalar(-0.509847), btScalar(0.697573)), + btVector3(btScalar(-0.482525), btScalar(-0.682105), btScalar(0.549229)), + btVector3(btScalar(-0.680396), btScalar(-0.716323), btScalar(-0.153451)), + btVector3(btScalar(-0.658346), btScalar(-0.746264), btScalar(0.097562)), + btVector3(btScalar(-0.653272), btScalar(-0.646915), btScalar(-0.392948)), + btVector3(btScalar(-0.590828), btScalar(-0.732655), btScalar(0.337645)), + btVector3(btScalar(-0.819140), btScalar(-0.518013), btScalar(-0.246166)), + btVector3(btScalar(-0.900513), btScalar(-0.282178), btScalar(-0.330487)), + btVector3(btScalar(-0.914953), btScalar(-0.028652), btScalar(-0.402122)), + btVector3(btScalar(-0.859924), btScalar(0.220209), btScalar(-0.459898)), + btVector3(btScalar(-0.777185), btScalar(0.613720), btScalar(-0.137836)), + btVector3(btScalar(-0.805285), btScalar(0.586889), btScalar(0.082728)), + btVector3(btScalar(-0.872413), btScalar(0.406077), btScalar(-0.271735)), + btVector3(btScalar(-0.859339), btScalar(0.448072), btScalar(0.246101)), + btVector3(btScalar(-0.757671), btScalar(0.216320), btScalar(0.615594)), + btVector3(btScalar(-0.826165), btScalar(0.348139), btScalar(0.442851)), + btVector3(btScalar(-0.671810), btScalar(-0.162803), btScalar(0.722557)), + btVector3(btScalar(-0.796504), btScalar(-0.004543), btScalar(0.604468)), + btVector3(btScalar(-0.676298), btScalar(-0.378223), btScalar(0.631794)), + btVector3(btScalar(-0.668883), btScalar(-0.558258), btScalar(0.490673)), + btVector3(btScalar(-0.821287), btScalar(-0.570118), btScalar(0.006994)), + btVector3(btScalar(-0.767428), btScalar(-0.587810), btScalar(0.255470)), + btVector3(btScalar(-0.933296), btScalar(-0.349837), btScalar(-0.079865)), + btVector3(btScalar(-0.982667), btScalar(-0.100393), btScalar(-0.155208)), + btVector3(btScalar(-0.961396), btScalar(0.160910), btScalar(-0.222938)), + btVector3(btScalar(-0.934858), btScalar(0.354555), btScalar(-0.006864)), + btVector3(btScalar(-0.941687), btScalar(0.229736), btScalar(0.245711)), + btVector3(btScalar(-0.884317), btScalar(0.131552), btScalar(0.447536)), + btVector3(btScalar(-0.810359), btScalar(-0.219769), btScalar(0.542788)), + btVector3(btScalar(-0.915929), btScalar(-0.210048), btScalar(0.341743)), + btVector3(btScalar(-0.816799), btScalar(-0.407192), btScalar(0.408303)), + btVector3(btScalar(-0.903050), btScalar(-0.392416), btScalar(0.174076)), + btVector3(btScalar(-0.980325), btScalar(-0.170969), btScalar(0.096586)), + btVector3(btScalar(-0.995936), btScalar(0.084891), btScalar(0.029441)), + btVector3(btScalar(-0.960031), btScalar(0.002650), btScalar(0.279283)), + }; + static btVector3 sUnitSpherePoints[NUM_UNITSPHERE_POINTS + MAX_PREFERRED_PENETRATION_DIRECTIONS * 2] = { btVector3(btScalar(0.000000) , btScalar(-0.000000),btScalar(-1.000000)), btVector3(btScalar(0.723608) , btScalar(-0.525725),btScalar(-0.447219)), @@ -165,6 +426,8 @@ btVector3* btShapeHull::getUnitSpherePoints() btVector3(btScalar(-0.425323) , btScalar(0.309011),btScalar(0.850654)), btVector3(btScalar(0.162456) , btScalar(0.499995),btScalar(0.850654)) }; + if (highres) + return sUnitSpherePointsHighres; return sUnitSpherePoints; } diff --git a/thirdparty/bullet/BulletCollision/CollisionShapes/btShapeHull.h b/thirdparty/bullet/BulletCollision/CollisionShapes/btShapeHull.h index e959f198b6..78ea4b6501 100644 --- a/thirdparty/bullet/BulletCollision/CollisionShapes/btShapeHull.h +++ b/thirdparty/bullet/BulletCollision/CollisionShapes/btShapeHull.h @@ -34,7 +34,7 @@ protected: unsigned int m_numIndices; const btConvexShape* m_shape; - static btVector3* getUnitSpherePoints(); + static btVector3* getUnitSpherePoints(int highres=0); public: BT_DECLARE_ALIGNED_ALLOCATOR(); @@ -42,7 +42,7 @@ public: btShapeHull (const btConvexShape* shape); ~btShapeHull (); - bool buildHull (btScalar margin); + bool buildHull (btScalar margin, int highres=0); int numTriangles () const; int numVertices () const; diff --git a/thirdparty/bullet/BulletCollision/CollisionShapes/btStaticPlaneShape.cpp b/thirdparty/bullet/BulletCollision/CollisionShapes/btStaticPlaneShape.cpp index d17141e3f2..b5e0e716d4 100644 --- a/thirdparty/bullet/BulletCollision/CollisionShapes/btStaticPlaneShape.cpp +++ b/thirdparty/bullet/BulletCollision/CollisionShapes/btStaticPlaneShape.cpp @@ -69,8 +69,6 @@ void btStaticPlaneShape::processAllTriangles(btTriangleCallback* callback,const //tangentDir0/tangentDir1 can be precalculated btPlaneSpace1(m_planeNormal,tangentDir0,tangentDir1); - btVector3 supVertex0,supVertex1; - btVector3 projectedCenter = center - (m_planeNormal.dot(center) - m_planeConstant)*m_planeNormal; btVector3 triangle[3]; diff --git a/thirdparty/bullet/BulletCollision/CollisionShapes/btTriangleIndexVertexArray.h b/thirdparty/bullet/BulletCollision/CollisionShapes/btTriangleIndexVertexArray.h index 9e1544e87a..b7a6f74361 100644 --- a/thirdparty/bullet/BulletCollision/CollisionShapes/btTriangleIndexVertexArray.h +++ b/thirdparty/bullet/BulletCollision/CollisionShapes/btTriangleIndexVertexArray.h @@ -63,7 +63,7 @@ typedef btAlignedObjectArray<btIndexedMesh> IndexedMeshArray; ///The btTriangleIndexVertexArray allows to access multiple triangle meshes, by indexing into existing triangle/index arrays. ///Additional meshes can be added using addIndexedMesh -///No duplcate is made of the vertex/index data, it only indexes into external vertex/index arrays. +///No duplicate is made of the vertex/index data, it only indexes into external vertex/index arrays. ///So keep those arrays around during the lifetime of this btTriangleIndexVertexArray. ATTRIBUTE_ALIGNED16( class) btTriangleIndexVertexArray : public btStridingMeshInterface { diff --git a/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.cpp b/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.cpp index 940282f576..3481fec850 100644 --- a/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.cpp +++ b/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.cpp @@ -113,12 +113,7 @@ bool btContinuousConvexCollision::calcTimeOfImpact( if ((relLinVelocLength+maxAngularProjectedVelocity) == 0.f) return false; - - btScalar lambda = btScalar(0.); - btVector3 v(1,0,0); - - int maxIter = MAX_ITERATIONS; btVector3 n; n.setValue(btScalar(0.),btScalar(0.),btScalar(0.)); @@ -137,8 +132,7 @@ bool btContinuousConvexCollision::calcTimeOfImpact( btPointCollector pointCollector1; - { - + { computeClosestPoints(fromA,fromB,pointCollector1); hasResult = pointCollector1.m_hasResult; @@ -172,28 +166,20 @@ bool btContinuousConvexCollision::calcTimeOfImpact( dLambda = dist / (projectedLinearVelocity+ maxAngularProjectedVelocity); - - - lambda = lambda + dLambda; + lambda += dLambda; - if (lambda > btScalar(1.)) + if (lambda > btScalar(1.) || lambda < btScalar(0.)) return false; - if (lambda < btScalar(0.)) - return false; - - //todo: next check with relative epsilon if (lambda <= lastLambda) { return false; //n.setValue(0,0,0); - break; + //break; } lastLambda = lambda; - - //interpolate to next lambda btTransform interpolatedTransA,interpolatedTransB,relativeTrans; @@ -223,7 +209,7 @@ bool btContinuousConvexCollision::calcTimeOfImpact( } numIter++; - if (numIter > maxIter) + if (numIter > MAX_ITERATIONS) { result.reportFailure(-2, numIter); return false; @@ -237,6 +223,5 @@ bool btContinuousConvexCollision::calcTimeOfImpact( } return false; - } diff --git a/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.h b/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.h index bdc0572f75..528b5e0101 100644 --- a/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.h +++ b/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.h @@ -25,7 +25,7 @@ class btStaticPlaneShape; /// btContinuousConvexCollision implements angular and linear time of impact for convex objects. /// Based on Brian Mirtich's Conservative Advancement idea (PhD thesis). -/// Algorithm operates in worldspace, in order to keep inbetween motion globally consistent. +/// Algorithm operates in worldspace, in order to keep in between motion globally consistent. /// It uses GJK at the moment. Future improvement would use minkowski sum / supporting vertex, merging innerloops class btContinuousConvexCollision : public btConvexCast { diff --git a/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.cpp b/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.cpp index 572ec36f56..b79f49d611 100644 --- a/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.cpp +++ b/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.cpp @@ -21,46 +21,64 @@ subject to the following restrictions: #include "BulletCollision/NarrowPhaseCollision/btGjkEpa2.h" -bool btGjkEpaPenetrationDepthSolver::calcPenDepth( btSimplexSolverInterface& simplexSolver, - const btConvexShape* pConvexA, const btConvexShape* pConvexB, - const btTransform& transformA, const btTransform& transformB, - btVector3& v, btVector3& wWitnessOnA, btVector3& wWitnessOnB, - class btIDebugDraw* debugDraw) +bool btGjkEpaPenetrationDepthSolver::calcPenDepth(btSimplexSolverInterface& simplexSolver, + const btConvexShape* pConvexA, const btConvexShape* pConvexB, + const btTransform& transformA, const btTransform& transformB, + btVector3& v, btVector3& wWitnessOnA, btVector3& wWitnessOnB, + class btIDebugDraw* debugDraw) { (void)debugDraw; (void)v; (void)simplexSolver; -// const btScalar radialmargin(btScalar(0.)); - - btVector3 guessVector(transformB.getOrigin()-transformA.getOrigin()); - btGjkEpaSolver2::sResults results; - + btVector3 guessVectors[] = { + btVector3(transformB.getOrigin() - transformA.getOrigin()).normalized(), + btVector3(transformA.getOrigin() - transformB.getOrigin()).normalized(), + btVector3(0, 0, 1), + btVector3(0, 1, 0), + btVector3(1, 0, 0), + btVector3(1, 1, 0), + btVector3(1, 1, 1), + btVector3(0, 1, 1), + btVector3(1, 0, 1), + }; - if(btGjkEpaSolver2::Penetration(pConvexA,transformA, - pConvexB,transformB, - guessVector,results)) - - { - // debugDraw->drawLine(results.witnesses[1],results.witnesses[1]+results.normal,btVector3(255,0,0)); - //resultOut->addContactPoint(results.normal,results.witnesses[1],-results.depth); - wWitnessOnA = results.witnesses[0]; - wWitnessOnB = results.witnesses[1]; - v = results.normal; - return true; - } else + int numVectors = sizeof(guessVectors) / sizeof(btVector3); + + for (int i = 0; i < numVectors; i++) { - if(btGjkEpaSolver2::Distance(pConvexA,transformA,pConvexB,transformB,guessVector,results)) + simplexSolver.reset(); + btVector3 guessVector = guessVectors[i]; + + btGjkEpaSolver2::sResults results; + + if (btGjkEpaSolver2::Penetration(pConvexA, transformA, + pConvexB, transformB, + guessVector, results)) + { wWitnessOnA = results.witnesses[0]; wWitnessOnB = results.witnesses[1]; v = results.normal; - return false; + return true; + } + else + { + if (btGjkEpaSolver2::Distance(pConvexA, transformA, pConvexB, transformB, guessVector, results)) + { + wWitnessOnA = results.witnesses[0]; + wWitnessOnB = results.witnesses[1]; + v = results.normal; + return false; + } } } + //failed to find a distance/penetration + wWitnessOnA.setValue(0, 0, 0); + wWitnessOnB.setValue(0, 0, 0); + v.setValue(0, 0, 0); return false; } - diff --git a/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.cpp b/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.cpp index 257b026d9b..a0b825f0e8 100644 --- a/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.cpp +++ b/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.cpp @@ -1,4 +1,4 @@ -/* +/* Bullet Continuous Collision Detection and Physics Library Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ @@ -32,7 +32,7 @@ subject to the following restrictions: //must be above the machine epsilon #ifdef BT_USE_DOUBLE_PRECISION #define REL_ERROR2 btScalar(1.0e-12) - btScalar gGjkEpaPenetrationTolerance = 1e-7; + btScalar gGjkEpaPenetrationTolerance = 1.0e-12; #else #define REL_ERROR2 btScalar(1.0e-6) btScalar gGjkEpaPenetrationTolerance = 0.001; @@ -83,6 +83,593 @@ void btGjkPairDetector::getClosestPoints(const ClosestPointInput& input,Result& getClosestPointsNonVirtual(input,output,debugDraw); } +static void btComputeSupport(const btConvexShape* convexA, const btTransform& localTransA, const btConvexShape* convexB, const btTransform& localTransB, const btVector3& dir, bool check2d, btVector3& supAworld, btVector3& supBworld, btVector3& aMinb) +{ + btVector3 seperatingAxisInA = (dir)* localTransA.getBasis(); + btVector3 seperatingAxisInB = (-dir)* localTransB.getBasis(); + + btVector3 pInANoMargin = convexA->localGetSupportVertexWithoutMarginNonVirtual(seperatingAxisInA); + btVector3 qInBNoMargin = convexB->localGetSupportVertexWithoutMarginNonVirtual(seperatingAxisInB); + + btVector3 pInA = pInANoMargin; + btVector3 qInB = qInBNoMargin; + + supAworld = localTransA(pInA); + supBworld = localTransB(qInB); + + if (check2d) + { + supAworld[2] = 0.f; + supBworld[2] = 0.f; + } + + aMinb = supAworld - supBworld; +} + +struct btSupportVector +{ + btVector3 v; //!< Support point in minkowski sum + btVector3 v1; //!< Support point in obj1 + btVector3 v2; //!< Support point in obj2 +}; + +struct btSimplex +{ + btSupportVector ps[4]; + int last; //!< index of last added point +}; + +static btVector3 ccd_vec3_origin(0, 0, 0); + + +inline void btSimplexInit(btSimplex *s) +{ + s->last = -1; +} + +inline int btSimplexSize(const btSimplex *s) +{ + return s->last + 1; +} + +inline const btSupportVector *btSimplexPoint(const btSimplex *s, int idx) +{ + // here is no check on boundaries + return &s->ps[idx]; +} +inline void btSupportCopy(btSupportVector *d, const btSupportVector *s) +{ + *d = *s; +} + +inline void btVec3Copy(btVector3 *v, const btVector3* w) +{ + *v = *w; +} + +inline void ccdVec3Add(btVector3*v, const btVector3*w) +{ + v->m_floats[0] += w->m_floats[0]; + v->m_floats[1] += w->m_floats[1]; + v->m_floats[2] += w->m_floats[2]; +} + + +inline void ccdVec3Sub(btVector3 *v, const btVector3 *w) +{ + *v -= *w; +} +inline void btVec3Sub2(btVector3 *d, const btVector3 *v, const btVector3 *w) +{ + *d = (*v) - (*w); + +} +inline btScalar btVec3Dot(const btVector3 *a, const btVector3 *b) +{ + btScalar dot; + dot = a->dot(*b); + + return dot; +} + +inline btScalar ccdVec3Dist2(const btVector3 *a, const btVector3*b) +{ + btVector3 ab; + btVec3Sub2(&ab, a, b); + return btVec3Dot(&ab, &ab); +} + + +inline void btVec3Scale(btVector3 *d, btScalar k) +{ + d->m_floats[0] *= k; + d->m_floats[1] *= k; + d->m_floats[2] *= k; +} + +inline void btVec3Cross(btVector3 *d, const btVector3 *a, const btVector3 *b) +{ + d->m_floats[0] = (a->m_floats[1] * b->m_floats[2]) - (a->m_floats[2] * b->m_floats[1]); + d->m_floats[1] = (a->m_floats[2] * b->m_floats[0]) - (a->m_floats[0] * b->m_floats[2]); + d->m_floats[2] = (a->m_floats[0] * b->m_floats[1]) - (a->m_floats[1] * b->m_floats[0]); +} + +inline void btTripleCross(const btVector3 *a, const btVector3 *b, + const btVector3 *c, btVector3 *d) +{ + btVector3 e; + btVec3Cross(&e, a, b); + btVec3Cross(d, &e, c); +} + +inline int ccdEq(btScalar _a, btScalar _b) +{ + btScalar ab; + btScalar a, b; + + ab = btFabs(_a - _b); + if (btFabs(ab) < SIMD_EPSILON) + return 1; + + a = btFabs(_a); + b = btFabs(_b); + if (b > a) { + return ab < SIMD_EPSILON * b; + } + else { + return ab < SIMD_EPSILON * a; + } +} + +btScalar ccdVec3X(const btVector3* v) +{ + return v->x(); +} + +btScalar ccdVec3Y(const btVector3* v) +{ + return v->y(); +} + +btScalar ccdVec3Z(const btVector3* v) +{ + return v->z(); +} +inline int btVec3Eq(const btVector3 *a, const btVector3 *b) +{ + return ccdEq(ccdVec3X(a), ccdVec3X(b)) + && ccdEq(ccdVec3Y(a), ccdVec3Y(b)) + && ccdEq(ccdVec3Z(a), ccdVec3Z(b)); +} + + +inline void btSimplexAdd(btSimplex *s, const btSupportVector *v) +{ + // here is no check on boundaries in sake of speed + ++s->last; + btSupportCopy(s->ps + s->last, v); +} + + +inline void btSimplexSet(btSimplex *s, size_t pos, const btSupportVector *a) +{ + btSupportCopy(s->ps + pos, a); +} + +inline void btSimplexSetSize(btSimplex *s, int size) +{ + s->last = size - 1; +} + +inline const btSupportVector *ccdSimplexLast(const btSimplex *s) +{ + return btSimplexPoint(s, s->last); +} + +inline int ccdSign(btScalar val) +{ + if (btFuzzyZero(val)) { + return 0; + } + else if (val < btScalar(0)) { + return -1; + } + return 1; +} + + +inline btScalar btVec3PointSegmentDist2(const btVector3 *P, + const btVector3 *x0, + const btVector3 *b, + btVector3 *witness) +{ + // The computation comes from solving equation of segment: + // S(t) = x0 + t.d + // where - x0 is initial point of segment + // - d is direction of segment from x0 (|d| > 0) + // - t belongs to <0, 1> interval + // + // Than, distance from a segment to some point P can be expressed: + // D(t) = |x0 + t.d - P|^2 + // which is distance from any point on segment. Minimization + // of this function brings distance from P to segment. + // Minimization of D(t) leads to simple quadratic equation that's + // solving is straightforward. + // + // Bonus of this method is witness point for free. + + btScalar dist, t; + btVector3 d, a; + + // direction of segment + btVec3Sub2(&d, b, x0); + + // precompute vector from P to x0 + btVec3Sub2(&a, x0, P); + + t = -btScalar(1.) * btVec3Dot(&a, &d); + t /= btVec3Dot(&d, &d); + + if (t < btScalar(0) || btFuzzyZero(t)) { + dist = ccdVec3Dist2(x0, P); + if (witness) + btVec3Copy(witness, x0); + } + else if (t > btScalar(1) || ccdEq(t, btScalar(1))) { + dist = ccdVec3Dist2(b, P); + if (witness) + btVec3Copy(witness, b); + } + else { + if (witness) { + btVec3Copy(witness, &d); + btVec3Scale(witness, t); + ccdVec3Add(witness, x0); + dist = ccdVec3Dist2(witness, P); + } + else { + // recycling variables + btVec3Scale(&d, t); + ccdVec3Add(&d, &a); + dist = btVec3Dot(&d, &d); + } + } + + return dist; +} + + +btScalar btVec3PointTriDist2(const btVector3 *P, + const btVector3 *x0, const btVector3 *B, + const btVector3 *C, + btVector3 *witness) +{ + // Computation comes from analytic expression for triangle (x0, B, C) + // T(s, t) = x0 + s.d1 + t.d2, where d1 = B - x0 and d2 = C - x0 and + // Then equation for distance is: + // D(s, t) = | T(s, t) - P |^2 + // This leads to minimization of quadratic function of two variables. + // The solution from is taken only if s is between 0 and 1, t is + // between 0 and 1 and t + s < 1, otherwise distance from segment is + // computed. + + btVector3 d1, d2, a; + double u, v, w, p, q, r; + double s, t, dist, dist2; + btVector3 witness2; + + btVec3Sub2(&d1, B, x0); + btVec3Sub2(&d2, C, x0); + btVec3Sub2(&a, x0, P); + + u = btVec3Dot(&a, &a); + v = btVec3Dot(&d1, &d1); + w = btVec3Dot(&d2, &d2); + p = btVec3Dot(&a, &d1); + q = btVec3Dot(&a, &d2); + r = btVec3Dot(&d1, &d2); + + s = (q * r - w * p) / (w * v - r * r); + t = (-s * r - q) / w; + + if ((btFuzzyZero(s) || s > btScalar(0)) + && (ccdEq(s, btScalar(1)) || s < btScalar(1)) + && (btFuzzyZero(t) || t > btScalar(0)) + && (ccdEq(t, btScalar(1)) || t < btScalar(1)) + && (ccdEq(t + s, btScalar(1)) || t + s < btScalar(1))) { + + if (witness) + { + btVec3Scale(&d1, s); + btVec3Scale(&d2, t); + btVec3Copy(witness, x0); + ccdVec3Add(witness, &d1); + ccdVec3Add(witness, &d2); + + dist = ccdVec3Dist2(witness, P); + } + else + { + dist = s * s * v; + dist += t * t * w; + dist += btScalar(2.) * s * t * r; + dist += btScalar(2.) * s * p; + dist += btScalar(2.) * t * q; + dist += u; + } + } + else { + dist = btVec3PointSegmentDist2(P, x0, B, witness); + + dist2 = btVec3PointSegmentDist2(P, x0, C, &witness2); + if (dist2 < dist) { + dist = dist2; + if (witness) + btVec3Copy(witness, &witness2); + } + + dist2 = btVec3PointSegmentDist2(P, B, C, &witness2); + if (dist2 < dist) { + dist = dist2; + if (witness) + btVec3Copy(witness, &witness2); + } + } + + return dist; +} + + +static int btDoSimplex2(btSimplex *simplex, btVector3 *dir) +{ + const btSupportVector *A, *B; + btVector3 AB, AO, tmp; + btScalar dot; + + // get last added as A + A = ccdSimplexLast(simplex); + // get the other point + B = btSimplexPoint(simplex, 0); + // compute AB oriented segment + btVec3Sub2(&AB, &B->v, &A->v); + // compute AO vector + btVec3Copy(&AO, &A->v); + btVec3Scale(&AO, -btScalar(1)); + + // dot product AB . AO + dot = btVec3Dot(&AB, &AO); + + // check if origin doesn't lie on AB segment + btVec3Cross(&tmp, &AB, &AO); + if (btFuzzyZero(btVec3Dot(&tmp, &tmp)) && dot > btScalar(0)) { + return 1; + } + + // check if origin is in area where AB segment is + if (btFuzzyZero(dot) || dot < btScalar(0)) { + // origin is in outside are of A + btSimplexSet(simplex, 0, A); + btSimplexSetSize(simplex, 1); + btVec3Copy(dir, &AO); + } + else { + // origin is in area where AB segment is + + // keep simplex untouched and set direction to + // AB x AO x AB + btTripleCross(&AB, &AO, &AB, dir); + } + + return 0; +} + + + +static int btDoSimplex3(btSimplex *simplex, btVector3 *dir) +{ + const btSupportVector *A, *B, *C; + btVector3 AO, AB, AC, ABC, tmp; + btScalar dot, dist; + + // get last added as A + A = ccdSimplexLast(simplex); + // get the other points + B = btSimplexPoint(simplex, 1); + C = btSimplexPoint(simplex, 0); + + // check touching contact + dist = btVec3PointTriDist2(&ccd_vec3_origin, &A->v, &B->v, &C->v, 0); + if (btFuzzyZero(dist)) { + return 1; + } + + // check if triangle is really triangle (has area > 0) + // if not simplex can't be expanded and thus no itersection is found + if (btVec3Eq(&A->v, &B->v) || btVec3Eq(&A->v, &C->v)) { + return -1; + } + + // compute AO vector + btVec3Copy(&AO, &A->v); + btVec3Scale(&AO, -btScalar(1)); + + // compute AB and AC segments and ABC vector (perpendircular to triangle) + btVec3Sub2(&AB, &B->v, &A->v); + btVec3Sub2(&AC, &C->v, &A->v); + btVec3Cross(&ABC, &AB, &AC); + + btVec3Cross(&tmp, &ABC, &AC); + dot = btVec3Dot(&tmp, &AO); + if (btFuzzyZero(dot) || dot > btScalar(0)) { + dot = btVec3Dot(&AC, &AO); + if (btFuzzyZero(dot) || dot > btScalar(0)) { + // C is already in place + btSimplexSet(simplex, 1, A); + btSimplexSetSize(simplex, 2); + btTripleCross(&AC, &AO, &AC, dir); + } + else { + + dot = btVec3Dot(&AB, &AO); + if (btFuzzyZero(dot) || dot > btScalar(0)) { + btSimplexSet(simplex, 0, B); + btSimplexSet(simplex, 1, A); + btSimplexSetSize(simplex, 2); + btTripleCross(&AB, &AO, &AB, dir); + } + else { + btSimplexSet(simplex, 0, A); + btSimplexSetSize(simplex, 1); + btVec3Copy(dir, &AO); + } + } + } + else { + btVec3Cross(&tmp, &AB, &ABC); + dot = btVec3Dot(&tmp, &AO); + if (btFuzzyZero(dot) || dot > btScalar(0)) + { + dot = btVec3Dot(&AB, &AO); + if (btFuzzyZero(dot) || dot > btScalar(0)) { + btSimplexSet(simplex, 0, B); + btSimplexSet(simplex, 1, A); + btSimplexSetSize(simplex, 2); + btTripleCross(&AB, &AO, &AB, dir); + } + else { + btSimplexSet(simplex, 0, A); + btSimplexSetSize(simplex, 1); + btVec3Copy(dir, &AO); + } + } + else { + dot = btVec3Dot(&ABC, &AO); + if (btFuzzyZero(dot) || dot > btScalar(0)) { + btVec3Copy(dir, &ABC); + } + else { + btSupportVector tmp; + btSupportCopy(&tmp, C); + btSimplexSet(simplex, 0, B); + btSimplexSet(simplex, 1, &tmp); + + btVec3Copy(dir, &ABC); + btVec3Scale(dir, -btScalar(1)); + } + } + } + + return 0; +} + +static int btDoSimplex4(btSimplex *simplex, btVector3 *dir) +{ + const btSupportVector *A, *B, *C, *D; + btVector3 AO, AB, AC, AD, ABC, ACD, ADB; + int B_on_ACD, C_on_ADB, D_on_ABC; + int AB_O, AC_O, AD_O; + btScalar dist; + + // get last added as A + A = ccdSimplexLast(simplex); + // get the other points + B = btSimplexPoint(simplex, 2); + C = btSimplexPoint(simplex, 1); + D = btSimplexPoint(simplex, 0); + + // check if tetrahedron is really tetrahedron (has volume > 0) + // if it is not simplex can't be expanded and thus no intersection is + // found + dist = btVec3PointTriDist2(&A->v, &B->v, &C->v, &D->v, 0); + if (btFuzzyZero(dist)) { + return -1; + } + + // check if origin lies on some of tetrahedron's face - if so objects + // intersect + dist = btVec3PointTriDist2(&ccd_vec3_origin, &A->v, &B->v, &C->v, 0); + if (btFuzzyZero(dist)) + return 1; + dist = btVec3PointTriDist2(&ccd_vec3_origin, &A->v, &C->v, &D->v, 0); + if (btFuzzyZero(dist)) + return 1; + dist = btVec3PointTriDist2(&ccd_vec3_origin, &A->v, &B->v, &D->v, 0); + if (btFuzzyZero(dist)) + return 1; + dist = btVec3PointTriDist2(&ccd_vec3_origin, &B->v, &C->v, &D->v, 0); + if (btFuzzyZero(dist)) + return 1; + + // compute AO, AB, AC, AD segments and ABC, ACD, ADB normal vectors + btVec3Copy(&AO, &A->v); + btVec3Scale(&AO, -btScalar(1)); + btVec3Sub2(&AB, &B->v, &A->v); + btVec3Sub2(&AC, &C->v, &A->v); + btVec3Sub2(&AD, &D->v, &A->v); + btVec3Cross(&ABC, &AB, &AC); + btVec3Cross(&ACD, &AC, &AD); + btVec3Cross(&ADB, &AD, &AB); + + // side (positive or negative) of B, C, D relative to planes ACD, ADB + // and ABC respectively + B_on_ACD = ccdSign(btVec3Dot(&ACD, &AB)); + C_on_ADB = ccdSign(btVec3Dot(&ADB, &AC)); + D_on_ABC = ccdSign(btVec3Dot(&ABC, &AD)); + + // whether origin is on same side of ACD, ADB, ABC as B, C, D + // respectively + AB_O = ccdSign(btVec3Dot(&ACD, &AO)) == B_on_ACD; + AC_O = ccdSign(btVec3Dot(&ADB, &AO)) == C_on_ADB; + AD_O = ccdSign(btVec3Dot(&ABC, &AO)) == D_on_ABC; + + if (AB_O && AC_O && AD_O) { + // origin is in tetrahedron + return 1; + // rearrange simplex to triangle and call btDoSimplex3() + } + else if (!AB_O) { + // B is farthest from the origin among all of the tetrahedron's + // points, so remove it from the list and go on with the triangle + // case + + // D and C are in place + btSimplexSet(simplex, 2, A); + btSimplexSetSize(simplex, 3); + } + else if (!AC_O) { + // C is farthest + btSimplexSet(simplex, 1, D); + btSimplexSet(simplex, 0, B); + btSimplexSet(simplex, 2, A); + btSimplexSetSize(simplex, 3); + } + else { // (!AD_O) + btSimplexSet(simplex, 0, C); + btSimplexSet(simplex, 1, B); + btSimplexSet(simplex, 2, A); + btSimplexSetSize(simplex, 3); + } + + return btDoSimplex3(simplex, dir); +} + +static int btDoSimplex(btSimplex *simplex, btVector3 *dir) +{ + if (btSimplexSize(simplex) == 2) { + // simplex contains segment only one segment + return btDoSimplex2(simplex, dir); + } + else if (btSimplexSize(simplex) == 3) { + // simplex contains triangle + return btDoSimplex3(simplex, dir); + } + else { // btSimplexSize(simplex) == 4 + // tetrahedron - this is the only shape which can encapsule origin + // so btDoSimplex4() also contains test on it + return btDoSimplex4(simplex, dir); + } +} + #ifdef __SPU__ void btGjkPairDetector::getClosestPointsNonVirtual(const ClosestPointInput& input,Result& output,class btIDebugDraw* debugDraw) #else @@ -123,193 +710,308 @@ void btGjkPairDetector::getClosestPointsNonVirtual(const ClosestPointInput& inpu bool checkSimplex = false; bool checkPenetration = true; m_degenerateSimplex = 0; - + m_lastUsedMethod = -1; - + int status = -2; + btVector3 orgNormalInB(0, 0, 0); + btScalar margin = marginA + marginB; + + //we add a separate implementation to check if the convex shapes intersect + //See also "Real-time Collision Detection with Implicit Objects" by Leif Olvang + //Todo: integrate the simplex penetration check directly inside the Bullet btVoronoiSimplexSolver + //and remove this temporary code from libCCD + //this fixes issue https://github.com/bulletphysics/bullet3/issues/1703 + //note, for large differences in shapes, use double precision build! { btScalar squaredDistance = BT_LARGE_FLOAT; btScalar delta = btScalar(0.); - - btScalar margin = marginA + marginB; - - - m_simplexSolver->reset(); + + - for ( ; ; ) - //while (true) - { + btSimplex simplex1; + btSimplex* simplex = &simplex1; + btSimplexInit(simplex); - btVector3 seperatingAxisInA = (-m_cachedSeparatingAxis)* input.m_transformA.getBasis(); - btVector3 seperatingAxisInB = m_cachedSeparatingAxis* input.m_transformB.getBasis(); + btVector3 dir(1, 0, 0); + { - btVector3 pInA = m_minkowskiA->localGetSupportVertexWithoutMarginNonVirtual(seperatingAxisInA); - btVector3 qInB = m_minkowskiB->localGetSupportVertexWithoutMarginNonVirtual(seperatingAxisInB); + btVector3 lastSupV; + btVector3 supAworld; + btVector3 supBworld; + btComputeSupport(m_minkowskiA, localTransA, m_minkowskiB, localTransB, dir, check2d, supAworld, supBworld, lastSupV); + + btSupportVector last; + last.v = lastSupV; + last.v1 = supAworld; + last.v2 = supBworld; - btVector3 pWorld = localTransA(pInA); - btVector3 qWorld = localTransB(qInB); + btSimplexAdd(simplex, &last); + dir = -lastSupV; - if (check2d) + + + // start iterations + for (int iterations = 0; iterations <gGjkMaxIter; iterations++) { - pWorld[2] = 0.f; - qWorld[2] = 0.f; - } + // obtain support point + btComputeSupport(m_minkowskiA, localTransA, m_minkowskiB, localTransB, dir, check2d, supAworld, supBworld, lastSupV); + + // check if farthest point in Minkowski difference in direction dir + // isn't somewhere before origin (the test on negative dot product) + // - because if it is, objects are not intersecting at all. + btScalar delta = lastSupV.dot(dir); + if (delta < 0) + { + //no intersection, besides margin + status = -1; + break; + } + + // add last support vector to simplex + last.v = lastSupV; + last.v1 = supAworld; + last.v2 = supBworld; - btVector3 w = pWorld - qWorld; - delta = m_cachedSeparatingAxis.dot(w); + btSimplexAdd(simplex, &last); - // potential exit, they don't overlap - if ((delta > btScalar(0.0)) && (delta * delta > squaredDistance * input.m_maximumDistanceSquared)) - { - m_degenerateSimplex = 10; - checkSimplex=true; - //checkPenetration = false; - break; - } + // if btDoSimplex returns 1 if objects intersect, -1 if objects don't + // intersect and 0 if algorithm should continue - //exit 0: the new point is already in the simplex, or we didn't come any closer - if (m_simplexSolver->inSimplex(w)) - { - m_degenerateSimplex = 1; - checkSimplex = true; - break; - } - // are we getting any closer ? - btScalar f0 = squaredDistance - delta; - btScalar f1 = squaredDistance * REL_ERROR2; + btVector3 newDir; + int do_simplex_res = btDoSimplex(simplex, &dir); - if (f0 <= f1) - { - if (f0 <= btScalar(0.)) + if (do_simplex_res == 1) { - m_degenerateSimplex = 2; - } else + status = 0; // intersection found + break; + } + else if (do_simplex_res == -1) + { + // intersection not found + status = -1; + break; + } + + if (btFuzzyZero(btVec3Dot(&dir, &dir))) + { + // intersection not found + status = -1; + } + + if (dir.length2() < SIMD_EPSILON) { - m_degenerateSimplex = 11; + //no intersection, besides margin + status = -1; + break; + } + + if (dir.fuzzyZero()) + { + // intersection not found + status = -1; + break; } - checkSimplex = true; - break; } - //add current vertex to simplex - m_simplexSolver->addVertex(w, pWorld, qWorld); - btVector3 newCachedSeparatingAxis; + } + + m_simplexSolver->reset(); + if (status == 0) + { + //status = 0; + //printf("Intersect!\n"); + } - //calculate the closest point to the origin (update vector v) - if (!m_simplexSolver->closest(newCachedSeparatingAxis)) + if (status==-1) + { + //printf("not intersect\n"); + } + //printf("dir=%f,%f,%f\n",dir[0],dir[1],dir[2]); + if (1) + { + for (; ; ) + //while (true) { - m_degenerateSimplex = 3; - checkSimplex = true; - break; - } - if(newCachedSeparatingAxis.length2()<REL_ERROR2) - { - m_cachedSeparatingAxis = newCachedSeparatingAxis; - m_degenerateSimplex = 6; - checkSimplex = true; - break; - } + btVector3 seperatingAxisInA = (-m_cachedSeparatingAxis)* localTransA.getBasis(); + btVector3 seperatingAxisInB = m_cachedSeparatingAxis* localTransB.getBasis(); + + + btVector3 pInA = m_minkowskiA->localGetSupportVertexWithoutMarginNonVirtual(seperatingAxisInA); + btVector3 qInB = m_minkowskiB->localGetSupportVertexWithoutMarginNonVirtual(seperatingAxisInB); + + btVector3 pWorld = localTransA(pInA); + btVector3 qWorld = localTransB(qInB); - btScalar previousSquaredDistance = squaredDistance; - squaredDistance = newCachedSeparatingAxis.length2(); + + if (check2d) + { + pWorld[2] = 0.f; + qWorld[2] = 0.f; + } + + btVector3 w = pWorld - qWorld; + delta = m_cachedSeparatingAxis.dot(w); + + // potential exit, they don't overlap + if ((delta > btScalar(0.0)) && (delta * delta > squaredDistance * input.m_maximumDistanceSquared)) + { + m_degenerateSimplex = 10; + checkSimplex = true; + //checkPenetration = false; + break; + } + + //exit 0: the new point is already in the simplex, or we didn't come any closer + if (m_simplexSolver->inSimplex(w)) + { + m_degenerateSimplex = 1; + checkSimplex = true; + break; + } + // are we getting any closer ? + btScalar f0 = squaredDistance - delta; + btScalar f1 = squaredDistance * REL_ERROR2; + + if (f0 <= f1) + { + if (f0 <= btScalar(0.)) + { + m_degenerateSimplex = 2; + } + else + { + m_degenerateSimplex = 11; + } + checkSimplex = true; + break; + } + + //add current vertex to simplex + m_simplexSolver->addVertex(w, pWorld, qWorld); + btVector3 newCachedSeparatingAxis; + + //calculate the closest point to the origin (update vector v) + if (!m_simplexSolver->closest(newCachedSeparatingAxis)) + { + m_degenerateSimplex = 3; + checkSimplex = true; + break; + } + + if (newCachedSeparatingAxis.length2() < REL_ERROR2) + { + m_cachedSeparatingAxis = newCachedSeparatingAxis; + m_degenerateSimplex = 6; + checkSimplex = true; + break; + } + + btScalar previousSquaredDistance = squaredDistance; + squaredDistance = newCachedSeparatingAxis.length2(); #if 0 -///warning: this termination condition leads to some problems in 2d test case see Bullet/Demos/Box2dDemo - if (squaredDistance>previousSquaredDistance) - { - m_degenerateSimplex = 7; - squaredDistance = previousSquaredDistance; - checkSimplex = false; - break; - } + ///warning: this termination condition leads to some problems in 2d test case see Bullet/Demos/Box2dDemo + if (squaredDistance > previousSquaredDistance) + { + m_degenerateSimplex = 7; + squaredDistance = previousSquaredDistance; + checkSimplex = false; + break; + } #endif // - - //redundant m_simplexSolver->compute_points(pointOnA, pointOnB); - //are we getting any closer ? - if (previousSquaredDistance - squaredDistance <= SIMD_EPSILON * previousSquaredDistance) - { -// m_simplexSolver->backup_closest(m_cachedSeparatingAxis); - checkSimplex = true; - m_degenerateSimplex = 12; - - break; - } + //redundant m_simplexSolver->compute_points(pointOnA, pointOnB); - m_cachedSeparatingAxis = newCachedSeparatingAxis; + //are we getting any closer ? + if (previousSquaredDistance - squaredDistance <= SIMD_EPSILON * previousSquaredDistance) + { + // m_simplexSolver->backup_closest(m_cachedSeparatingAxis); + checkSimplex = true; + m_degenerateSimplex = 12; + + break; + } - //degeneracy, this is typically due to invalid/uninitialized worldtransforms for a btCollisionObject - if (m_curIter++ > gGjkMaxIter) - { - #if defined(DEBUG) || defined (_DEBUG) + m_cachedSeparatingAxis = newCachedSeparatingAxis; - printf("btGjkPairDetector maxIter exceeded:%i\n",m_curIter); - printf("sepAxis=(%f,%f,%f), squaredDistance = %f, shapeTypeA=%i,shapeTypeB=%i\n", - m_cachedSeparatingAxis.getX(), - m_cachedSeparatingAxis.getY(), - m_cachedSeparatingAxis.getZ(), - squaredDistance, - m_minkowskiA->getShapeType(), - m_minkowskiB->getShapeType()); + //degeneracy, this is typically due to invalid/uninitialized worldtransforms for a btCollisionObject + if (m_curIter++ > gGjkMaxIter) + { +#if defined(DEBUG) || defined (_DEBUG) - #endif - break; + printf("btGjkPairDetector maxIter exceeded:%i\n", m_curIter); + printf("sepAxis=(%f,%f,%f), squaredDistance = %f, shapeTypeA=%i,shapeTypeB=%i\n", + m_cachedSeparatingAxis.getX(), + m_cachedSeparatingAxis.getY(), + m_cachedSeparatingAxis.getZ(), + squaredDistance, + m_minkowskiA->getShapeType(), + m_minkowskiB->getShapeType()); - } +#endif + break; + } - bool check = (!m_simplexSolver->fullSimplex()); - //bool check = (!m_simplexSolver->fullSimplex() && squaredDistance > SIMD_EPSILON * m_simplexSolver->maxVertex()); - if (!check) - { - //do we need this backup_closest here ? -// m_simplexSolver->backup_closest(m_cachedSeparatingAxis); - m_degenerateSimplex = 13; - break; + bool check = (!m_simplexSolver->fullSimplex()); + //bool check = (!m_simplexSolver->fullSimplex() && squaredDistance > SIMD_EPSILON * m_simplexSolver->maxVertex()); + + if (!check) + { + //do we need this backup_closest here ? + // m_simplexSolver->backup_closest(m_cachedSeparatingAxis); + m_degenerateSimplex = 13; + break; + } } - } - if (checkSimplex) - { + if (checkSimplex) + { m_simplexSolver->compute_points(pointOnA, pointOnB); normalInB = m_cachedSeparatingAxis; btScalar lenSqr =m_cachedSeparatingAxis.length2(); - //valid normal - if (lenSqr < REL_ERROR2) - { - m_degenerateSimplex = 5; - } - if (lenSqr > SIMD_EPSILON*SIMD_EPSILON) - { - btScalar rlen = btScalar(1.) / btSqrt(lenSqr ); - normalInB *= rlen; //normalize + //valid normal + if (lenSqr < REL_ERROR2) + { + m_degenerateSimplex = 5; + } + if (lenSqr > SIMD_EPSILON*SIMD_EPSILON) + { + btScalar rlen = btScalar(1.) / btSqrt(lenSqr); + normalInB *= rlen; //normalize - btScalar s = btSqrt(squaredDistance); - - btAssert(s > btScalar(0.0)); - pointOnA -= m_cachedSeparatingAxis * (marginA / s); - pointOnB += m_cachedSeparatingAxis * (marginB / s); - distance = ((btScalar(1.)/rlen) - margin); - isValid = true; - - m_lastUsedMethod = 1; - } else - { - m_lastUsedMethod = 2; + btScalar s = btSqrt(squaredDistance); + + btAssert(s > btScalar(0.0)); + pointOnA -= m_cachedSeparatingAxis * (marginA / s); + pointOnB += m_cachedSeparatingAxis * (marginB / s); + distance = ((btScalar(1.) / rlen) - margin); + isValid = true; + orgNormalInB = normalInB; + + m_lastUsedMethod = 1; + } + else + { + m_lastUsedMethod = 2; + } } } + + bool catchDegeneratePenetrationCase = (m_catchDegeneracies && m_penetrationDepthSolver && m_degenerateSimplex && ((distance+margin) < gGjkEpaPenetrationTolerance)); //if (checkPenetration && !isValid) - if (checkPenetration && (!isValid || catchDegeneratePenetrationCase )) + if ((checkPenetration && (!isValid || catchDegeneratePenetrationCase )) || (status == 0)) { //penetration case @@ -331,70 +1033,79 @@ void btGjkPairDetector::getClosestPointsNonVirtual(const ClosestPointInput& inpu ); - if (isValid2) + if (m_cachedSeparatingAxis.length2()) { - btVector3 tmpNormalInB = tmpPointOnB-tmpPointOnA; - btScalar lenSqr = tmpNormalInB.length2(); - if (lenSqr <= (SIMD_EPSILON*SIMD_EPSILON)) + if (isValid2) { - tmpNormalInB = m_cachedSeparatingAxis; - lenSqr = m_cachedSeparatingAxis.length2(); - } + btVector3 tmpNormalInB = tmpPointOnB - tmpPointOnA; + btScalar lenSqr = tmpNormalInB.length2(); + if (lenSqr <= (SIMD_EPSILON*SIMD_EPSILON)) + { + tmpNormalInB = m_cachedSeparatingAxis; + lenSqr = m_cachedSeparatingAxis.length2(); + } - if (lenSqr > (SIMD_EPSILON*SIMD_EPSILON)) - { - tmpNormalInB /= btSqrt(lenSqr); - btScalar distance2 = -(tmpPointOnA-tmpPointOnB).length(); - m_lastUsedMethod = 3; - //only replace valid penetrations when the result is deeper (check) - if (!isValid || (distance2 < distance)) + if (lenSqr > (SIMD_EPSILON*SIMD_EPSILON)) { - distance = distance2; - pointOnA = tmpPointOnA; - pointOnB = tmpPointOnB; - normalInB = tmpNormalInB; - - isValid = true; - - } else + tmpNormalInB /= btSqrt(lenSqr); + btScalar distance2 = -(tmpPointOnA - tmpPointOnB).length(); + m_lastUsedMethod = 3; + //only replace valid penetrations when the result is deeper (check) + if (!isValid || (distance2 < distance)) + { + distance = distance2; + pointOnA = tmpPointOnA; + pointOnB = tmpPointOnB; + normalInB = tmpNormalInB; + isValid = true; + + } + else + { + m_lastUsedMethod = 8; + } + } + else { - m_lastUsedMethod = 8; + m_lastUsedMethod = 9; } - } else - { - m_lastUsedMethod = 9; } - } else - - { - ///this is another degenerate case, where the initial GJK calculation reports a degenerate case - ///EPA reports no penetration, and the second GJK (using the supporting vector without margin) - ///reports a valid positive distance. Use the results of the second GJK instead of failing. - ///thanks to Jacob.Langford for the reproduction case - ///http://code.google.com/p/bullet/issues/detail?id=250 + else - - if (m_cachedSeparatingAxis.length2() > btScalar(0.)) { - btScalar distance2 = (tmpPointOnA-tmpPointOnB).length()-margin; - //only replace valid distances when the distance is less - if (!isValid || (distance2 < distance)) - { - distance = distance2; - pointOnA = tmpPointOnA; - pointOnB = tmpPointOnB; - pointOnA -= m_cachedSeparatingAxis * marginA ; - pointOnB += m_cachedSeparatingAxis * marginB ; - normalInB = m_cachedSeparatingAxis; - normalInB.normalize(); - - isValid = true; - m_lastUsedMethod = 6; - } else + ///this is another degenerate case, where the initial GJK calculation reports a degenerate case + ///EPA reports no penetration, and the second GJK (using the supporting vector without margin) + ///reports a valid positive distance. Use the results of the second GJK instead of failing. + ///thanks to Jacob.Langford for the reproduction case + ///http://code.google.com/p/bullet/issues/detail?id=250 + + + if (m_cachedSeparatingAxis.length2() > btScalar(0.)) { - m_lastUsedMethod = 5; + btScalar distance2 = (tmpPointOnA - tmpPointOnB).length() - margin; + //only replace valid distances when the distance is less + if (!isValid || (distance2 < distance)) + { + distance = distance2; + pointOnA = tmpPointOnA; + pointOnB = tmpPointOnB; + pointOnA -= m_cachedSeparatingAxis * marginA; + pointOnB += m_cachedSeparatingAxis * marginB; + normalInB = m_cachedSeparatingAxis; + normalInB.normalize(); + + isValid = true; + m_lastUsedMethod = 6; + } + else + { + m_lastUsedMethod = 5; + } } } + } else + { + //printf("EPA didn't return a valid value\n"); } } @@ -409,17 +1120,33 @@ void btGjkPairDetector::getClosestPointsNonVirtual(const ClosestPointInput& inpu m_cachedSeparatingAxis = normalInB; m_cachedSeparatingDistance = distance; - + if (1) { ///todo: need to track down this EPA penetration solver degeneracy ///the penetration solver reports penetration but the contact normal ///connecting the contact points is pointing in the opposite direction ///until then, detect the issue and revert the normal + btScalar d2 = 0.f; + { + btVector3 seperatingAxisInA = (-orgNormalInB)* localTransA.getBasis(); + btVector3 seperatingAxisInB = orgNormalInB* localTransB.getBasis(); + + + btVector3 pInA = m_minkowskiA->localGetSupportVertexWithoutMarginNonVirtual(seperatingAxisInA); + btVector3 qInB = m_minkowskiB->localGetSupportVertexWithoutMarginNonVirtual(seperatingAxisInB); + + btVector3 pWorld = localTransA(pInA); + btVector3 qWorld = localTransB(qInB); + btVector3 w = pWorld - qWorld; + d2 = orgNormalInB.dot(w)- margin; + } + btScalar d1=0; { - btVector3 seperatingAxisInA = (normalInB)* input.m_transformA.getBasis(); - btVector3 seperatingAxisInB = -normalInB* input.m_transformB.getBasis(); + + btVector3 seperatingAxisInA = (normalInB)* localTransA.getBasis(); + btVector3 seperatingAxisInB = -normalInB* localTransB.getBasis(); btVector3 pInA = m_minkowskiA->localGetSupportVertexWithoutMarginNonVirtual(seperatingAxisInA); @@ -428,7 +1155,8 @@ void btGjkPairDetector::getClosestPointsNonVirtual(const ClosestPointInput& inpu btVector3 pWorld = localTransA(pInA); btVector3 qWorld = localTransB(qInB); btVector3 w = pWorld - qWorld; - d1 = (-normalInB).dot(w); + d1 = (-normalInB).dot(w)- margin; + } btScalar d0 = 0.f; { @@ -442,21 +1170,37 @@ void btGjkPairDetector::getClosestPointsNonVirtual(const ClosestPointInput& inpu btVector3 pWorld = localTransA(pInA); btVector3 qWorld = localTransB(qInB); btVector3 w = pWorld - qWorld; - d0 = normalInB.dot(w); + d0 = normalInB.dot(w)-margin; } + if (d1>d0) { m_lastUsedMethod = 10; normalInB*=-1; } + if (orgNormalInB.length2()) + { + if (d2 > d0 && d2 > d1 && d2 > distance) + { + + normalInB = orgNormalInB; + distance = d2; + } + } } + + output.addContactPoint( normalInB, pointOnB+positionOffset, distance); } + else + { + //printf("invalid gjk query\n"); + } } diff --git a/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btPersistentManifold.cpp b/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btPersistentManifold.cpp index 23aaece22b..9603a8bbdc 100644 --- a/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btPersistentManifold.cpp +++ b/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btPersistentManifold.cpp @@ -16,7 +16,13 @@ subject to the following restrictions: #include "btPersistentManifold.h" #include "LinearMath/btTransform.h" +#include "LinearMath/btSerializer.h" +#ifdef BT_USE_DOUBLE_PRECISION +#define btCollisionObjectData btCollisionObjectDoubleData +#else +#define btCollisionObjectData btCollisionObjectFloatData +#endif btScalar gContactBreakingThreshold = btScalar(0.02); ContactDestroyedCallback gContactDestroyedCallback = 0; @@ -33,6 +39,8 @@ btPersistentManifold::btPersistentManifold() m_body0(0), m_body1(0), m_cachedPoints (0), +m_companionIdA(0), +m_companionIdB(0), m_index1a(0) { } @@ -303,6 +311,149 @@ void btPersistentManifold::refreshContactPoints(const btTransform& trA,const btT } +int btPersistentManifold::calculateSerializeBufferSize() const +{ + return sizeof(btPersistentManifoldData); +} + +const char* btPersistentManifold::serialize(const class btPersistentManifold* manifold, void* dataBuffer, class btSerializer* serializer) const +{ + btPersistentManifoldData* dataOut = (btPersistentManifoldData*)dataBuffer; + memset(dataOut, 0, sizeof(btPersistentManifoldData)); + + dataOut->m_body0 = (btCollisionObjectData*)serializer->getUniquePointer((void*)manifold->getBody0()); + dataOut->m_body1 = (btCollisionObjectData*)serializer->getUniquePointer((void*)manifold->getBody1()); + dataOut->m_contactBreakingThreshold = manifold->getContactBreakingThreshold(); + dataOut->m_contactProcessingThreshold = manifold->getContactProcessingThreshold(); + dataOut->m_numCachedPoints = manifold->getNumContacts(); + dataOut->m_companionIdA = manifold->m_companionIdA; + dataOut->m_companionIdB = manifold->m_companionIdB; + dataOut->m_index1a = manifold->m_index1a; + dataOut->m_objectType = manifold->m_objectType; + + for (int i = 0; i < this->getNumContacts(); i++) + { + const btManifoldPoint& pt = manifold->getContactPoint(i); + dataOut->m_pointCacheAppliedImpulse[i] = pt.m_appliedImpulse; + dataOut->m_pointCacheAppliedImpulseLateral1[i] = pt.m_appliedImpulseLateral1; + dataOut->m_pointCacheAppliedImpulseLateral2[i] = pt.m_appliedImpulseLateral2; + pt.m_localPointA.serialize(dataOut->m_pointCacheLocalPointA[i]); + pt.m_localPointB.serialize(dataOut->m_pointCacheLocalPointB[i]); + pt.m_normalWorldOnB.serialize(dataOut->m_pointCacheNormalWorldOnB[i]); + dataOut->m_pointCacheDistance[i] = pt.m_distance1; + dataOut->m_pointCacheCombinedContactDamping1[i] = pt.m_combinedContactDamping1; + dataOut->m_pointCacheCombinedContactStiffness1[i] = pt.m_combinedContactStiffness1; + dataOut->m_pointCacheLifeTime[i] = pt.m_lifeTime; + dataOut->m_pointCacheFrictionCFM[i] = pt.m_frictionCFM; + dataOut->m_pointCacheContactERP[i] = pt.m_contactERP; + dataOut->m_pointCacheContactCFM[i] = pt.m_contactCFM; + dataOut->m_pointCacheContactPointFlags[i] = pt.m_contactPointFlags; + dataOut->m_pointCacheIndex0[i] = pt.m_index0; + dataOut->m_pointCacheIndex1[i] = pt.m_index1; + dataOut->m_pointCachePartId0[i] = pt.m_partId0; + dataOut->m_pointCachePartId1[i] = pt.m_partId1; + pt.m_positionWorldOnA.serialize(dataOut->m_pointCachePositionWorldOnA[i]); + pt.m_positionWorldOnB.serialize(dataOut->m_pointCachePositionWorldOnB[i]); + dataOut->m_pointCacheCombinedFriction[i] = pt.m_combinedFriction; + pt.m_lateralFrictionDir1.serialize(dataOut->m_pointCacheLateralFrictionDir1[i]); + pt.m_lateralFrictionDir2.serialize(dataOut->m_pointCacheLateralFrictionDir2[i]); + dataOut->m_pointCacheCombinedRollingFriction[i] = pt.m_combinedRollingFriction; + dataOut->m_pointCacheCombinedSpinningFriction[i] = pt.m_combinedSpinningFriction; + dataOut->m_pointCacheCombinedRestitution[i] = pt.m_combinedRestitution; + dataOut->m_pointCacheContactMotion1[i] = pt.m_contactMotion1; + dataOut->m_pointCacheContactMotion2[i] = pt.m_contactMotion2; + } + return btPersistentManifoldDataName; +} + +void btPersistentManifold::deSerialize(const struct btPersistentManifoldDoubleData* manifoldDataPtr) +{ + m_contactBreakingThreshold = manifoldDataPtr->m_contactBreakingThreshold; + m_contactProcessingThreshold = manifoldDataPtr->m_contactProcessingThreshold; + m_cachedPoints = manifoldDataPtr->m_numCachedPoints; + m_companionIdA = manifoldDataPtr->m_companionIdA; + m_companionIdB = manifoldDataPtr->m_companionIdB; + //m_index1a = manifoldDataPtr->m_index1a; + m_objectType = manifoldDataPtr->m_objectType; + + for (int i = 0; i < this->getNumContacts(); i++) + { + btManifoldPoint& pt = m_pointCache[i]; + + pt.m_appliedImpulse = manifoldDataPtr->m_pointCacheAppliedImpulse[i]; + pt.m_appliedImpulseLateral1 = manifoldDataPtr->m_pointCacheAppliedImpulseLateral1[i]; + pt.m_appliedImpulseLateral2 = manifoldDataPtr->m_pointCacheAppliedImpulseLateral2[i]; + pt.m_localPointA.deSerializeDouble(manifoldDataPtr->m_pointCacheLocalPointA[i]); + pt.m_localPointB.deSerializeDouble(manifoldDataPtr->m_pointCacheLocalPointB[i]); + pt.m_normalWorldOnB.deSerializeDouble(manifoldDataPtr->m_pointCacheNormalWorldOnB[i]); + pt.m_distance1 = manifoldDataPtr->m_pointCacheDistance[i]; + pt.m_combinedContactDamping1 = manifoldDataPtr->m_pointCacheCombinedContactDamping1[i]; + pt.m_combinedContactStiffness1 = manifoldDataPtr->m_pointCacheCombinedContactStiffness1[i]; + pt.m_lifeTime = manifoldDataPtr->m_pointCacheLifeTime[i]; + pt.m_frictionCFM = manifoldDataPtr->m_pointCacheFrictionCFM[i]; + pt.m_contactERP = manifoldDataPtr->m_pointCacheContactERP[i]; + pt.m_contactCFM = manifoldDataPtr->m_pointCacheContactCFM[i]; + pt.m_contactPointFlags = manifoldDataPtr->m_pointCacheContactPointFlags[i]; + pt.m_index0 = manifoldDataPtr->m_pointCacheIndex0[i]; + pt.m_index1 = manifoldDataPtr->m_pointCacheIndex1[i]; + pt.m_partId0 = manifoldDataPtr->m_pointCachePartId0[i]; + pt.m_partId1 = manifoldDataPtr->m_pointCachePartId1[i]; + pt.m_positionWorldOnA.deSerializeDouble(manifoldDataPtr->m_pointCachePositionWorldOnA[i]); + pt.m_positionWorldOnB.deSerializeDouble(manifoldDataPtr->m_pointCachePositionWorldOnB[i]); + pt.m_combinedFriction = manifoldDataPtr->m_pointCacheCombinedFriction[i]; + pt.m_lateralFrictionDir1.deSerializeDouble(manifoldDataPtr->m_pointCacheLateralFrictionDir1[i]); + pt.m_lateralFrictionDir2.deSerializeDouble(manifoldDataPtr->m_pointCacheLateralFrictionDir2[i]); + pt.m_combinedRollingFriction = manifoldDataPtr->m_pointCacheCombinedRollingFriction[i]; + pt.m_combinedSpinningFriction = manifoldDataPtr->m_pointCacheCombinedSpinningFriction[i]; + pt.m_combinedRestitution = manifoldDataPtr->m_pointCacheCombinedRestitution[i]; + pt.m_contactMotion1 = manifoldDataPtr->m_pointCacheContactMotion1[i]; + pt.m_contactMotion2 = manifoldDataPtr->m_pointCacheContactMotion2[i]; + } +} +void btPersistentManifold::deSerialize(const struct btPersistentManifoldFloatData* manifoldDataPtr) +{ + m_contactBreakingThreshold = manifoldDataPtr->m_contactBreakingThreshold; + m_contactProcessingThreshold = manifoldDataPtr->m_contactProcessingThreshold; + m_cachedPoints = manifoldDataPtr->m_numCachedPoints; + m_companionIdA = manifoldDataPtr->m_companionIdA; + m_companionIdB = manifoldDataPtr->m_companionIdB; + //m_index1a = manifoldDataPtr->m_index1a; + m_objectType = manifoldDataPtr->m_objectType; + + for (int i = 0; i < this->getNumContacts(); i++) + { + btManifoldPoint& pt = m_pointCache[i]; + + pt.m_appliedImpulse = manifoldDataPtr->m_pointCacheAppliedImpulse[i]; + pt.m_appliedImpulseLateral1 = manifoldDataPtr->m_pointCacheAppliedImpulseLateral1[i]; + pt.m_appliedImpulseLateral2 = manifoldDataPtr->m_pointCacheAppliedImpulseLateral2[i]; + pt.m_localPointA.deSerialize(manifoldDataPtr->m_pointCacheLocalPointA[i]); + pt.m_localPointB.deSerialize(manifoldDataPtr->m_pointCacheLocalPointB[i]); + pt.m_normalWorldOnB.deSerialize(manifoldDataPtr->m_pointCacheNormalWorldOnB[i]); + pt.m_distance1 = manifoldDataPtr->m_pointCacheDistance[i]; + pt.m_combinedContactDamping1 = manifoldDataPtr->m_pointCacheCombinedContactDamping1[i]; + pt.m_combinedContactStiffness1 = manifoldDataPtr->m_pointCacheCombinedContactStiffness1[i]; + pt.m_lifeTime = manifoldDataPtr->m_pointCacheLifeTime[i]; + pt.m_frictionCFM = manifoldDataPtr->m_pointCacheFrictionCFM[i]; + pt.m_contactERP = manifoldDataPtr->m_pointCacheContactERP[i]; + pt.m_contactCFM = manifoldDataPtr->m_pointCacheContactCFM[i]; + pt.m_contactPointFlags = manifoldDataPtr->m_pointCacheContactPointFlags[i]; + pt.m_index0 = manifoldDataPtr->m_pointCacheIndex0[i]; + pt.m_index1 = manifoldDataPtr->m_pointCacheIndex1[i]; + pt.m_partId0 = manifoldDataPtr->m_pointCachePartId0[i]; + pt.m_partId1 = manifoldDataPtr->m_pointCachePartId1[i]; + pt.m_positionWorldOnA.deSerialize(manifoldDataPtr->m_pointCachePositionWorldOnA[i]); + pt.m_positionWorldOnB.deSerialize(manifoldDataPtr->m_pointCachePositionWorldOnB[i]); + pt.m_combinedFriction = manifoldDataPtr->m_pointCacheCombinedFriction[i]; + pt.m_lateralFrictionDir1.deSerialize(manifoldDataPtr->m_pointCacheLateralFrictionDir1[i]); + pt.m_lateralFrictionDir2.deSerialize(manifoldDataPtr->m_pointCacheLateralFrictionDir2[i]); + pt.m_combinedRollingFriction = manifoldDataPtr->m_pointCacheCombinedRollingFriction[i]; + pt.m_combinedSpinningFriction = manifoldDataPtr->m_pointCacheCombinedSpinningFriction[i]; + pt.m_combinedRestitution = manifoldDataPtr->m_pointCacheCombinedRestitution[i]; + pt.m_contactMotion1 = manifoldDataPtr->m_pointCacheContactMotion1[i]; + pt.m_contactMotion2 = manifoldDataPtr->m_pointCacheContactMotion2[i]; + } +}
\ No newline at end of file diff --git a/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btPersistentManifold.h b/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btPersistentManifold.h index f872c8e1c9..67be0c48eb 100644 --- a/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btPersistentManifold.h +++ b/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btPersistentManifold.h @@ -24,6 +24,8 @@ class btCollisionObject; #include "LinearMath/btAlignedAllocator.h" struct btCollisionResult; +struct btCollisionObjectDoubleData; +struct btCollisionObjectFloatData; ///maximum contact breaking and merging threshold extern btScalar gContactBreakingThreshold; @@ -95,7 +97,10 @@ public: : btTypedObject(BT_PERSISTENT_MANIFOLD_TYPE), m_body0(body0),m_body1(body1),m_cachedPoints(0), m_contactBreakingThreshold(contactBreakingThreshold), - m_contactProcessingThreshold(contactProcessingThreshold) + m_contactProcessingThreshold(contactProcessingThreshold), + m_companionIdA(0), + m_companionIdB(0), + m_index1a(0) { } @@ -256,10 +261,115 @@ public: m_cachedPoints = 0; } + int calculateSerializeBufferSize() const; + const char* serialize(const class btPersistentManifold* manifold, void* dataBuffer, class btSerializer* serializer) const; + void deSerialize(const struct btPersistentManifoldDoubleData* manifoldDataPtr); + void deSerialize(const struct btPersistentManifoldFloatData* manifoldDataPtr); -} -; +}; + + + +struct btPersistentManifoldDoubleData +{ + btVector3DoubleData m_pointCacheLocalPointA[4]; + btVector3DoubleData m_pointCacheLocalPointB[4]; + btVector3DoubleData m_pointCachePositionWorldOnA[4]; + btVector3DoubleData m_pointCachePositionWorldOnB[4]; + btVector3DoubleData m_pointCacheNormalWorldOnB[4]; + btVector3DoubleData m_pointCacheLateralFrictionDir1[4]; + btVector3DoubleData m_pointCacheLateralFrictionDir2[4]; + double m_pointCacheDistance[4]; + double m_pointCacheAppliedImpulse[4]; + double m_pointCacheCombinedFriction[4]; + double m_pointCacheCombinedRollingFriction[4]; + double m_pointCacheCombinedSpinningFriction[4]; + double m_pointCacheCombinedRestitution[4]; + int m_pointCachePartId0[4]; + int m_pointCachePartId1[4]; + int m_pointCacheIndex0[4]; + int m_pointCacheIndex1[4]; + int m_pointCacheContactPointFlags[4]; + double m_pointCacheAppliedImpulseLateral1[4]; + double m_pointCacheAppliedImpulseLateral2[4]; + double m_pointCacheContactMotion1[4]; + double m_pointCacheContactMotion2[4]; + double m_pointCacheContactCFM[4]; + double m_pointCacheCombinedContactStiffness1[4]; + double m_pointCacheContactERP[4]; + double m_pointCacheCombinedContactDamping1[4]; + double m_pointCacheFrictionCFM[4]; + int m_pointCacheLifeTime[4]; + + int m_numCachedPoints; + int m_companionIdA; + int m_companionIdB; + int m_index1a; + + int m_objectType; + double m_contactBreakingThreshold; + double m_contactProcessingThreshold; + int m_padding; + + btCollisionObjectDoubleData *m_body0; + btCollisionObjectDoubleData *m_body1; +}; + + +struct btPersistentManifoldFloatData +{ + btVector3FloatData m_pointCacheLocalPointA[4]; + btVector3FloatData m_pointCacheLocalPointB[4]; + btVector3FloatData m_pointCachePositionWorldOnA[4]; + btVector3FloatData m_pointCachePositionWorldOnB[4]; + btVector3FloatData m_pointCacheNormalWorldOnB[4]; + btVector3FloatData m_pointCacheLateralFrictionDir1[4]; + btVector3FloatData m_pointCacheLateralFrictionDir2[4]; + float m_pointCacheDistance[4]; + float m_pointCacheAppliedImpulse[4]; + float m_pointCacheCombinedFriction[4]; + float m_pointCacheCombinedRollingFriction[4]; + float m_pointCacheCombinedSpinningFriction[4]; + float m_pointCacheCombinedRestitution[4]; + int m_pointCachePartId0[4]; + int m_pointCachePartId1[4]; + int m_pointCacheIndex0[4]; + int m_pointCacheIndex1[4]; + int m_pointCacheContactPointFlags[4]; + float m_pointCacheAppliedImpulseLateral1[4]; + float m_pointCacheAppliedImpulseLateral2[4]; + float m_pointCacheContactMotion1[4]; + float m_pointCacheContactMotion2[4]; + float m_pointCacheContactCFM[4]; + float m_pointCacheCombinedContactStiffness1[4]; + float m_pointCacheContactERP[4]; + float m_pointCacheCombinedContactDamping1[4]; + float m_pointCacheFrictionCFM[4]; + int m_pointCacheLifeTime[4]; + + int m_numCachedPoints; + int m_companionIdA; + int m_companionIdB; + int m_index1a; + + int m_objectType; + float m_contactBreakingThreshold; + float m_contactProcessingThreshold; + int m_padding; + + btCollisionObjectFloatData *m_body0; + btCollisionObjectFloatData *m_body1; +}; + +#ifdef BT_USE_DOUBLE_PRECISION +#define btPersistentManifoldData btPersistentManifoldDoubleData +#define btPersistentManifoldDataName "btPersistentManifoldDoubleData" +#else +#define btPersistentManifoldData btPersistentManifoldFloatData +#define btPersistentManifoldDataName "btPersistentManifoldFloatData" +#endif //BT_USE_DOUBLE_PRECISION + diff --git a/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.cpp b/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.cpp index ec638f60ba..08d6e6de86 100644 --- a/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.cpp +++ b/thirdparty/bullet/BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.cpp @@ -72,11 +72,18 @@ bool btSubsimplexConvexCast::calcTimeOfImpact( btScalar dist2 = v.length2(); + #ifdef BT_USE_DOUBLE_PRECISION - btScalar epsilon = btScalar(0.0001); + btScalar epsilon = SIMD_EPSILON * 10; #else +//todo: epsilon kept for backward compatibility of unit tests. +//will need to digg deeper to make the algorithm more robust +//since, a large epsilon can cause an early termination with false +//positive results (ray intersections that shouldn't be there) btScalar epsilon = btScalar(0.0001); #endif //BT_USE_DOUBLE_PRECISION + + btVector3 w,p; btScalar VdotR; diff --git a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btBatchedConstraints.cpp b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btBatchedConstraints.cpp new file mode 100644 index 0000000000..c82ba87f9f --- /dev/null +++ b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btBatchedConstraints.cpp @@ -0,0 +1,1128 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + + +#include "btBatchedConstraints.h" + +#include "LinearMath/btIDebugDraw.h" +#include "LinearMath/btMinMax.h" +#include "LinearMath/btStackAlloc.h" +#include "LinearMath/btQuickprof.h" + +#include <string.h> //for memset + +const int kNoMerge = -1; + +bool btBatchedConstraints::s_debugDrawBatches = false; + + +struct btBatchedConstraintInfo +{ + int constraintIndex; + int numConstraintRows; + int bodyIds[2]; +}; + + +struct btBatchInfo +{ + int numConstraints; + int mergeIndex; + + btBatchInfo() : numConstraints(0), mergeIndex(kNoMerge) {} +}; + + +bool btBatchedConstraints::validate(btConstraintArray* constraints, const btAlignedObjectArray<btSolverBody>& bodies) const +{ + // + // validate: for debugging only. Verify coloring of bodies, that no body is touched by more than one batch in any given phase + // + int errors = 0; + const int kUnassignedBatch = -1; + + btAlignedObjectArray<int> bodyBatchId; + for (int iPhase = 0; iPhase < m_phases.size(); ++iPhase) + { + bodyBatchId.resizeNoInitialize(0); + bodyBatchId.resize( bodies.size(), kUnassignedBatch ); + const Range& phase = m_phases[iPhase]; + for (int iBatch = phase.begin; iBatch < phase.end; ++iBatch) + { + const Range& batch = m_batches[iBatch]; + for (int iiCons = batch.begin; iiCons < batch.end; ++iiCons) + { + int iCons = m_constraintIndices[iiCons]; + const btSolverConstraint& cons = constraints->at(iCons); + const btSolverBody& bodyA = bodies[cons.m_solverBodyIdA]; + const btSolverBody& bodyB = bodies[cons.m_solverBodyIdB]; + if (! bodyA.internalGetInvMass().isZero()) + { + int thisBodyBatchId = bodyBatchId[cons.m_solverBodyIdA]; + if (thisBodyBatchId == kUnassignedBatch) + { + bodyBatchId[cons.m_solverBodyIdA] = iBatch; + } + else if (thisBodyBatchId != iBatch) + { + btAssert( !"dynamic body is used in 2 different batches in the same phase" ); + errors++; + } + } + if (! bodyB.internalGetInvMass().isZero()) + { + int thisBodyBatchId = bodyBatchId[cons.m_solverBodyIdB]; + if (thisBodyBatchId == kUnassignedBatch) + { + bodyBatchId[cons.m_solverBodyIdB] = iBatch; + } + else if (thisBodyBatchId != iBatch) + { + btAssert( !"dynamic body is used in 2 different batches in the same phase" ); + errors++; + } + } + } + } + } + return errors == 0; +} + + +static void debugDrawSingleBatch( const btBatchedConstraints* bc, + btConstraintArray* constraints, + const btAlignedObjectArray<btSolverBody>& bodies, + int iBatch, + const btVector3& color, + const btVector3& offset + ) +{ + if (bc && bc->m_debugDrawer && iBatch < bc->m_batches.size()) + { + const btBatchedConstraints::Range& b = bc->m_batches[iBatch]; + for (int iiCon = b.begin; iiCon < b.end; ++iiCon) + { + int iCon = bc->m_constraintIndices[iiCon]; + const btSolverConstraint& con = constraints->at(iCon); + int iBody0 = con.m_solverBodyIdA; + int iBody1 = con.m_solverBodyIdB; + btVector3 pos0 = bodies[iBody0].getWorldTransform().getOrigin() + offset; + btVector3 pos1 = bodies[iBody1].getWorldTransform().getOrigin() + offset; + bc->m_debugDrawer->drawLine(pos0, pos1, color); + } + } +} + + +static void debugDrawPhase( const btBatchedConstraints* bc, + btConstraintArray* constraints, + const btAlignedObjectArray<btSolverBody>& bodies, + int iPhase, + const btVector3& color0, + const btVector3& color1, + const btVector3& offset + ) +{ + BT_PROFILE( "debugDrawPhase" ); + if ( bc && bc->m_debugDrawer && iPhase < bc->m_phases.size() ) + { + const btBatchedConstraints::Range& phase = bc->m_phases[iPhase]; + for (int iBatch = phase.begin; iBatch < phase.end; ++iBatch) + { + float tt = float(iBatch - phase.begin) / float(btMax(1, phase.end - phase.begin - 1)); + btVector3 col = lerp(color0, color1, tt); + debugDrawSingleBatch(bc, constraints, bodies, iBatch, col, offset); + } + } +} + + +static void debugDrawAllBatches( const btBatchedConstraints* bc, + btConstraintArray* constraints, + const btAlignedObjectArray<btSolverBody>& bodies + ) +{ + BT_PROFILE( "debugDrawAllBatches" ); + if ( bc && bc->m_debugDrawer && bc->m_phases.size() > 0 ) + { + btVector3 bboxMin(BT_LARGE_FLOAT, BT_LARGE_FLOAT, BT_LARGE_FLOAT); + btVector3 bboxMax = -bboxMin; + for (int iBody = 0; iBody < bodies.size(); ++iBody) + { + const btVector3& pos = bodies[iBody].getWorldTransform().getOrigin(); + bboxMin.setMin(pos); + bboxMax.setMax(pos); + } + btVector3 bboxExtent = bboxMax - bboxMin; + btVector3 offsetBase = btVector3( 0, bboxExtent.y()*1.1f, 0 ); + btVector3 offsetStep = btVector3( 0, 0, bboxExtent.z()*1.1f ); + int numPhases = bc->m_phases.size(); + for (int iPhase = 0; iPhase < numPhases; ++iPhase) + { + float b = float(iPhase)/float(numPhases-1); + btVector3 color0 = btVector3(1,0,b); + btVector3 color1 = btVector3(0,1,b); + btVector3 offset = offsetBase + offsetStep*(float(iPhase) - float(numPhases-1)*0.5); + debugDrawPhase(bc, constraints, bodies, iPhase, color0, color1, offset); + } + } +} + + +static void initBatchedBodyDynamicFlags(btAlignedObjectArray<bool>* outBodyDynamicFlags, const btAlignedObjectArray<btSolverBody>& bodies) +{ + BT_PROFILE("initBatchedBodyDynamicFlags"); + btAlignedObjectArray<bool>& bodyDynamicFlags = *outBodyDynamicFlags; + bodyDynamicFlags.resizeNoInitialize(bodies.size()); + for (int i = 0; i < bodies.size(); ++i) + { + const btSolverBody& body = bodies[ i ]; + bodyDynamicFlags[i] = ( body.internalGetInvMass().x() > btScalar( 0 ) ); + } +} + + +static int runLengthEncodeConstraintInfo(btBatchedConstraintInfo* outConInfos, int numConstraints) +{ + BT_PROFILE("runLengthEncodeConstraintInfo"); + // detect and run-length encode constraint rows that repeat the same bodies + int iDest = 0; + int iSrc = 0; + while (iSrc < numConstraints) + { + const btBatchedConstraintInfo& srcConInfo = outConInfos[iSrc]; + btBatchedConstraintInfo& conInfo = outConInfos[iDest]; + conInfo.constraintIndex = iSrc; + conInfo.bodyIds[0] = srcConInfo.bodyIds[0]; + conInfo.bodyIds[1] = srcConInfo.bodyIds[1]; + while (iSrc < numConstraints && outConInfos[iSrc].bodyIds[0] == srcConInfo.bodyIds[0] && outConInfos[iSrc].bodyIds[1] == srcConInfo.bodyIds[1]) + { + ++iSrc; + } + conInfo.numConstraintRows = iSrc - conInfo.constraintIndex; + ++iDest; + } + return iDest; +} + + +struct ReadSolverConstraintsLoop : public btIParallelForBody +{ + btBatchedConstraintInfo* m_outConInfos; + btConstraintArray* m_constraints; + + ReadSolverConstraintsLoop( btBatchedConstraintInfo* outConInfos, btConstraintArray* constraints ) + { + m_outConInfos = outConInfos; + m_constraints = constraints; + } + void forLoop( int iBegin, int iEnd ) const BT_OVERRIDE + { + for (int i = iBegin; i < iEnd; ++i) + { + btBatchedConstraintInfo& conInfo = m_outConInfos[i]; + const btSolverConstraint& con = m_constraints->at( i ); + conInfo.bodyIds[0] = con.m_solverBodyIdA; + conInfo.bodyIds[1] = con.m_solverBodyIdB; + conInfo.constraintIndex = i; + conInfo.numConstraintRows = 1; + } + } +}; + + +static int initBatchedConstraintInfo(btBatchedConstraintInfo* outConInfos, btConstraintArray* constraints) +{ + BT_PROFILE("initBatchedConstraintInfo"); + int numConstraints = constraints->size(); + bool inParallel = true; + if (inParallel) + { + ReadSolverConstraintsLoop loop(outConInfos, constraints); + int grainSize = 1200; + btParallelFor(0, numConstraints, grainSize, loop); + } + else + { + for (int i = 0; i < numConstraints; ++i) + { + btBatchedConstraintInfo& conInfo = outConInfos[i]; + const btSolverConstraint& con = constraints->at( i ); + conInfo.bodyIds[0] = con.m_solverBodyIdA; + conInfo.bodyIds[1] = con.m_solverBodyIdB; + conInfo.constraintIndex = i; + conInfo.numConstraintRows = 1; + } + } + bool useRunLengthEncoding = true; + if (useRunLengthEncoding) + { + numConstraints = runLengthEncodeConstraintInfo(outConInfos, numConstraints); + } + return numConstraints; +} + + +static void expandConstraintRowsInPlace(int* constraintBatchIds, const btBatchedConstraintInfo* conInfos, int numConstraints, int numConstraintRows) +{ + BT_PROFILE("expandConstraintRowsInPlace"); + if (numConstraintRows > numConstraints) + { + // we walk the array in reverse to avoid overwriteing + for (int iCon = numConstraints - 1; iCon >= 0; --iCon) + { + const btBatchedConstraintInfo& conInfo = conInfos[iCon]; + int iBatch = constraintBatchIds[iCon]; + for (int i = conInfo.numConstraintRows - 1; i >= 0; --i) + { + int iDest = conInfo.constraintIndex + i; + btAssert(iDest >= iCon); + btAssert(iDest >= 0 && iDest < numConstraintRows); + constraintBatchIds[iDest] = iBatch; + } + } + } +} + + +static void expandConstraintRows(int* destConstraintBatchIds, const int* srcConstraintBatchIds, const btBatchedConstraintInfo* conInfos, int numConstraints, int numConstraintRows) +{ + BT_PROFILE("expandConstraintRows"); + for ( int iCon = 0; iCon < numConstraints; ++iCon ) + { + const btBatchedConstraintInfo& conInfo = conInfos[ iCon ]; + int iBatch = srcConstraintBatchIds[ iCon ]; + for ( int i = 0; i < conInfo.numConstraintRows; ++i ) + { + int iDest = conInfo.constraintIndex + i; + btAssert( iDest >= iCon ); + btAssert( iDest >= 0 && iDest < numConstraintRows ); + destConstraintBatchIds[ iDest ] = iBatch; + } + } +} + + +struct ExpandConstraintRowsLoop : public btIParallelForBody +{ + int* m_destConstraintBatchIds; + const int* m_srcConstraintBatchIds; + const btBatchedConstraintInfo* m_conInfos; + int m_numConstraintRows; + + ExpandConstraintRowsLoop( int* destConstraintBatchIds, const int* srcConstraintBatchIds, const btBatchedConstraintInfo* conInfos, int numConstraintRows) + { + m_destConstraintBatchIds = destConstraintBatchIds; + m_srcConstraintBatchIds = srcConstraintBatchIds; + m_conInfos = conInfos; + m_numConstraintRows = numConstraintRows; + } + void forLoop( int iBegin, int iEnd ) const BT_OVERRIDE + { + expandConstraintRows(m_destConstraintBatchIds, m_srcConstraintBatchIds + iBegin, m_conInfos + iBegin, iEnd - iBegin, m_numConstraintRows); + } +}; + + +static void expandConstraintRowsMt(int* destConstraintBatchIds, const int* srcConstraintBatchIds, const btBatchedConstraintInfo* conInfos, int numConstraints, int numConstraintRows) +{ + BT_PROFILE("expandConstraintRowsMt"); + ExpandConstraintRowsLoop loop(destConstraintBatchIds, srcConstraintBatchIds, conInfos, numConstraintRows); + int grainSize = 600; + btParallelFor(0, numConstraints, grainSize, loop); +} + + +static void initBatchedConstraintInfoArray(btAlignedObjectArray<btBatchedConstraintInfo>* outConInfos, btConstraintArray* constraints) +{ + BT_PROFILE("initBatchedConstraintInfoArray"); + btAlignedObjectArray<btBatchedConstraintInfo>& conInfos = *outConInfos; + int numConstraints = constraints->size(); + conInfos.resizeNoInitialize(numConstraints); + + int newSize = initBatchedConstraintInfo(&outConInfos->at(0), constraints); + conInfos.resizeNoInitialize(newSize); +} + + +static void mergeSmallBatches(btBatchInfo* batches, int iBeginBatch, int iEndBatch, int minBatchSize, int maxBatchSize) +{ + BT_PROFILE("mergeSmallBatches"); + for ( int iBatch = iEndBatch - 1; iBatch >= iBeginBatch; --iBatch ) + { + btBatchInfo& batch = batches[ iBatch ]; + if ( batch.mergeIndex == kNoMerge && batch.numConstraints > 0 && batch.numConstraints < minBatchSize ) + { + for ( int iDestBatch = iBatch - 1; iDestBatch >= iBeginBatch; --iDestBatch ) + { + btBatchInfo& destBatch = batches[ iDestBatch ]; + if ( destBatch.mergeIndex == kNoMerge && ( destBatch.numConstraints + batch.numConstraints ) < maxBatchSize ) + { + destBatch.numConstraints += batch.numConstraints; + batch.numConstraints = 0; + batch.mergeIndex = iDestBatch; + break; + } + } + } + } + // flatten mergeIndexes + // e.g. in case where A was merged into B and then B was merged into C, we need A to point to C instead of B + // Note: loop goes forward through batches because batches always merge from higher indexes to lower, + // so by going from low to high it reduces the amount of trail-following + for ( int iBatch = iBeginBatch; iBatch < iEndBatch; ++iBatch ) + { + btBatchInfo& batch = batches[ iBatch ]; + if ( batch.mergeIndex != kNoMerge ) + { + int iMergeDest = batches[ batch.mergeIndex ].mergeIndex; + // follow trail of merges to the end + while ( iMergeDest != kNoMerge ) + { + int iNext = batches[ iMergeDest ].mergeIndex; + if ( iNext == kNoMerge ) + { + batch.mergeIndex = iMergeDest; + break; + } + iMergeDest = iNext; + } + } + } +} + + +static void updateConstraintBatchIdsForMerges(int* constraintBatchIds, int numConstraints, const btBatchInfo* batches, int numBatches) +{ + BT_PROFILE("updateConstraintBatchIdsForMerges"); + // update batchIds to account for merges + for (int i = 0; i < numConstraints; ++i) + { + int iBatch = constraintBatchIds[i]; + btAssert(iBatch < numBatches); + // if this constraint references a batch that was merged into another batch + if (batches[iBatch].mergeIndex != kNoMerge) + { + // update batchId + constraintBatchIds[i] = batches[iBatch].mergeIndex; + } + } +} + + +struct UpdateConstraintBatchIdsForMergesLoop : public btIParallelForBody +{ + int* m_constraintBatchIds; + const btBatchInfo* m_batches; + int m_numBatches; + + UpdateConstraintBatchIdsForMergesLoop( int* constraintBatchIds, const btBatchInfo* batches, int numBatches ) + { + m_constraintBatchIds = constraintBatchIds; + m_batches = batches; + m_numBatches = numBatches; + } + void forLoop( int iBegin, int iEnd ) const BT_OVERRIDE + { + BT_PROFILE( "UpdateConstraintBatchIdsForMergesLoop" ); + updateConstraintBatchIdsForMerges( m_constraintBatchIds + iBegin, iEnd - iBegin, m_batches, m_numBatches ); + } +}; + + +static void updateConstraintBatchIdsForMergesMt(int* constraintBatchIds, int numConstraints, const btBatchInfo* batches, int numBatches) +{ + BT_PROFILE( "updateConstraintBatchIdsForMergesMt" ); + UpdateConstraintBatchIdsForMergesLoop loop(constraintBatchIds, batches, numBatches); + int grainSize = 800; + btParallelFor(0, numConstraints, grainSize, loop); +} + + +inline bool BatchCompare(const btBatchedConstraints::Range& a, const btBatchedConstraints::Range& b) +{ + int lenA = a.end - a.begin; + int lenB = b.end - b.begin; + return lenA > lenB; +} + + +static void writeOutConstraintIndicesForRangeOfBatches(btBatchedConstraints* bc, + const int* constraintBatchIds, + int numConstraints, + int* constraintIdPerBatch, + int batchBegin, + int batchEnd + ) +{ + BT_PROFILE("writeOutConstraintIndicesForRangeOfBatches"); + for ( int iCon = 0; iCon < numConstraints; ++iCon ) + { + int iBatch = constraintBatchIds[ iCon ]; + if (iBatch >= batchBegin && iBatch < batchEnd) + { + int iDestCon = constraintIdPerBatch[ iBatch ]; + constraintIdPerBatch[ iBatch ] = iDestCon + 1; + bc->m_constraintIndices[ iDestCon ] = iCon; + } + } +} + + +struct WriteOutConstraintIndicesLoop : public btIParallelForBody +{ + btBatchedConstraints* m_batchedConstraints; + const int* m_constraintBatchIds; + int m_numConstraints; + int* m_constraintIdPerBatch; + int m_maxNumBatchesPerPhase; + + WriteOutConstraintIndicesLoop( btBatchedConstraints* bc, const int* constraintBatchIds, int numConstraints, int* constraintIdPerBatch, int maxNumBatchesPerPhase ) + { + m_batchedConstraints = bc; + m_constraintBatchIds = constraintBatchIds; + m_numConstraints = numConstraints; + m_constraintIdPerBatch = constraintIdPerBatch; + m_maxNumBatchesPerPhase = maxNumBatchesPerPhase; + } + void forLoop( int iBegin, int iEnd ) const BT_OVERRIDE + { + BT_PROFILE( "WriteOutConstraintIndicesLoop" ); + int batchBegin = iBegin * m_maxNumBatchesPerPhase; + int batchEnd = iEnd * m_maxNumBatchesPerPhase; + writeOutConstraintIndicesForRangeOfBatches(m_batchedConstraints, + m_constraintBatchIds, + m_numConstraints, + m_constraintIdPerBatch, + batchBegin, + batchEnd + ); + } +}; + + +static void writeOutConstraintIndicesMt(btBatchedConstraints* bc, + const int* constraintBatchIds, + int numConstraints, + int* constraintIdPerBatch, + int maxNumBatchesPerPhase, + int numPhases + ) +{ + BT_PROFILE("writeOutConstraintIndicesMt"); + bool inParallel = true; + if (inParallel) + { + WriteOutConstraintIndicesLoop loop( bc, constraintBatchIds, numConstraints, constraintIdPerBatch, maxNumBatchesPerPhase ); + btParallelFor( 0, numPhases, 1, loop ); + } + else + { + for ( int iCon = 0; iCon < numConstraints; ++iCon ) + { + int iBatch = constraintBatchIds[ iCon ]; + int iDestCon = constraintIdPerBatch[ iBatch ]; + constraintIdPerBatch[ iBatch ] = iDestCon + 1; + bc->m_constraintIndices[ iDestCon ] = iCon; + } + } +} + + +static void writeGrainSizes(btBatchedConstraints* bc) +{ + typedef btBatchedConstraints::Range Range; + int numPhases = bc->m_phases.size(); + bc->m_phaseGrainSize.resizeNoInitialize(numPhases); + int numThreads = btGetTaskScheduler()->getNumThreads(); + for (int iPhase = 0; iPhase < numPhases; ++iPhase) + { + const Range& phase = bc->m_phases[ iPhase ]; + int numBatches = phase.end - phase.begin; + float grainSize = floor((0.25f*numBatches / float(numThreads)) + 0.0f); + bc->m_phaseGrainSize[ iPhase ] = btMax(1, int(grainSize)); + } +} + + +static void writeOutBatches(btBatchedConstraints* bc, + const int* constraintBatchIds, + int numConstraints, + const btBatchInfo* batches, + int* batchWork, + int maxNumBatchesPerPhase, + int numPhases +) +{ + BT_PROFILE("writeOutBatches"); + typedef btBatchedConstraints::Range Range; + bc->m_constraintIndices.reserve( numConstraints ); + bc->m_batches.resizeNoInitialize( 0 ); + bc->m_phases.resizeNoInitialize( 0 ); + + //int maxNumBatches = numPhases * maxNumBatchesPerPhase; + { + int* constraintIdPerBatch = batchWork; // for each batch, keep an index into the next available slot in the m_constraintIndices array + int iConstraint = 0; + for (int iPhase = 0; iPhase < numPhases; ++iPhase) + { + int curPhaseBegin = bc->m_batches.size(); + int iBegin = iPhase * maxNumBatchesPerPhase; + int iEnd = iBegin + maxNumBatchesPerPhase; + for ( int i = iBegin; i < iEnd; ++i ) + { + const btBatchInfo& batch = batches[ i ]; + int curBatchBegin = iConstraint; + constraintIdPerBatch[ i ] = curBatchBegin; // record the start of each batch in m_constraintIndices array + int numConstraints = batch.numConstraints; + iConstraint += numConstraints; + if ( numConstraints > 0 ) + { + bc->m_batches.push_back( Range( curBatchBegin, iConstraint ) ); + } + } + // if any batches were emitted this phase, + if ( bc->m_batches.size() > curPhaseBegin ) + { + // output phase + bc->m_phases.push_back( Range( curPhaseBegin, bc->m_batches.size() ) ); + } + } + + btAssert(iConstraint == numConstraints); + bc->m_constraintIndices.resizeNoInitialize( numConstraints ); + writeOutConstraintIndicesMt( bc, constraintBatchIds, numConstraints, constraintIdPerBatch, maxNumBatchesPerPhase, numPhases ); + } + // for each phase + for (int iPhase = 0; iPhase < bc->m_phases.size(); ++iPhase) + { + // sort the batches from largest to smallest (can be helpful to some task schedulers) + const Range& curBatches = bc->m_phases[iPhase]; + bc->m_batches.quickSortInternal(BatchCompare, curBatches.begin, curBatches.end-1); + } + bc->m_phaseOrder.resize(bc->m_phases.size()); + for (int i = 0; i < bc->m_phases.size(); ++i) + { + bc->m_phaseOrder[i] = i; + } + writeGrainSizes(bc); +} + + +// +// PreallocatedMemoryHelper -- helper object for allocating a number of chunks of memory in a single contiguous block. +// It is generally more efficient to do a single larger allocation than many smaller allocations. +// +// Example Usage: +// +// btVector3* bodyPositions = NULL; +// btBatchedConstraintInfo* conInfos = NULL; +// { +// PreallocatedMemoryHelper<8> memHelper; +// memHelper.addChunk( (void**) &bodyPositions, sizeof( btVector3 ) * bodies.size() ); +// memHelper.addChunk( (void**) &conInfos, sizeof( btBatchedConstraintInfo ) * numConstraints ); +// void* memPtr = malloc( memHelper.getSizeToAllocate() ); // allocate the memory +// memHelper.setChunkPointers( memPtr ); // update pointers to chunks +// } +template <int N> +class PreallocatedMemoryHelper +{ + struct Chunk + { + void** ptr; + size_t size; + }; + Chunk m_chunks[N]; + int m_numChunks; +public: + PreallocatedMemoryHelper() {m_numChunks=0;} + void addChunk( void** ptr, size_t sz ) + { + btAssert( m_numChunks < N ); + if ( m_numChunks < N ) + { + Chunk& chunk = m_chunks[ m_numChunks ]; + chunk.ptr = ptr; + chunk.size = sz; + m_numChunks++; + } + } + size_t getSizeToAllocate() const + { + size_t totalSize = 0; + for (int i = 0; i < m_numChunks; ++i) + { + totalSize += m_chunks[i].size; + } + return totalSize; + } + void setChunkPointers(void* mem) const + { + size_t totalSize = 0; + for (int i = 0; i < m_numChunks; ++i) + { + const Chunk& chunk = m_chunks[ i ]; + char* chunkPtr = static_cast<char*>(mem) + totalSize; + *chunk.ptr = chunkPtr; + totalSize += chunk.size; + } + } +}; + + + +static btVector3 findMaxDynamicConstraintExtent( + btVector3* bodyPositions, + bool* bodyDynamicFlags, + btBatchedConstraintInfo* conInfos, + int numConstraints, + int numBodies + ) +{ + BT_PROFILE("findMaxDynamicConstraintExtent"); + btVector3 consExtent = btVector3(1,1,1) * 0.001; + for (int iCon = 0; iCon < numConstraints; ++iCon) + { + const btBatchedConstraintInfo& con = conInfos[ iCon ]; + int iBody0 = con.bodyIds[0]; + int iBody1 = con.bodyIds[1]; + btAssert(iBody0 >= 0 && iBody0 < numBodies); + btAssert(iBody1 >= 0 && iBody1 < numBodies); + // is it a dynamic constraint? + if (bodyDynamicFlags[iBody0] && bodyDynamicFlags[iBody1]) + { + btVector3 delta = bodyPositions[iBody1] - bodyPositions[iBody0]; + consExtent.setMax(delta.absolute()); + } + } + return consExtent; +} + + +struct btIntVec3 +{ + int m_ints[ 3 ]; + + SIMD_FORCE_INLINE const int& operator[](int i) const {return m_ints[i];} + SIMD_FORCE_INLINE int& operator[](int i) {return m_ints[i];} +}; + + +struct AssignConstraintsToGridBatchesParams +{ + bool* bodyDynamicFlags; + btIntVec3* bodyGridCoords; + int numBodies; + btBatchedConstraintInfo* conInfos; + int* constraintBatchIds; + btIntVec3 gridChunkDim; + int maxNumBatchesPerPhase; + int numPhases; + int phaseMask; + + AssignConstraintsToGridBatchesParams() + { + memset(this, 0, sizeof(*this)); + } +}; + + +static void assignConstraintsToGridBatches(const AssignConstraintsToGridBatchesParams& params, int iConBegin, int iConEnd) +{ + BT_PROFILE("assignConstraintsToGridBatches"); + // (can be done in parallel) + for ( int iCon = iConBegin; iCon < iConEnd; ++iCon ) + { + const btBatchedConstraintInfo& con = params.conInfos[ iCon ]; + int iBody0 = con.bodyIds[ 0 ]; + int iBody1 = con.bodyIds[ 1 ]; + int iPhase = iCon; //iBody0; // pseudorandom choice to distribute evenly amongst phases + iPhase &= params.phaseMask; + int gridCoord[ 3 ]; + // is it a dynamic constraint? + if ( params.bodyDynamicFlags[ iBody0 ] && params.bodyDynamicFlags[ iBody1 ] ) + { + const btIntVec3& body0Coords = params.bodyGridCoords[iBody0]; + const btIntVec3& body1Coords = params.bodyGridCoords[iBody1]; + // for each dimension x,y,z, + for (int i = 0; i < 3; ++i) + { + int coordMin = btMin(body0Coords.m_ints[i], body1Coords.m_ints[i]); + int coordMax = btMax(body0Coords.m_ints[i], body1Coords.m_ints[i]); + if (coordMin != coordMax) + { + btAssert( coordMax == coordMin + 1 ); + if ((coordMin&1) == 0) + { + iPhase &= ~(1 << i); // force bit off + } + else + { + iPhase |= (1 << i); // force bit on + iPhase &= params.phaseMask; + } + } + gridCoord[ i ] = coordMin; + } + } + else + { + if ( !params.bodyDynamicFlags[ iBody0 ] ) + { + iBody0 = con.bodyIds[ 1 ]; + } + btAssert(params.bodyDynamicFlags[ iBody0 ]); + const btIntVec3& body0Coords = params.bodyGridCoords[iBody0]; + // for each dimension x,y,z, + for ( int i = 0; i < 3; ++i ) + { + gridCoord[ i ] = body0Coords.m_ints[ i ]; + } + } + // calculate chunk coordinates + int chunkCoord[ 3 ]; + btIntVec3 gridChunkDim = params.gridChunkDim; + // for each dimension x,y,z, + for ( int i = 0; i < 3; ++i ) + { + int coordOffset = ( iPhase >> i ) & 1; + chunkCoord[ i ] = (gridCoord[ i ] - coordOffset)/2; + btClamp( chunkCoord[ i ], 0, gridChunkDim[ i ] - 1); + btAssert( chunkCoord[ i ] < gridChunkDim[ i ] ); + } + int iBatch = iPhase * params.maxNumBatchesPerPhase + chunkCoord[ 0 ] + chunkCoord[ 1 ] * gridChunkDim[ 0 ] + chunkCoord[ 2 ] * gridChunkDim[ 0 ] * gridChunkDim[ 1 ]; + btAssert(iBatch >= 0 && iBatch < params.maxNumBatchesPerPhase*params.numPhases); + params.constraintBatchIds[ iCon ] = iBatch; + } +} + + +struct AssignConstraintsToGridBatchesLoop : public btIParallelForBody +{ + const AssignConstraintsToGridBatchesParams* m_params; + + AssignConstraintsToGridBatchesLoop( const AssignConstraintsToGridBatchesParams& params ) + { + m_params = ¶ms; + } + void forLoop( int iBegin, int iEnd ) const BT_OVERRIDE + { + assignConstraintsToGridBatches(*m_params, iBegin, iEnd); + } +}; + + +// +// setupSpatialGridBatchesMt -- generate batches using a uniform 3D grid +// +/* + +Bodies are treated as 3D points at their center of mass. We only consider dynamic bodies at this stage, +because only dynamic bodies are mutated when a constraint is solved, thus subject to race conditions. + +1. Compute a bounding box around all dynamic bodies +2. Compute the maximum extent of all dynamic constraints. Each dynamic constraint is treated as a line segment, and we need the size of + box that will fully enclose any single dynamic constraint + +3. Establish the cell size of our grid, the cell size in each dimension must be at least as large as the dynamic constraints max-extent, + so that no dynamic constraint can span more than 2 cells of our grid on any axis of the grid. The cell size should be adjusted + larger in order to keep the total number of cells from being excessively high + +Key idea: Given that each constraint spans 1 or 2 grid cells in each dimension, we can handle all constraints by processing + in chunks of 2x2x2 cells with 8 different 1-cell offsets ((0,0,0),(0,0,1),(0,1,0),(0,1,1),(1,0,0)...). + For each of the 8 offsets, we create a phase, and for each 2x2x2 chunk with dynamic constraints becomes a batch in that phase. + +4. Once the grid is established, we can calculate for each constraint which phase and batch it belongs in. + +5. Do a merge small batches on the batches of each phase separately, to try to even out the sizes of batches + +Optionally, we can "collapse" one dimension of our 3D grid to turn it into a 2D grid, which reduces the number of phases +to 4. With fewer phases, there are more constraints per phase and this makes it easier to create batches of a useful size. +*/ +// +static void setupSpatialGridBatchesMt( + btBatchedConstraints* batchedConstraints, + btAlignedObjectArray<char>* scratchMemory, + btConstraintArray* constraints, + const btAlignedObjectArray<btSolverBody>& bodies, + int minBatchSize, + int maxBatchSize, + bool use2DGrid +) +{ + BT_PROFILE("setupSpatialGridBatchesMt"); + const int numPhases = 8; + int numConstraints = constraints->size(); + int numConstraintRows = constraints->size(); + + const int maxGridChunkCount = 128; + int allocNumBatchesPerPhase = maxGridChunkCount; + int minNumBatchesPerPhase = 16; + int allocNumBatches = allocNumBatchesPerPhase * numPhases; + + btVector3* bodyPositions = NULL; + bool* bodyDynamicFlags = NULL; + btIntVec3* bodyGridCoords = NULL; + btBatchInfo* batches = NULL; + int* batchWork = NULL; + btBatchedConstraintInfo* conInfos = NULL; + int* constraintBatchIds = NULL; + int* constraintRowBatchIds = NULL; + { + PreallocatedMemoryHelper<10> memHelper; + memHelper.addChunk( (void**) &bodyPositions, sizeof( btVector3 ) * bodies.size() ); + memHelper.addChunk( (void**) &bodyDynamicFlags, sizeof( bool ) * bodies.size() ); + memHelper.addChunk( (void**) &bodyGridCoords, sizeof( btIntVec3 ) * bodies.size() ); + memHelper.addChunk( (void**) &batches, sizeof( btBatchInfo )* allocNumBatches ); + memHelper.addChunk( (void**) &batchWork, sizeof( int )* allocNumBatches ); + memHelper.addChunk( (void**) &conInfos, sizeof( btBatchedConstraintInfo ) * numConstraints ); + memHelper.addChunk( (void**) &constraintBatchIds, sizeof( int ) * numConstraints ); + memHelper.addChunk( (void**) &constraintRowBatchIds, sizeof( int ) * numConstraintRows ); + size_t scratchSize = memHelper.getSizeToAllocate(); + // if we need to reallocate + if (scratchMemory->capacity() < scratchSize) + { + // allocate 6.25% extra to avoid repeated reallocs + scratchMemory->reserve( scratchSize + scratchSize/16 ); + } + scratchMemory->resizeNoInitialize( scratchSize ); + char* memPtr = &scratchMemory->at(0); + memHelper.setChunkPointers( memPtr ); + } + + numConstraints = initBatchedConstraintInfo(conInfos, constraints); + + // compute bounding box around all dynamic bodies + // (could be done in parallel) + btVector3 bboxMin(BT_LARGE_FLOAT, BT_LARGE_FLOAT, BT_LARGE_FLOAT); + btVector3 bboxMax = -bboxMin; + //int dynamicBodyCount = 0; + for (int i = 0; i < bodies.size(); ++i) + { + const btSolverBody& body = bodies[i]; + btVector3 bodyPos = body.getWorldTransform().getOrigin(); + bool isDynamic = ( body.internalGetInvMass().x() > btScalar( 0 ) ); + bodyPositions[i] = bodyPos; + bodyDynamicFlags[i] = isDynamic; + if (isDynamic) + { + //dynamicBodyCount++; + bboxMin.setMin(bodyPos); + bboxMax.setMax(bodyPos); + } + } + + // find max extent of all dynamic constraints + // (could be done in parallel) + btVector3 consExtent = findMaxDynamicConstraintExtent(bodyPositions, bodyDynamicFlags, conInfos, numConstraints, bodies.size()); + + btVector3 gridExtent = bboxMax - bboxMin; + + btVector3 gridCellSize = consExtent; + int gridDim[3]; + gridDim[ 0 ] = int( 1.0 + gridExtent.x() / gridCellSize.x() ); + gridDim[ 1 ] = int( 1.0 + gridExtent.y() / gridCellSize.y() ); + gridDim[ 2 ] = int( 1.0 + gridExtent.z() / gridCellSize.z() ); + + // if we can collapse an axis, it will cut our number of phases in half which could be more efficient + int phaseMask = 7; + bool collapseAxis = use2DGrid; + if ( collapseAxis ) + { + // pick the smallest axis to collapse, leaving us with the greatest number of cells in our grid + int iAxisToCollapse = 0; + int axisDim = gridDim[iAxisToCollapse]; + //for each dimension + for ( int i = 0; i < 3; ++i ) + { + if (gridDim[i] < axisDim) + { + iAxisToCollapse = i; + axisDim = gridDim[i]; + } + } + // collapse it + gridCellSize[iAxisToCollapse] = gridExtent[iAxisToCollapse] * 2.0f; + phaseMask &= ~(1 << iAxisToCollapse); + } + + int numGridChunks = 0; + btIntVec3 gridChunkDim; // each chunk is 2x2x2 group of cells + while (true) + { + gridDim[0] = int( 1.0 + gridExtent.x() / gridCellSize.x() ); + gridDim[1] = int( 1.0 + gridExtent.y() / gridCellSize.y() ); + gridDim[2] = int( 1.0 + gridExtent.z() / gridCellSize.z() ); + gridChunkDim[ 0 ] = btMax( 1, ( gridDim[ 0 ] + 0 ) / 2 ); + gridChunkDim[ 1 ] = btMax( 1, ( gridDim[ 1 ] + 0 ) / 2 ); + gridChunkDim[ 2 ] = btMax( 1, ( gridDim[ 2 ] + 0 ) / 2 ); + numGridChunks = gridChunkDim[ 0 ] * gridChunkDim[ 1 ] * gridChunkDim[ 2 ]; + float nChunks = float(gridChunkDim[0]) * float(gridChunkDim[1]) * float(gridChunkDim[2]); // suceptible to integer overflow + if ( numGridChunks <= maxGridChunkCount && nChunks <= maxGridChunkCount ) + { + break; + } + gridCellSize *= 1.25; // should roughly cut numCells in half + } + btAssert(numGridChunks <= maxGridChunkCount ); + int maxNumBatchesPerPhase = numGridChunks; + + // for each dynamic body, compute grid coords + btVector3 invGridCellSize = btVector3(1,1,1)/gridCellSize; + // (can be done in parallel) + for (int iBody = 0; iBody < bodies.size(); ++iBody) + { + btIntVec3& coords = bodyGridCoords[iBody]; + if (bodyDynamicFlags[iBody]) + { + btVector3 v = ( bodyPositions[ iBody ] - bboxMin )*invGridCellSize; + coords.m_ints[0] = int(v.x()); + coords.m_ints[1] = int(v.y()); + coords.m_ints[2] = int(v.z()); + btAssert(coords.m_ints[0] >= 0 && coords.m_ints[0] < gridDim[0]); + btAssert(coords.m_ints[1] >= 0 && coords.m_ints[1] < gridDim[1]); + btAssert(coords.m_ints[2] >= 0 && coords.m_ints[2] < gridDim[2]); + } + else + { + coords.m_ints[0] = -1; + coords.m_ints[1] = -1; + coords.m_ints[2] = -1; + } + } + + for (int iPhase = 0; iPhase < numPhases; ++iPhase) + { + int batchBegin = iPhase * maxNumBatchesPerPhase; + int batchEnd = batchBegin + maxNumBatchesPerPhase; + for ( int iBatch = batchBegin; iBatch < batchEnd; ++iBatch ) + { + btBatchInfo& batch = batches[ iBatch ]; + batch = btBatchInfo(); + } + } + + { + AssignConstraintsToGridBatchesParams params; + params.bodyDynamicFlags = bodyDynamicFlags; + params.bodyGridCoords = bodyGridCoords; + params.numBodies = bodies.size(); + params.conInfos = conInfos; + params.constraintBatchIds = constraintBatchIds; + params.gridChunkDim = gridChunkDim; + params.maxNumBatchesPerPhase = maxNumBatchesPerPhase; + params.numPhases = numPhases; + params.phaseMask = phaseMask; + bool inParallel = true; + if (inParallel) + { + AssignConstraintsToGridBatchesLoop loop(params); + int grainSize = 250; + btParallelFor(0, numConstraints, grainSize, loop); + } + else + { + assignConstraintsToGridBatches( params, 0, numConstraints ); + } + } + for ( int iCon = 0; iCon < numConstraints; ++iCon ) + { + const btBatchedConstraintInfo& con = conInfos[ iCon ]; + int iBatch = constraintBatchIds[ iCon ]; + btBatchInfo& batch = batches[iBatch]; + batch.numConstraints += con.numConstraintRows; + } + + for (int iPhase = 0; iPhase < numPhases; ++iPhase) + { + // if phase is legit, + if (iPhase == (iPhase&phaseMask)) + { + int iBeginBatch = iPhase * maxNumBatchesPerPhase; + int iEndBatch = iBeginBatch + maxNumBatchesPerPhase; + mergeSmallBatches( batches, iBeginBatch, iEndBatch, minBatchSize, maxBatchSize ); + } + } + // all constraints have been assigned a batchId + updateConstraintBatchIdsForMergesMt(constraintBatchIds, numConstraints, batches, maxNumBatchesPerPhase*numPhases); + + if (numConstraintRows > numConstraints) + { + expandConstraintRowsMt(&constraintRowBatchIds[0], &constraintBatchIds[0], &conInfos[0], numConstraints, numConstraintRows); + } + else + { + constraintRowBatchIds = constraintBatchIds; + } + + writeOutBatches(batchedConstraints, constraintRowBatchIds, numConstraintRows, batches, batchWork, maxNumBatchesPerPhase, numPhases); + btAssert(batchedConstraints->validate(constraints, bodies)); +} + + +static void setupSingleBatch( + btBatchedConstraints* bc, + int numConstraints +) +{ + BT_PROFILE("setupSingleBatch"); + typedef btBatchedConstraints::Range Range; + + bc->m_constraintIndices.resize( numConstraints ); + for ( int i = 0; i < numConstraints; ++i ) + { + bc->m_constraintIndices[ i ] = i; + } + + bc->m_batches.resizeNoInitialize( 0 ); + bc->m_phases.resizeNoInitialize( 0 ); + bc->m_phaseOrder.resizeNoInitialize( 0 ); + bc->m_phaseGrainSize.resizeNoInitialize( 0 ); + + if (numConstraints > 0) + { + bc->m_batches.push_back( Range( 0, numConstraints ) ); + bc->m_phases.push_back( Range( 0, 1 ) ); + bc->m_phaseOrder.push_back(0); + bc->m_phaseGrainSize.push_back(1); + } +} + + +void btBatchedConstraints::setup( + btConstraintArray* constraints, + const btAlignedObjectArray<btSolverBody>& bodies, + BatchingMethod batchingMethod, + int minBatchSize, + int maxBatchSize, + btAlignedObjectArray<char>* scratchMemory + ) +{ + if (constraints->size() >= minBatchSize*4) + { + bool use2DGrid = batchingMethod == BATCHING_METHOD_SPATIAL_GRID_2D; + setupSpatialGridBatchesMt( this, scratchMemory, constraints, bodies, minBatchSize, maxBatchSize, use2DGrid ); + if (s_debugDrawBatches) + { + debugDrawAllBatches( this, constraints, bodies ); + } + } + else + { + setupSingleBatch( this, constraints->size() ); + } +} + + diff --git a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btBatchedConstraints.h b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btBatchedConstraints.h new file mode 100644 index 0000000000..0fd8f31dd4 --- /dev/null +++ b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btBatchedConstraints.h @@ -0,0 +1,66 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef BT_BATCHED_CONSTRAINTS_H +#define BT_BATCHED_CONSTRAINTS_H + +#include "LinearMath/btThreads.h" +#include "LinearMath/btAlignedObjectArray.h" +#include "BulletDynamics/ConstraintSolver/btSolverBody.h" +#include "BulletDynamics/ConstraintSolver/btSolverConstraint.h" + + +class btIDebugDraw; + +struct btBatchedConstraints +{ + enum BatchingMethod + { + BATCHING_METHOD_SPATIAL_GRID_2D, + BATCHING_METHOD_SPATIAL_GRID_3D, + BATCHING_METHOD_COUNT + }; + struct Range + { + int begin; + int end; + + Range() : begin( 0 ), end( 0 ) {} + Range( int _beg, int _end ) : begin( _beg ), end( _end ) {} + }; + + btAlignedObjectArray<int> m_constraintIndices; + btAlignedObjectArray<Range> m_batches; // each batch is a range of indices in the m_constraintIndices array + btAlignedObjectArray<Range> m_phases; // each phase is range of indices in the m_batches array + btAlignedObjectArray<char> m_phaseGrainSize; // max grain size for each phase + btAlignedObjectArray<int> m_phaseOrder; // phases can be done in any order, so we can randomize the order here + btIDebugDraw* m_debugDrawer; + + static bool s_debugDrawBatches; + + btBatchedConstraints() {m_debugDrawer=NULL;} + void setup( btConstraintArray* constraints, + const btAlignedObjectArray<btSolverBody>& bodies, + BatchingMethod batchingMethod, + int minBatchSize, + int maxBatchSize, + btAlignedObjectArray<char>* scratchMemory + ); + bool validate( btConstraintArray* constraints, const btAlignedObjectArray<btSolverBody>& bodies ) const; +}; + + +#endif // BT_BATCHED_CONSTRAINTS_H + diff --git a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btConstraintSolver.h b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btConstraintSolver.h index 890afe6da4..0491639f70 100644 --- a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btConstraintSolver.h +++ b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btConstraintSolver.h @@ -34,7 +34,8 @@ enum btConstraintSolverType { BT_SEQUENTIAL_IMPULSE_SOLVER=1, BT_MLCP_SOLVER=2, - BT_NNCG_SOLVER=4 + BT_NNCG_SOLVER=4, + BT_MULTIBODY_SOLVER=8, }; class btConstraintSolver diff --git a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btContactSolverInfo.h b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btContactSolverInfo.h index 28d0c1dd48..93865cbc59 100644 --- a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btContactSolverInfo.h +++ b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btContactSolverInfo.h @@ -29,7 +29,8 @@ enum btSolverMode SOLVER_CACHE_FRIENDLY = 128, SOLVER_SIMD = 256, SOLVER_INTERLEAVE_CONTACT_AND_FRICTION_CONSTRAINTS = 512, - SOLVER_ALLOW_ZERO_LENGTH_FRICTION_DIRECTIONS = 1024 + SOLVER_ALLOW_ZERO_LENGTH_FRICTION_DIRECTIONS = 1024, + SOLVER_DISABLE_IMPLICIT_CONE_FRICTION = 2048 }; struct btContactSolverInfoData diff --git a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.cpp b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.cpp index fa17254ec3..c38b8353f0 100644 --- a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.cpp +++ b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.cpp @@ -855,8 +855,8 @@ int btGeneric6DofConstraint::get_limit_motor_info2( tag_vel, info->fps * limot->m_stopERP); info->m_constraintError[srow] += mot_fact * limot->m_targetVelocity; - info->m_lowerLimit[srow] = -limot->m_maxMotorForce; - info->m_upperLimit[srow] = limot->m_maxMotorForce; + info->m_lowerLimit[srow] = -limot->m_maxMotorForce / info->fps; + info->m_upperLimit[srow] = limot->m_maxMotorForce / info->fps; } } if(limit) diff --git a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.h b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.h index bea8629c32..b2ad45f749 100644 --- a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.h +++ b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.h @@ -77,7 +77,7 @@ public: { m_accumulatedImpulse = 0.f; m_targetVelocity = 0; - m_maxMotorForce = 0.1f; + m_maxMotorForce = 6.0f; m_maxLimitForce = 300.0f; m_loLimit = 1.0f; m_hiLimit = -1.0f; diff --git a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btGeneric6DofSpring2Constraint.cpp b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btGeneric6DofSpring2Constraint.cpp index f0976ee493..540dcd18f7 100644 --- a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btGeneric6DofSpring2Constraint.cpp +++ b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btGeneric6DofSpring2Constraint.cpp @@ -719,8 +719,8 @@ int btGeneric6DofSpring2Constraint::get_limit_motor_info2( tag_vel, info->fps * limot->m_motorERP); info->m_constraintError[srow] = mot_fact * limot->m_targetVelocity; - info->m_lowerLimit[srow] = -limot->m_maxMotorForce; - info->m_upperLimit[srow] = limot->m_maxMotorForce; + info->m_lowerLimit[srow] = -limot->m_maxMotorForce / info->fps; + info->m_upperLimit[srow] = limot->m_maxMotorForce / info->fps; info->cfm[srow] = limot->m_motorCFM; srow += info->rowskip; ++count; @@ -769,8 +769,8 @@ int btGeneric6DofSpring2Constraint::get_limit_motor_info2( mot_fact = 0; } info->m_constraintError[srow] = mot_fact * targetvelocity * (rotational ? -1 : 1); - info->m_lowerLimit[srow] = -limot->m_maxMotorForce; - info->m_upperLimit[srow] = limot->m_maxMotorForce; + info->m_lowerLimit[srow] = -limot->m_maxMotorForce / info->fps; + info->m_upperLimit[srow] = limot->m_maxMotorForce / info->fps; info->cfm[srow] = limot->m_motorCFM; srow += info->rowskip; ++count; @@ -797,6 +797,12 @@ int btGeneric6DofSpring2Constraint::get_limit_motor_info2( btScalar cfm = BT_ZERO; btScalar mA = BT_ONE / m_rbA.getInvMass(); btScalar mB = BT_ONE / m_rbB.getInvMass(); + if (rotational) { + btScalar rrA = (m_calculatedTransformA.getOrigin() - transA.getOrigin()).length2(); + btScalar rrB = (m_calculatedTransformB.getOrigin() - transB.getOrigin()).length2(); + if (m_rbA.getInvMass()) mA = mA * rrA + 1 / (m_rbA.getInvInertiaTensorWorld() * ax1).length(); + if (m_rbB.getInvMass()) mB = mB * rrB + 1 / (m_rbB.getInvInertiaTensorWorld() * ax1).length(); + } btScalar m = mA > mB ? mB : mA; btScalar angularfreq = sqrt(ks / m); @@ -815,7 +821,18 @@ int btGeneric6DofSpring2Constraint::get_limit_motor_info2( btScalar fd = -kd * (vel) * (rotational ? -1 : 1) * dt; btScalar f = (fs+fd); - info->m_constraintError[srow] = (vel + f * (rotational ? -1 : 1)) ; + // after the spring force affecting the body(es) the new velocity will be + // vel + f / m * (rotational ? -1 : 1) + // so in theory this should be set here for m_constraintError + // (with m_constraintError we set a desired velocity for the affected body(es)) + // however in practice any value is fine as long as it is greater then the "proper" velocity, + // because the m_lowerLimit and the m_upperLimit will determinate the strength of the final pulling force + // so it is much simpler (and more robust) just to simply use inf (with the proper sign) + // you may also wonder what if the current velocity (vel) so high that the pulling force will not change its direction (in this iteration) + // will we not request a velocity with the wrong direction ? + // and the answare is not, because in practice during the solving the current velocity is subtracted from the m_constraintError + // so the sign of the force that is really matters + info->m_constraintError[srow] = (rotational ? -1 : 1) * (f < 0 ? -SIMD_INFINITY : SIMD_INFINITY); btScalar minf = f < fd ? f : fd; btScalar maxf = f < fd ? fd : f; diff --git a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btGeneric6DofSpring2Constraint.h b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btGeneric6DofSpring2Constraint.h index 66d1769583..1b8d0eace9 100644 --- a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btGeneric6DofSpring2Constraint.h +++ b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btGeneric6DofSpring2Constraint.h @@ -107,7 +107,7 @@ public: m_motorCFM = 0.f; m_enableMotor = false; m_targetVelocity = 0; - m_maxMotorForce = 0.1f; + m_maxMotorForce = 6.0f; m_servoMotor = false; m_servoTarget = 0; m_enableSpring = false; diff --git a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btGeneric6DofSpringConstraint.cpp b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btGeneric6DofSpringConstraint.cpp index 6f765884ec..3f875989ea 100644 --- a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btGeneric6DofSpringConstraint.cpp +++ b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btGeneric6DofSpringConstraint.cpp @@ -131,7 +131,7 @@ void btGeneric6DofSpringConstraint::internalUpdateSprings(btConstraintInfo2* inf btScalar force = delta * m_springStiffness[i]; btScalar velFactor = info->fps * m_springDamping[i] / btScalar(info->m_numIterations); m_linearLimits.m_targetVelocity[i] = velFactor * force; - m_linearLimits.m_maxMotorForce[i] = btFabs(force) / info->fps; + m_linearLimits.m_maxMotorForce[i] = btFabs(force); } } for(i = 0; i < 3; i++) @@ -146,7 +146,7 @@ void btGeneric6DofSpringConstraint::internalUpdateSprings(btConstraintInfo2* inf btScalar force = -delta * m_springStiffness[i+3]; btScalar velFactor = info->fps * m_springDamping[i+3] / btScalar(info->m_numIterations); m_angularLimits[i].m_targetVelocity = velFactor * force; - m_angularLimits[i].m_maxMotorForce = btFabs(force) / info->fps; + m_angularLimits[i].m_maxMotorForce = btFabs(force); } } } diff --git a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.cpp b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.cpp index b0d57a3e87..63174a6ec0 100644 --- a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.cpp +++ b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.cpp @@ -21,6 +21,7 @@ subject to the following restrictions: #include "btSequentialImpulseConstraintSolver.h" #include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h" + #include "LinearMath/btIDebugDraw.h" #include "LinearMath/btCpuFeatureUtility.h" @@ -42,11 +43,11 @@ int gNumSplitImpulseRecoveries = 0; //#define VERBOSE_RESIDUAL_PRINTF 1 ///This is the scalar reference implementation of solving a single constraint row, the innerloop of the Projected Gauss Seidel/Sequential Impulse constraint solver ///Below are optional SSE2 and SSE4/FMA3 versions. We assume most hardware has SSE2. For SSE4/FMA3 we perform a CPU feature check. -static btSimdScalar gResolveSingleConstraintRowGeneric_scalar_reference(btSolverBody& body1, btSolverBody& body2, const btSolverConstraint& c) +static btScalar gResolveSingleConstraintRowGeneric_scalar_reference(btSolverBody& bodyA, btSolverBody& bodyB, const btSolverConstraint& c) { btScalar deltaImpulse = c.m_rhs - btScalar(c.m_appliedImpulse)*c.m_cfm; - const btScalar deltaVel1Dotn = c.m_contactNormal1.dot(body1.internalGetDeltaLinearVelocity()) + c.m_relpos1CrossNormal.dot(body1.internalGetDeltaAngularVelocity()); - const btScalar deltaVel2Dotn = c.m_contactNormal2.dot(body2.internalGetDeltaLinearVelocity()) + c.m_relpos2CrossNormal.dot(body2.internalGetDeltaAngularVelocity()); + const btScalar deltaVel1Dotn = c.m_contactNormal1.dot(bodyA.internalGetDeltaLinearVelocity()) + c.m_relpos1CrossNormal.dot(bodyA.internalGetDeltaAngularVelocity()); + const btScalar deltaVel2Dotn = c.m_contactNormal2.dot(bodyB.internalGetDeltaLinearVelocity()) + c.m_relpos2CrossNormal.dot(bodyB.internalGetDeltaAngularVelocity()); // const btScalar delta_rel_vel = deltaVel1Dotn-deltaVel2Dotn; deltaImpulse -= deltaVel1Dotn*c.m_jacDiagABInv; @@ -68,18 +69,18 @@ static btSimdScalar gResolveSingleConstraintRowGeneric_scalar_reference(btSolver c.m_appliedImpulse = sum; } - body1.internalApplyImpulse(c.m_contactNormal1*body1.internalGetInvMass(), c.m_angularComponentA, deltaImpulse); - body2.internalApplyImpulse(c.m_contactNormal2*body2.internalGetInvMass(), c.m_angularComponentB, deltaImpulse); + bodyA.internalApplyImpulse(c.m_contactNormal1*bodyA.internalGetInvMass(), c.m_angularComponentA, deltaImpulse); + bodyB.internalApplyImpulse(c.m_contactNormal2*bodyB.internalGetInvMass(), c.m_angularComponentB, deltaImpulse); - return deltaImpulse; + return deltaImpulse*(1./c.m_jacDiagABInv); } -static btSimdScalar gResolveSingleConstraintRowLowerLimit_scalar_reference(btSolverBody& body1, btSolverBody& body2, const btSolverConstraint& c) +static btScalar gResolveSingleConstraintRowLowerLimit_scalar_reference(btSolverBody& bodyA, btSolverBody& bodyB, const btSolverConstraint& c) { btScalar deltaImpulse = c.m_rhs - btScalar(c.m_appliedImpulse)*c.m_cfm; - const btScalar deltaVel1Dotn = c.m_contactNormal1.dot(body1.internalGetDeltaLinearVelocity()) + c.m_relpos1CrossNormal.dot(body1.internalGetDeltaAngularVelocity()); - const btScalar deltaVel2Dotn = c.m_contactNormal2.dot(body2.internalGetDeltaLinearVelocity()) + c.m_relpos2CrossNormal.dot(body2.internalGetDeltaAngularVelocity()); + const btScalar deltaVel1Dotn = c.m_contactNormal1.dot(bodyA.internalGetDeltaLinearVelocity()) + c.m_relpos1CrossNormal.dot(bodyA.internalGetDeltaAngularVelocity()); + const btScalar deltaVel2Dotn = c.m_contactNormal2.dot(bodyB.internalGetDeltaLinearVelocity()) + c.m_relpos2CrossNormal.dot(bodyB.internalGetDeltaAngularVelocity()); deltaImpulse -= deltaVel1Dotn*c.m_jacDiagABInv; deltaImpulse -= deltaVel2Dotn*c.m_jacDiagABInv; @@ -93,10 +94,10 @@ static btSimdScalar gResolveSingleConstraintRowLowerLimit_scalar_reference(btSol { c.m_appliedImpulse = sum; } - body1.internalApplyImpulse(c.m_contactNormal1*body1.internalGetInvMass(), c.m_angularComponentA, deltaImpulse); - body2.internalApplyImpulse(c.m_contactNormal2*body2.internalGetInvMass(), c.m_angularComponentB, deltaImpulse); + bodyA.internalApplyImpulse(c.m_contactNormal1*bodyA.internalGetInvMass(), c.m_angularComponentA, deltaImpulse); + bodyB.internalApplyImpulse(c.m_contactNormal2*bodyB.internalGetInvMass(), c.m_angularComponentB, deltaImpulse); - return deltaImpulse; + return deltaImpulse*(1./c.m_jacDiagABInv); } @@ -149,14 +150,14 @@ static inline __m128 btSimdDot3( __m128 vec0, __m128 vec1 ) #endif // Project Gauss Seidel or the equivalent Sequential Impulse -static btSimdScalar gResolveSingleConstraintRowGeneric_sse2(btSolverBody& body1, btSolverBody& body2, const btSolverConstraint& c) +static btScalar gResolveSingleConstraintRowGeneric_sse2(btSolverBody& bodyA, btSolverBody& bodyB, const btSolverConstraint& c) { __m128 cpAppliedImp = _mm_set1_ps(c.m_appliedImpulse); __m128 lowerLimit1 = _mm_set1_ps(c.m_lowerLimit); __m128 upperLimit1 = _mm_set1_ps(c.m_upperLimit); btSimdScalar deltaImpulse = _mm_sub_ps(_mm_set1_ps(c.m_rhs), _mm_mul_ps(_mm_set1_ps(c.m_appliedImpulse), _mm_set1_ps(c.m_cfm))); - __m128 deltaVel1Dotn = _mm_add_ps(btSimdDot3(c.m_contactNormal1.mVec128, body1.internalGetDeltaLinearVelocity().mVec128), btSimdDot3(c.m_relpos1CrossNormal.mVec128, body1.internalGetDeltaAngularVelocity().mVec128)); - __m128 deltaVel2Dotn = _mm_add_ps(btSimdDot3(c.m_contactNormal2.mVec128, body2.internalGetDeltaLinearVelocity().mVec128), btSimdDot3(c.m_relpos2CrossNormal.mVec128, body2.internalGetDeltaAngularVelocity().mVec128)); + __m128 deltaVel1Dotn = _mm_add_ps(btSimdDot3(c.m_contactNormal1.mVec128, bodyA.internalGetDeltaLinearVelocity().mVec128), btSimdDot3(c.m_relpos1CrossNormal.mVec128, bodyA.internalGetDeltaAngularVelocity().mVec128)); + __m128 deltaVel2Dotn = _mm_add_ps(btSimdDot3(c.m_contactNormal2.mVec128, bodyB.internalGetDeltaLinearVelocity().mVec128), btSimdDot3(c.m_relpos2CrossNormal.mVec128, bodyB.internalGetDeltaAngularVelocity().mVec128)); deltaImpulse = _mm_sub_ps(deltaImpulse, _mm_mul_ps(deltaVel1Dotn, _mm_set1_ps(c.m_jacDiagABInv))); deltaImpulse = _mm_sub_ps(deltaImpulse, _mm_mul_ps(deltaVel2Dotn, _mm_set1_ps(c.m_jacDiagABInv))); btSimdScalar sum = _mm_add_ps(cpAppliedImp, deltaImpulse); @@ -169,27 +170,27 @@ static btSimdScalar gResolveSingleConstraintRowGeneric_sse2(btSolverBody& body1, __m128 upperMinApplied = _mm_sub_ps(upperLimit1, cpAppliedImp); deltaImpulse = _mm_or_ps(_mm_and_ps(resultUpperLess, deltaImpulse), _mm_andnot_ps(resultUpperLess, upperMinApplied)); c.m_appliedImpulse = _mm_or_ps(_mm_and_ps(resultUpperLess, c.m_appliedImpulse), _mm_andnot_ps(resultUpperLess, upperLimit1)); - __m128 linearComponentA = _mm_mul_ps(c.m_contactNormal1.mVec128, body1.internalGetInvMass().mVec128); - __m128 linearComponentB = _mm_mul_ps((c.m_contactNormal2).mVec128, body2.internalGetInvMass().mVec128); + __m128 linearComponentA = _mm_mul_ps(c.m_contactNormal1.mVec128, bodyA.internalGetInvMass().mVec128); + __m128 linearComponentB = _mm_mul_ps((c.m_contactNormal2).mVec128, bodyB.internalGetInvMass().mVec128); __m128 impulseMagnitude = deltaImpulse; - body1.internalGetDeltaLinearVelocity().mVec128 = _mm_add_ps(body1.internalGetDeltaLinearVelocity().mVec128, _mm_mul_ps(linearComponentA, impulseMagnitude)); - body1.internalGetDeltaAngularVelocity().mVec128 = _mm_add_ps(body1.internalGetDeltaAngularVelocity().mVec128, _mm_mul_ps(c.m_angularComponentA.mVec128, impulseMagnitude)); - body2.internalGetDeltaLinearVelocity().mVec128 = _mm_add_ps(body2.internalGetDeltaLinearVelocity().mVec128, _mm_mul_ps(linearComponentB, impulseMagnitude)); - body2.internalGetDeltaAngularVelocity().mVec128 = _mm_add_ps(body2.internalGetDeltaAngularVelocity().mVec128, _mm_mul_ps(c.m_angularComponentB.mVec128, impulseMagnitude)); - return deltaImpulse; + bodyA.internalGetDeltaLinearVelocity().mVec128 = _mm_add_ps(bodyA.internalGetDeltaLinearVelocity().mVec128, _mm_mul_ps(linearComponentA, impulseMagnitude)); + bodyA.internalGetDeltaAngularVelocity().mVec128 = _mm_add_ps(bodyA.internalGetDeltaAngularVelocity().mVec128, _mm_mul_ps(c.m_angularComponentA.mVec128, impulseMagnitude)); + bodyB.internalGetDeltaLinearVelocity().mVec128 = _mm_add_ps(bodyB.internalGetDeltaLinearVelocity().mVec128, _mm_mul_ps(linearComponentB, impulseMagnitude)); + bodyB.internalGetDeltaAngularVelocity().mVec128 = _mm_add_ps(bodyB.internalGetDeltaAngularVelocity().mVec128, _mm_mul_ps(c.m_angularComponentB.mVec128, impulseMagnitude)); + return deltaImpulse.m_floats[0]/c.m_jacDiagABInv; } // Enhanced version of gResolveSingleConstraintRowGeneric_sse2 with SSE4.1 and FMA3 -static btSimdScalar gResolveSingleConstraintRowGeneric_sse4_1_fma3(btSolverBody& body1, btSolverBody& body2, const btSolverConstraint& c) +static btScalar gResolveSingleConstraintRowGeneric_sse4_1_fma3(btSolverBody& bodyA, btSolverBody& bodyB, const btSolverConstraint& c) { #if defined (BT_ALLOW_SSE4) __m128 tmp = _mm_set_ps1(c.m_jacDiagABInv); __m128 deltaImpulse = _mm_set_ps1(c.m_rhs - btScalar(c.m_appliedImpulse)*c.m_cfm); const __m128 lowerLimit = _mm_set_ps1(c.m_lowerLimit); const __m128 upperLimit = _mm_set_ps1(c.m_upperLimit); - const __m128 deltaVel1Dotn = _mm_add_ps(DOT_PRODUCT(c.m_contactNormal1.mVec128, body1.internalGetDeltaLinearVelocity().mVec128), DOT_PRODUCT(c.m_relpos1CrossNormal.mVec128, body1.internalGetDeltaAngularVelocity().mVec128)); - const __m128 deltaVel2Dotn = _mm_add_ps(DOT_PRODUCT(c.m_contactNormal2.mVec128, body2.internalGetDeltaLinearVelocity().mVec128), DOT_PRODUCT(c.m_relpos2CrossNormal.mVec128, body2.internalGetDeltaAngularVelocity().mVec128)); + const __m128 deltaVel1Dotn = _mm_add_ps(DOT_PRODUCT(c.m_contactNormal1.mVec128, bodyA.internalGetDeltaLinearVelocity().mVec128), DOT_PRODUCT(c.m_relpos1CrossNormal.mVec128, bodyA.internalGetDeltaAngularVelocity().mVec128)); + const __m128 deltaVel2Dotn = _mm_add_ps(DOT_PRODUCT(c.m_contactNormal2.mVec128, bodyB.internalGetDeltaLinearVelocity().mVec128), DOT_PRODUCT(c.m_relpos2CrossNormal.mVec128, bodyB.internalGetDeltaAngularVelocity().mVec128)); deltaImpulse = FMNADD(deltaVel1Dotn, tmp, deltaImpulse); deltaImpulse = FMNADD(deltaVel2Dotn, tmp, deltaImpulse); tmp = _mm_add_ps(c.m_appliedImpulse, deltaImpulse); // sum @@ -197,26 +198,27 @@ static btSimdScalar gResolveSingleConstraintRowGeneric_sse4_1_fma3(btSolverBody& const __m128 maskUpper = _mm_cmpgt_ps(upperLimit, tmp); deltaImpulse = _mm_blendv_ps(_mm_sub_ps(lowerLimit, c.m_appliedImpulse), _mm_blendv_ps(_mm_sub_ps(upperLimit, c.m_appliedImpulse), deltaImpulse, maskUpper), maskLower); c.m_appliedImpulse = _mm_blendv_ps(lowerLimit, _mm_blendv_ps(upperLimit, tmp, maskUpper), maskLower); - body1.internalGetDeltaLinearVelocity().mVec128 = FMADD(_mm_mul_ps(c.m_contactNormal1.mVec128, body1.internalGetInvMass().mVec128), deltaImpulse, body1.internalGetDeltaLinearVelocity().mVec128); - body1.internalGetDeltaAngularVelocity().mVec128 = FMADD(c.m_angularComponentA.mVec128, deltaImpulse, body1.internalGetDeltaAngularVelocity().mVec128); - body2.internalGetDeltaLinearVelocity().mVec128 = FMADD(_mm_mul_ps(c.m_contactNormal2.mVec128, body2.internalGetInvMass().mVec128), deltaImpulse, body2.internalGetDeltaLinearVelocity().mVec128); - body2.internalGetDeltaAngularVelocity().mVec128 = FMADD(c.m_angularComponentB.mVec128, deltaImpulse, body2.internalGetDeltaAngularVelocity().mVec128); - return deltaImpulse; + bodyA.internalGetDeltaLinearVelocity().mVec128 = FMADD(_mm_mul_ps(c.m_contactNormal1.mVec128, bodyA.internalGetInvMass().mVec128), deltaImpulse, bodyA.internalGetDeltaLinearVelocity().mVec128); + bodyA.internalGetDeltaAngularVelocity().mVec128 = FMADD(c.m_angularComponentA.mVec128, deltaImpulse, bodyA.internalGetDeltaAngularVelocity().mVec128); + bodyB.internalGetDeltaLinearVelocity().mVec128 = FMADD(_mm_mul_ps(c.m_contactNormal2.mVec128, bodyB.internalGetInvMass().mVec128), deltaImpulse, bodyB.internalGetDeltaLinearVelocity().mVec128); + bodyB.internalGetDeltaAngularVelocity().mVec128 = FMADD(c.m_angularComponentB.mVec128, deltaImpulse, bodyB.internalGetDeltaAngularVelocity().mVec128); + btSimdScalar deltaImp = deltaImpulse; + return deltaImp.m_floats[0]*(1./c.m_jacDiagABInv); #else - return gResolveSingleConstraintRowGeneric_sse2(body1,body2,c); + return gResolveSingleConstraintRowGeneric_sse2(bodyA,bodyB,c); #endif } -static btSimdScalar gResolveSingleConstraintRowLowerLimit_sse2(btSolverBody& body1, btSolverBody& body2, const btSolverConstraint& c) +static btScalar gResolveSingleConstraintRowLowerLimit_sse2(btSolverBody& bodyA, btSolverBody& bodyB, const btSolverConstraint& c) { __m128 cpAppliedImp = _mm_set1_ps(c.m_appliedImpulse); __m128 lowerLimit1 = _mm_set1_ps(c.m_lowerLimit); __m128 upperLimit1 = _mm_set1_ps(c.m_upperLimit); btSimdScalar deltaImpulse = _mm_sub_ps(_mm_set1_ps(c.m_rhs), _mm_mul_ps(_mm_set1_ps(c.m_appliedImpulse), _mm_set1_ps(c.m_cfm))); - __m128 deltaVel1Dotn = _mm_add_ps(btSimdDot3(c.m_contactNormal1.mVec128, body1.internalGetDeltaLinearVelocity().mVec128), btSimdDot3(c.m_relpos1CrossNormal.mVec128, body1.internalGetDeltaAngularVelocity().mVec128)); - __m128 deltaVel2Dotn = _mm_add_ps(btSimdDot3(c.m_contactNormal2.mVec128, body2.internalGetDeltaLinearVelocity().mVec128), btSimdDot3(c.m_relpos2CrossNormal.mVec128, body2.internalGetDeltaAngularVelocity().mVec128)); + __m128 deltaVel1Dotn = _mm_add_ps(btSimdDot3(c.m_contactNormal1.mVec128, bodyA.internalGetDeltaLinearVelocity().mVec128), btSimdDot3(c.m_relpos1CrossNormal.mVec128, bodyA.internalGetDeltaAngularVelocity().mVec128)); + __m128 deltaVel2Dotn = _mm_add_ps(btSimdDot3(c.m_contactNormal2.mVec128, bodyB.internalGetDeltaLinearVelocity().mVec128), btSimdDot3(c.m_relpos2CrossNormal.mVec128, bodyB.internalGetDeltaAngularVelocity().mVec128)); deltaImpulse = _mm_sub_ps(deltaImpulse, _mm_mul_ps(deltaVel1Dotn, _mm_set1_ps(c.m_jacDiagABInv))); deltaImpulse = _mm_sub_ps(deltaImpulse, _mm_mul_ps(deltaVel2Dotn, _mm_set1_ps(c.m_jacDiagABInv))); btSimdScalar sum = _mm_add_ps(cpAppliedImp, deltaImpulse); @@ -226,39 +228,40 @@ static btSimdScalar gResolveSingleConstraintRowLowerLimit_sse2(btSolverBody& bod __m128 lowMinApplied = _mm_sub_ps(lowerLimit1, cpAppliedImp); deltaImpulse = _mm_or_ps(_mm_and_ps(resultLowerLess, lowMinApplied), _mm_andnot_ps(resultLowerLess, deltaImpulse)); c.m_appliedImpulse = _mm_or_ps(_mm_and_ps(resultLowerLess, lowerLimit1), _mm_andnot_ps(resultLowerLess, sum)); - __m128 linearComponentA = _mm_mul_ps(c.m_contactNormal1.mVec128, body1.internalGetInvMass().mVec128); - __m128 linearComponentB = _mm_mul_ps(c.m_contactNormal2.mVec128, body2.internalGetInvMass().mVec128); + __m128 linearComponentA = _mm_mul_ps(c.m_contactNormal1.mVec128, bodyA.internalGetInvMass().mVec128); + __m128 linearComponentB = _mm_mul_ps(c.m_contactNormal2.mVec128, bodyB.internalGetInvMass().mVec128); __m128 impulseMagnitude = deltaImpulse; - body1.internalGetDeltaLinearVelocity().mVec128 = _mm_add_ps(body1.internalGetDeltaLinearVelocity().mVec128, _mm_mul_ps(linearComponentA, impulseMagnitude)); - body1.internalGetDeltaAngularVelocity().mVec128 = _mm_add_ps(body1.internalGetDeltaAngularVelocity().mVec128, _mm_mul_ps(c.m_angularComponentA.mVec128, impulseMagnitude)); - body2.internalGetDeltaLinearVelocity().mVec128 = _mm_add_ps(body2.internalGetDeltaLinearVelocity().mVec128, _mm_mul_ps(linearComponentB, impulseMagnitude)); - body2.internalGetDeltaAngularVelocity().mVec128 = _mm_add_ps(body2.internalGetDeltaAngularVelocity().mVec128, _mm_mul_ps(c.m_angularComponentB.mVec128, impulseMagnitude)); - return deltaImpulse; + bodyA.internalGetDeltaLinearVelocity().mVec128 = _mm_add_ps(bodyA.internalGetDeltaLinearVelocity().mVec128, _mm_mul_ps(linearComponentA, impulseMagnitude)); + bodyA.internalGetDeltaAngularVelocity().mVec128 = _mm_add_ps(bodyA.internalGetDeltaAngularVelocity().mVec128, _mm_mul_ps(c.m_angularComponentA.mVec128, impulseMagnitude)); + bodyB.internalGetDeltaLinearVelocity().mVec128 = _mm_add_ps(bodyB.internalGetDeltaLinearVelocity().mVec128, _mm_mul_ps(linearComponentB, impulseMagnitude)); + bodyB.internalGetDeltaAngularVelocity().mVec128 = _mm_add_ps(bodyB.internalGetDeltaAngularVelocity().mVec128, _mm_mul_ps(c.m_angularComponentB.mVec128, impulseMagnitude)); + return deltaImpulse.m_floats[0]/c.m_jacDiagABInv; } // Enhanced version of gResolveSingleConstraintRowGeneric_sse2 with SSE4.1 and FMA3 -static btSimdScalar gResolveSingleConstraintRowLowerLimit_sse4_1_fma3(btSolverBody& body1, btSolverBody& body2, const btSolverConstraint& c) +static btScalar gResolveSingleConstraintRowLowerLimit_sse4_1_fma3(btSolverBody& bodyA, btSolverBody& bodyB, const btSolverConstraint& c) { #ifdef BT_ALLOW_SSE4 __m128 tmp = _mm_set_ps1(c.m_jacDiagABInv); __m128 deltaImpulse = _mm_set_ps1(c.m_rhs - btScalar(c.m_appliedImpulse)*c.m_cfm); const __m128 lowerLimit = _mm_set_ps1(c.m_lowerLimit); - const __m128 deltaVel1Dotn = _mm_add_ps(DOT_PRODUCT(c.m_contactNormal1.mVec128, body1.internalGetDeltaLinearVelocity().mVec128), DOT_PRODUCT(c.m_relpos1CrossNormal.mVec128, body1.internalGetDeltaAngularVelocity().mVec128)); - const __m128 deltaVel2Dotn = _mm_add_ps(DOT_PRODUCT(c.m_contactNormal2.mVec128, body2.internalGetDeltaLinearVelocity().mVec128), DOT_PRODUCT(c.m_relpos2CrossNormal.mVec128, body2.internalGetDeltaAngularVelocity().mVec128)); + const __m128 deltaVel1Dotn = _mm_add_ps(DOT_PRODUCT(c.m_contactNormal1.mVec128, bodyA.internalGetDeltaLinearVelocity().mVec128), DOT_PRODUCT(c.m_relpos1CrossNormal.mVec128, bodyA.internalGetDeltaAngularVelocity().mVec128)); + const __m128 deltaVel2Dotn = _mm_add_ps(DOT_PRODUCT(c.m_contactNormal2.mVec128, bodyB.internalGetDeltaLinearVelocity().mVec128), DOT_PRODUCT(c.m_relpos2CrossNormal.mVec128, bodyB.internalGetDeltaAngularVelocity().mVec128)); deltaImpulse = FMNADD(deltaVel1Dotn, tmp, deltaImpulse); deltaImpulse = FMNADD(deltaVel2Dotn, tmp, deltaImpulse); tmp = _mm_add_ps(c.m_appliedImpulse, deltaImpulse); const __m128 mask = _mm_cmpgt_ps(tmp, lowerLimit); deltaImpulse = _mm_blendv_ps(_mm_sub_ps(lowerLimit, c.m_appliedImpulse), deltaImpulse, mask); c.m_appliedImpulse = _mm_blendv_ps(lowerLimit, tmp, mask); - body1.internalGetDeltaLinearVelocity().mVec128 = FMADD(_mm_mul_ps(c.m_contactNormal1.mVec128, body1.internalGetInvMass().mVec128), deltaImpulse, body1.internalGetDeltaLinearVelocity().mVec128); - body1.internalGetDeltaAngularVelocity().mVec128 = FMADD(c.m_angularComponentA.mVec128, deltaImpulse, body1.internalGetDeltaAngularVelocity().mVec128); - body2.internalGetDeltaLinearVelocity().mVec128 = FMADD(_mm_mul_ps(c.m_contactNormal2.mVec128, body2.internalGetInvMass().mVec128), deltaImpulse, body2.internalGetDeltaLinearVelocity().mVec128); - body2.internalGetDeltaAngularVelocity().mVec128 = FMADD(c.m_angularComponentB.mVec128, deltaImpulse, body2.internalGetDeltaAngularVelocity().mVec128); - return deltaImpulse; + bodyA.internalGetDeltaLinearVelocity().mVec128 = FMADD(_mm_mul_ps(c.m_contactNormal1.mVec128, bodyA.internalGetInvMass().mVec128), deltaImpulse, bodyA.internalGetDeltaLinearVelocity().mVec128); + bodyA.internalGetDeltaAngularVelocity().mVec128 = FMADD(c.m_angularComponentA.mVec128, deltaImpulse, bodyA.internalGetDeltaAngularVelocity().mVec128); + bodyB.internalGetDeltaLinearVelocity().mVec128 = FMADD(_mm_mul_ps(c.m_contactNormal2.mVec128, bodyB.internalGetInvMass().mVec128), deltaImpulse, bodyB.internalGetDeltaLinearVelocity().mVec128); + bodyB.internalGetDeltaAngularVelocity().mVec128 = FMADD(c.m_angularComponentB.mVec128, deltaImpulse, bodyB.internalGetDeltaAngularVelocity().mVec128); + btSimdScalar deltaImp = deltaImpulse; + return deltaImp.m_floats[0]*(1./c.m_jacDiagABInv); #else - return gResolveSingleConstraintRowLowerLimit_sse2(body1,body2,c); + return gResolveSingleConstraintRowLowerLimit_sse2(bodyA,bodyB,c); #endif //BT_ALLOW_SSE4 } @@ -267,32 +270,32 @@ static btSimdScalar gResolveSingleConstraintRowLowerLimit_sse4_1_fma3(btSolverBo -btSimdScalar btSequentialImpulseConstraintSolver::resolveSingleConstraintRowGenericSIMD(btSolverBody& body1,btSolverBody& body2,const btSolverConstraint& c) +btScalar btSequentialImpulseConstraintSolver::resolveSingleConstraintRowGenericSIMD(btSolverBody& bodyA,btSolverBody& bodyB,const btSolverConstraint& c) { - return m_resolveSingleConstraintRowGeneric(body1, body2, c); + return m_resolveSingleConstraintRowGeneric(bodyA, bodyB, c); } // Project Gauss Seidel or the equivalent Sequential Impulse -btSimdScalar btSequentialImpulseConstraintSolver::resolveSingleConstraintRowGeneric(btSolverBody& body1,btSolverBody& body2,const btSolverConstraint& c) +btScalar btSequentialImpulseConstraintSolver::resolveSingleConstraintRowGeneric(btSolverBody& bodyA,btSolverBody& bodyB,const btSolverConstraint& c) { - return m_resolveSingleConstraintRowGeneric(body1, body2, c); + return m_resolveSingleConstraintRowGeneric(bodyA, bodyB, c); } -btSimdScalar btSequentialImpulseConstraintSolver::resolveSingleConstraintRowLowerLimitSIMD(btSolverBody& body1,btSolverBody& body2,const btSolverConstraint& c) +btScalar btSequentialImpulseConstraintSolver::resolveSingleConstraintRowLowerLimitSIMD(btSolverBody& bodyA,btSolverBody& bodyB,const btSolverConstraint& c) { - return m_resolveSingleConstraintRowLowerLimit(body1, body2, c); + return m_resolveSingleConstraintRowLowerLimit(bodyA, bodyB, c); } -btSimdScalar btSequentialImpulseConstraintSolver::resolveSingleConstraintRowLowerLimit(btSolverBody& body1,btSolverBody& body2,const btSolverConstraint& c) +btScalar btSequentialImpulseConstraintSolver::resolveSingleConstraintRowLowerLimit(btSolverBody& bodyA,btSolverBody& bodyB,const btSolverConstraint& c) { - return m_resolveSingleConstraintRowLowerLimit(body1, body2, c); + return m_resolveSingleConstraintRowLowerLimit(bodyA, bodyB, c); } -static btSimdScalar gResolveSplitPenetrationImpulse_scalar_reference( - btSolverBody& body1, - btSolverBody& body2, +static btScalar gResolveSplitPenetrationImpulse_scalar_reference( + btSolverBody& bodyA, + btSolverBody& bodyB, const btSolverConstraint& c) { btScalar deltaImpulse = 0.f; @@ -301,8 +304,8 @@ static btSimdScalar gResolveSplitPenetrationImpulse_scalar_reference( { gNumSplitImpulseRecoveries++; deltaImpulse = c.m_rhsPenetration-btScalar(c.m_appliedPushImpulse)*c.m_cfm; - const btScalar deltaVel1Dotn = c.m_contactNormal1.dot(body1.internalGetPushVelocity()) + c.m_relpos1CrossNormal.dot(body1.internalGetTurnVelocity()); - const btScalar deltaVel2Dotn = c.m_contactNormal2.dot(body2.internalGetPushVelocity()) + c.m_relpos2CrossNormal.dot(body2.internalGetTurnVelocity()); + const btScalar deltaVel1Dotn = c.m_contactNormal1.dot(bodyA.internalGetPushVelocity()) + c.m_relpos1CrossNormal.dot(bodyA.internalGetTurnVelocity()); + const btScalar deltaVel2Dotn = c.m_contactNormal2.dot(bodyB.internalGetPushVelocity()) + c.m_relpos2CrossNormal.dot(bodyB.internalGetTurnVelocity()); deltaImpulse -= deltaVel1Dotn*c.m_jacDiagABInv; deltaImpulse -= deltaVel2Dotn*c.m_jacDiagABInv; @@ -316,13 +319,13 @@ static btSimdScalar gResolveSplitPenetrationImpulse_scalar_reference( { c.m_appliedPushImpulse = sum; } - body1.internalApplyPushImpulse(c.m_contactNormal1*body1.internalGetInvMass(),c.m_angularComponentA,deltaImpulse); - body2.internalApplyPushImpulse(c.m_contactNormal2*body2.internalGetInvMass(),c.m_angularComponentB,deltaImpulse); + bodyA.internalApplyPushImpulse(c.m_contactNormal1*bodyA.internalGetInvMass(),c.m_angularComponentA,deltaImpulse); + bodyB.internalApplyPushImpulse(c.m_contactNormal2*bodyB.internalGetInvMass(),c.m_angularComponentB,deltaImpulse); } - return deltaImpulse; + return deltaImpulse*(1./c.m_jacDiagABInv); } -static btSimdScalar gResolveSplitPenetrationImpulse_sse2(btSolverBody& body1,btSolverBody& body2,const btSolverConstraint& c) +static btScalar gResolveSplitPenetrationImpulse_sse2(btSolverBody& bodyA,btSolverBody& bodyB,const btSolverConstraint& c) { #ifdef USE_SIMD if (!c.m_rhsPenetration) @@ -334,8 +337,8 @@ static btSimdScalar gResolveSplitPenetrationImpulse_sse2(btSolverBody& body1,btS __m128 lowerLimit1 = _mm_set1_ps(c.m_lowerLimit); __m128 upperLimit1 = _mm_set1_ps(c.m_upperLimit); __m128 deltaImpulse = _mm_sub_ps(_mm_set1_ps(c.m_rhsPenetration), _mm_mul_ps(_mm_set1_ps(c.m_appliedPushImpulse),_mm_set1_ps(c.m_cfm))); - __m128 deltaVel1Dotn = _mm_add_ps(btSimdDot3(c.m_contactNormal1.mVec128,body1.internalGetPushVelocity().mVec128), btSimdDot3(c.m_relpos1CrossNormal.mVec128,body1.internalGetTurnVelocity().mVec128)); - __m128 deltaVel2Dotn = _mm_add_ps(btSimdDot3(c.m_contactNormal2.mVec128,body2.internalGetPushVelocity().mVec128), btSimdDot3(c.m_relpos2CrossNormal.mVec128,body2.internalGetTurnVelocity().mVec128)); + __m128 deltaVel1Dotn = _mm_add_ps(btSimdDot3(c.m_contactNormal1.mVec128,bodyA.internalGetPushVelocity().mVec128), btSimdDot3(c.m_relpos1CrossNormal.mVec128,bodyA.internalGetTurnVelocity().mVec128)); + __m128 deltaVel2Dotn = _mm_add_ps(btSimdDot3(c.m_contactNormal2.mVec128,bodyB.internalGetPushVelocity().mVec128), btSimdDot3(c.m_relpos2CrossNormal.mVec128,bodyB.internalGetTurnVelocity().mVec128)); deltaImpulse = _mm_sub_ps(deltaImpulse,_mm_mul_ps(deltaVel1Dotn,_mm_set1_ps(c.m_jacDiagABInv))); deltaImpulse = _mm_sub_ps(deltaImpulse,_mm_mul_ps(deltaVel2Dotn,_mm_set1_ps(c.m_jacDiagABInv))); btSimdScalar sum = _mm_add_ps(cpAppliedImp,deltaImpulse); @@ -345,16 +348,17 @@ static btSimdScalar gResolveSplitPenetrationImpulse_sse2(btSolverBody& body1,btS __m128 lowMinApplied = _mm_sub_ps(lowerLimit1,cpAppliedImp); deltaImpulse = _mm_or_ps( _mm_and_ps(resultLowerLess, lowMinApplied), _mm_andnot_ps(resultLowerLess, deltaImpulse) ); c.m_appliedPushImpulse = _mm_or_ps( _mm_and_ps(resultLowerLess, lowerLimit1), _mm_andnot_ps(resultLowerLess, sum) ); - __m128 linearComponentA = _mm_mul_ps(c.m_contactNormal1.mVec128,body1.internalGetInvMass().mVec128); - __m128 linearComponentB = _mm_mul_ps(c.m_contactNormal2.mVec128,body2.internalGetInvMass().mVec128); + __m128 linearComponentA = _mm_mul_ps(c.m_contactNormal1.mVec128,bodyA.internalGetInvMass().mVec128); + __m128 linearComponentB = _mm_mul_ps(c.m_contactNormal2.mVec128,bodyB.internalGetInvMass().mVec128); __m128 impulseMagnitude = deltaImpulse; - body1.internalGetPushVelocity().mVec128 = _mm_add_ps(body1.internalGetPushVelocity().mVec128,_mm_mul_ps(linearComponentA,impulseMagnitude)); - body1.internalGetTurnVelocity().mVec128 = _mm_add_ps(body1.internalGetTurnVelocity().mVec128 ,_mm_mul_ps(c.m_angularComponentA.mVec128,impulseMagnitude)); - body2.internalGetPushVelocity().mVec128 = _mm_add_ps(body2.internalGetPushVelocity().mVec128,_mm_mul_ps(linearComponentB,impulseMagnitude)); - body2.internalGetTurnVelocity().mVec128 = _mm_add_ps(body2.internalGetTurnVelocity().mVec128 ,_mm_mul_ps(c.m_angularComponentB.mVec128,impulseMagnitude)); - return deltaImpulse; + bodyA.internalGetPushVelocity().mVec128 = _mm_add_ps(bodyA.internalGetPushVelocity().mVec128,_mm_mul_ps(linearComponentA,impulseMagnitude)); + bodyA.internalGetTurnVelocity().mVec128 = _mm_add_ps(bodyA.internalGetTurnVelocity().mVec128 ,_mm_mul_ps(c.m_angularComponentA.mVec128,impulseMagnitude)); + bodyB.internalGetPushVelocity().mVec128 = _mm_add_ps(bodyB.internalGetPushVelocity().mVec128,_mm_mul_ps(linearComponentB,impulseMagnitude)); + bodyB.internalGetTurnVelocity().mVec128 = _mm_add_ps(bodyB.internalGetTurnVelocity().mVec128 ,_mm_mul_ps(c.m_angularComponentB.mVec128,impulseMagnitude)); + btSimdScalar deltaImp = deltaImpulse; + return deltaImp.m_floats[0] * (1. / c.m_jacDiagABInv); #else - return gResolveSplitPenetrationImpulse_scalar_reference(body1,body2,c); + return gResolveSplitPenetrationImpulse_scalar_reference(bodyA,bodyB,c); #endif } @@ -548,7 +552,7 @@ void btSequentialImpulseConstraintSolver::setupFrictionConstraint(btSolverConstr btSolverBody& solverBodyB = m_tmpSolverBodyPool[solverBodyIdB]; btRigidBody* body0 = m_tmpSolverBodyPool[solverBodyIdA].m_originalBody; - btRigidBody* body1 = m_tmpSolverBodyPool[solverBodyIdB].m_originalBody; + btRigidBody* bodyA = m_tmpSolverBodyPool[solverBodyIdB].m_originalBody; solverConstraint.m_solverBodyIdA = solverBodyIdA; solverConstraint.m_solverBodyIdB = solverBodyIdB; @@ -572,12 +576,12 @@ void btSequentialImpulseConstraintSolver::setupFrictionConstraint(btSolverConstr solverConstraint.m_angularComponentA .setZero(); } - if (body1) + if (bodyA) { solverConstraint.m_contactNormal2 = -normalAxis; btVector3 ftorqueAxis1 = rel_pos2.cross(solverConstraint.m_contactNormal2); solverConstraint.m_relpos2CrossNormal = ftorqueAxis1; - solverConstraint.m_angularComponentB = body1->getInvInertiaTensorWorld()*ftorqueAxis1*body1->getAngularFactor(); + solverConstraint.m_angularComponentB = bodyA->getInvInertiaTensorWorld()*ftorqueAxis1*bodyA->getAngularFactor(); } else { solverConstraint.m_contactNormal2.setZero(); @@ -594,10 +598,10 @@ void btSequentialImpulseConstraintSolver::setupFrictionConstraint(btSolverConstr vec = ( solverConstraint.m_angularComponentA).cross(rel_pos1); denom0 = body0->getInvMass() + normalAxis.dot(vec); } - if (body1) + if (bodyA) { vec = ( -solverConstraint.m_angularComponentB).cross(rel_pos2); - denom1 = body1->getInvMass() + normalAxis.dot(vec); + denom1 = bodyA->getInvMass() + normalAxis.dot(vec); } btScalar denom = relaxation/(denom0+denom1); solverConstraint.m_jacDiagABInv = denom; @@ -609,8 +613,8 @@ void btSequentialImpulseConstraintSolver::setupFrictionConstraint(btSolverConstr btScalar rel_vel; btScalar vel1Dotn = solverConstraint.m_contactNormal1.dot(body0?solverBodyA.m_linearVelocity+solverBodyA.m_externalForceImpulse:btVector3(0,0,0)) + solverConstraint.m_relpos1CrossNormal.dot(body0?solverBodyA.m_angularVelocity:btVector3(0,0,0)); - btScalar vel2Dotn = solverConstraint.m_contactNormal2.dot(body1?solverBodyB.m_linearVelocity+solverBodyB.m_externalForceImpulse:btVector3(0,0,0)) - + solverConstraint.m_relpos2CrossNormal.dot(body1?solverBodyB.m_angularVelocity:btVector3(0,0,0)); + btScalar vel2Dotn = solverConstraint.m_contactNormal2.dot(bodyA?solverBodyB.m_linearVelocity+solverBodyB.m_externalForceImpulse:btVector3(0,0,0)) + + solverConstraint.m_relpos2CrossNormal.dot(bodyA?solverBodyB.m_angularVelocity:btVector3(0,0,0)); rel_vel = vel1Dotn+vel2Dotn; @@ -662,7 +666,7 @@ void btSequentialImpulseConstraintSolver::setupTorsionalFrictionConstraint( btSo btSolverBody& solverBodyB = m_tmpSolverBodyPool[solverBodyIdB]; btRigidBody* body0 = m_tmpSolverBodyPool[solverBodyIdA].m_originalBody; - btRigidBody* body1 = m_tmpSolverBodyPool[solverBodyIdB].m_originalBody; + btRigidBody* bodyA = m_tmpSolverBodyPool[solverBodyIdB].m_originalBody; solverConstraint.m_solverBodyIdA = solverBodyIdA; solverConstraint.m_solverBodyIdB = solverBodyIdB; @@ -681,13 +685,13 @@ void btSequentialImpulseConstraintSolver::setupTorsionalFrictionConstraint( btSo { btVector3 ftorqueAxis1 = normalAxis1; solverConstraint.m_relpos2CrossNormal = ftorqueAxis1; - solverConstraint.m_angularComponentB = body1 ? body1->getInvInertiaTensorWorld()*ftorqueAxis1*body1->getAngularFactor() : btVector3(0,0,0); + solverConstraint.m_angularComponentB = bodyA ? bodyA->getInvInertiaTensorWorld()*ftorqueAxis1*bodyA->getAngularFactor() : btVector3(0,0,0); } { btVector3 iMJaA = body0?body0->getInvInertiaTensorWorld()*solverConstraint.m_relpos1CrossNormal:btVector3(0,0,0); - btVector3 iMJaB = body1?body1->getInvInertiaTensorWorld()*solverConstraint.m_relpos2CrossNormal:btVector3(0,0,0); + btVector3 iMJaB = bodyA?bodyA->getInvInertiaTensorWorld()*solverConstraint.m_relpos2CrossNormal:btVector3(0,0,0); btScalar sum = 0; sum += iMJaA.dot(solverConstraint.m_relpos1CrossNormal); sum += iMJaB.dot(solverConstraint.m_relpos2CrossNormal); @@ -700,8 +704,8 @@ void btSequentialImpulseConstraintSolver::setupTorsionalFrictionConstraint( btSo btScalar rel_vel; btScalar vel1Dotn = solverConstraint.m_contactNormal1.dot(body0?solverBodyA.m_linearVelocity+solverBodyA.m_externalForceImpulse:btVector3(0,0,0)) + solverConstraint.m_relpos1CrossNormal.dot(body0?solverBodyA.m_angularVelocity:btVector3(0,0,0)); - btScalar vel2Dotn = solverConstraint.m_contactNormal2.dot(body1?solverBodyB.m_linearVelocity+solverBodyB.m_externalForceImpulse:btVector3(0,0,0)) - + solverConstraint.m_relpos2CrossNormal.dot(body1?solverBodyB.m_angularVelocity:btVector3(0,0,0)); + btScalar vel2Dotn = solverConstraint.m_contactNormal2.dot(bodyA?solverBodyB.m_linearVelocity+solverBodyB.m_externalForceImpulse:btVector3(0,0,0)) + + solverConstraint.m_relpos2CrossNormal.dot(bodyA?solverBodyB.m_angularVelocity:btVector3(0,0,0)); rel_vel = vel1Dotn+vel2Dotn; @@ -738,23 +742,21 @@ int btSequentialImpulseConstraintSolver::getOrInitSolverBody(btCollisionObject& { #if BT_THREADSAFE int solverBodyId = -1; - if ( !body.isStaticOrKinematicObject() ) + bool isRigidBodyType = btRigidBody::upcast( &body ) != NULL; + if ( isRigidBodyType && !body.isStaticOrKinematicObject() ) { // dynamic body // Dynamic bodies can only be in one island, so it's safe to write to the companionId solverBodyId = body.getCompanionId(); if ( solverBodyId < 0 ) { - if ( btRigidBody* rb = btRigidBody::upcast( &body ) ) - { - solverBodyId = m_tmpSolverBodyPool.size(); - btSolverBody& solverBody = m_tmpSolverBodyPool.expand(); - initSolverBody( &solverBody, &body, timeStep ); - body.setCompanionId( solverBodyId ); - } + solverBodyId = m_tmpSolverBodyPool.size(); + btSolverBody& solverBody = m_tmpSolverBodyPool.expand(); + initSolverBody( &solverBody, &body, timeStep ); + body.setCompanionId( solverBodyId ); } } - else if (body.isKinematicObject()) + else if (isRigidBodyType && body.isKinematicObject()) { // // NOTE: must test for kinematic before static because some kinematic objects also @@ -774,7 +776,6 @@ int btSequentialImpulseConstraintSolver::getOrInitSolverBody(btCollisionObject& if ( solverBodyId == INVALID_SOLVER_BODY_ID ) { // create a table entry for this body - btRigidBody* rb = btRigidBody::upcast( &body ); solverBodyId = m_tmpSolverBodyPool.size(); btSolverBody& solverBody = m_tmpSolverBodyPool.expand(); initSolverBody( &solverBody, &body, timeStep ); @@ -783,6 +784,13 @@ int btSequentialImpulseConstraintSolver::getOrInitSolverBody(btCollisionObject& } else { + bool isMultiBodyType = (body.getInternalType()&btCollisionObject::CO_FEATHERSTONE_LINK); + // Incorrectly set collision object flags can degrade performance in various ways. + if (!isMultiBodyType) + { + btAssert( body.isStaticOrKinematicObject() ); + } + //it could be a multibody link collider // all fixed bodies (inf mass) get mapped to a single solver id if ( m_fixedBodyId < 0 ) { @@ -792,7 +800,7 @@ int btSequentialImpulseConstraintSolver::getOrInitSolverBody(btCollisionObject& } solverBodyId = m_fixedBodyId; } - btAssert( solverBodyId < m_tmpSolverBodyPool.size() ); + btAssert( solverBodyId >= 0 && solverBodyId < m_tmpSolverBodyPool.size() ); return solverBodyId; #else // BT_THREADSAFE @@ -1258,6 +1266,256 @@ void btSequentialImpulseConstraintSolver::convertContacts(btPersistentManifold** } } + +void btSequentialImpulseConstraintSolver::convertJoint(btSolverConstraint* currentConstraintRow, + btTypedConstraint* constraint, + const btTypedConstraint::btConstraintInfo1& info1, + int solverBodyIdA, + int solverBodyIdB, + const btContactSolverInfo& infoGlobal + ) +{ + const btRigidBody& rbA = constraint->getRigidBodyA(); + const btRigidBody& rbB = constraint->getRigidBodyB(); + + const btSolverBody* bodyAPtr = &m_tmpSolverBodyPool[solverBodyIdA]; + const btSolverBody* bodyBPtr = &m_tmpSolverBodyPool[solverBodyIdB]; + + int overrideNumSolverIterations = constraint->getOverrideNumSolverIterations() > 0 ? constraint->getOverrideNumSolverIterations() : infoGlobal.m_numIterations; + if (overrideNumSolverIterations>m_maxOverrideNumSolverIterations) + m_maxOverrideNumSolverIterations = overrideNumSolverIterations; + + for (int j=0;j<info1.m_numConstraintRows;j++) + { + memset(¤tConstraintRow[j],0,sizeof(btSolverConstraint)); + currentConstraintRow[j].m_lowerLimit = -SIMD_INFINITY; + currentConstraintRow[j].m_upperLimit = SIMD_INFINITY; + currentConstraintRow[j].m_appliedImpulse = 0.f; + currentConstraintRow[j].m_appliedPushImpulse = 0.f; + currentConstraintRow[j].m_solverBodyIdA = solverBodyIdA; + currentConstraintRow[j].m_solverBodyIdB = solverBodyIdB; + currentConstraintRow[j].m_overrideNumSolverIterations = overrideNumSolverIterations; + } + + // these vectors are already cleared in initSolverBody, no need to redundantly clear again + btAssert(bodyAPtr->getDeltaLinearVelocity().isZero()); + btAssert(bodyAPtr->getDeltaAngularVelocity().isZero()); + btAssert(bodyAPtr->getPushVelocity().isZero()); + btAssert(bodyAPtr->getTurnVelocity().isZero()); + btAssert(bodyBPtr->getDeltaLinearVelocity().isZero()); + btAssert(bodyBPtr->getDeltaAngularVelocity().isZero()); + btAssert(bodyBPtr->getPushVelocity().isZero()); + btAssert(bodyBPtr->getTurnVelocity().isZero()); + //bodyAPtr->internalGetDeltaLinearVelocity().setValue(0.f,0.f,0.f); + //bodyAPtr->internalGetDeltaAngularVelocity().setValue(0.f,0.f,0.f); + //bodyAPtr->internalGetPushVelocity().setValue(0.f,0.f,0.f); + //bodyAPtr->internalGetTurnVelocity().setValue(0.f,0.f,0.f); + //bodyBPtr->internalGetDeltaLinearVelocity().setValue(0.f,0.f,0.f); + //bodyBPtr->internalGetDeltaAngularVelocity().setValue(0.f,0.f,0.f); + //bodyBPtr->internalGetPushVelocity().setValue(0.f,0.f,0.f); + //bodyBPtr->internalGetTurnVelocity().setValue(0.f,0.f,0.f); + + + btTypedConstraint::btConstraintInfo2 info2; + info2.fps = 1.f/infoGlobal.m_timeStep; + info2.erp = infoGlobal.m_erp; + info2.m_J1linearAxis = currentConstraintRow->m_contactNormal1; + info2.m_J1angularAxis = currentConstraintRow->m_relpos1CrossNormal; + info2.m_J2linearAxis = currentConstraintRow->m_contactNormal2; + info2.m_J2angularAxis = currentConstraintRow->m_relpos2CrossNormal; + info2.rowskip = sizeof(btSolverConstraint)/sizeof(btScalar);//check this + ///the size of btSolverConstraint needs be a multiple of btScalar + btAssert(info2.rowskip*sizeof(btScalar)== sizeof(btSolverConstraint)); + info2.m_constraintError = ¤tConstraintRow->m_rhs; + currentConstraintRow->m_cfm = infoGlobal.m_globalCfm; + info2.m_damping = infoGlobal.m_damping; + info2.cfm = ¤tConstraintRow->m_cfm; + info2.m_lowerLimit = ¤tConstraintRow->m_lowerLimit; + info2.m_upperLimit = ¤tConstraintRow->m_upperLimit; + info2.m_numIterations = infoGlobal.m_numIterations; + constraint->getInfo2(&info2); + + ///finalize the constraint setup + for (int j=0;j<info1.m_numConstraintRows;j++) + { + btSolverConstraint& solverConstraint = currentConstraintRow[j]; + + if (solverConstraint.m_upperLimit>=constraint->getBreakingImpulseThreshold()) + { + solverConstraint.m_upperLimit = constraint->getBreakingImpulseThreshold(); + } + + if (solverConstraint.m_lowerLimit<=-constraint->getBreakingImpulseThreshold()) + { + solverConstraint.m_lowerLimit = -constraint->getBreakingImpulseThreshold(); + } + + solverConstraint.m_originalContactPoint = constraint; + + { + const btVector3& ftorqueAxis1 = solverConstraint.m_relpos1CrossNormal; + solverConstraint.m_angularComponentA = constraint->getRigidBodyA().getInvInertiaTensorWorld()*ftorqueAxis1*constraint->getRigidBodyA().getAngularFactor(); + } + { + const btVector3& ftorqueAxis2 = solverConstraint.m_relpos2CrossNormal; + solverConstraint.m_angularComponentB = constraint->getRigidBodyB().getInvInertiaTensorWorld()*ftorqueAxis2*constraint->getRigidBodyB().getAngularFactor(); + } + + { + btVector3 iMJlA = solverConstraint.m_contactNormal1*rbA.getInvMass(); + btVector3 iMJaA = rbA.getInvInertiaTensorWorld()*solverConstraint.m_relpos1CrossNormal; + btVector3 iMJlB = solverConstraint.m_contactNormal2*rbB.getInvMass();//sign of normal? + btVector3 iMJaB = rbB.getInvInertiaTensorWorld()*solverConstraint.m_relpos2CrossNormal; + + btScalar sum = iMJlA.dot(solverConstraint.m_contactNormal1); + sum += iMJaA.dot(solverConstraint.m_relpos1CrossNormal); + sum += iMJlB.dot(solverConstraint.m_contactNormal2); + sum += iMJaB.dot(solverConstraint.m_relpos2CrossNormal); + btScalar fsum = btFabs(sum); + btAssert(fsum > SIMD_EPSILON); + btScalar sorRelaxation = 1.f;//todo: get from globalInfo? + solverConstraint.m_jacDiagABInv = fsum>SIMD_EPSILON?sorRelaxation/sum : 0.f; + } + + { + btScalar rel_vel; + btVector3 externalForceImpulseA = bodyAPtr->m_originalBody ? bodyAPtr->m_externalForceImpulse : btVector3(0,0,0); + btVector3 externalTorqueImpulseA = bodyAPtr->m_originalBody ? bodyAPtr->m_externalTorqueImpulse : btVector3(0,0,0); + + btVector3 externalForceImpulseB = bodyBPtr->m_originalBody ? bodyBPtr->m_externalForceImpulse : btVector3(0,0,0); + btVector3 externalTorqueImpulseB = bodyBPtr->m_originalBody ?bodyBPtr->m_externalTorqueImpulse : btVector3(0,0,0); + + btScalar vel1Dotn = solverConstraint.m_contactNormal1.dot(rbA.getLinearVelocity()+externalForceImpulseA) + + solverConstraint.m_relpos1CrossNormal.dot(rbA.getAngularVelocity()+externalTorqueImpulseA); + + btScalar vel2Dotn = solverConstraint.m_contactNormal2.dot(rbB.getLinearVelocity()+externalForceImpulseB) + + solverConstraint.m_relpos2CrossNormal.dot(rbB.getAngularVelocity()+externalTorqueImpulseB); + + rel_vel = vel1Dotn+vel2Dotn; + btScalar restitution = 0.f; + btScalar positionalError = solverConstraint.m_rhs;//already filled in by getConstraintInfo2 + btScalar velocityError = restitution - rel_vel * info2.m_damping; + btScalar penetrationImpulse = positionalError*solverConstraint.m_jacDiagABInv; + btScalar velocityImpulse = velocityError *solverConstraint.m_jacDiagABInv; + solverConstraint.m_rhs = penetrationImpulse+velocityImpulse; + solverConstraint.m_appliedImpulse = 0.f; + } + } +} + + +void btSequentialImpulseConstraintSolver::convertJoints(btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal) +{ + BT_PROFILE("convertJoints"); + for (int j=0;j<numConstraints;j++) + { + btTypedConstraint* constraint = constraints[j]; + constraint->buildJacobian(); + constraint->internalSetAppliedImpulse(0.0f); + } + + int totalNumRows = 0; + + m_tmpConstraintSizesPool.resizeNoInitialize(numConstraints); + //calculate the total number of contraint rows + for (int i=0;i<numConstraints;i++) + { + btTypedConstraint::btConstraintInfo1& info1 = m_tmpConstraintSizesPool[i]; + btJointFeedback* fb = constraints[i]->getJointFeedback(); + if (fb) + { + fb->m_appliedForceBodyA.setZero(); + fb->m_appliedTorqueBodyA.setZero(); + fb->m_appliedForceBodyB.setZero(); + fb->m_appliedTorqueBodyB.setZero(); + } + + if (constraints[i]->isEnabled()) + { + constraints[i]->getInfo1(&info1); + } else + { + info1.m_numConstraintRows = 0; + info1.nub = 0; + } + totalNumRows += info1.m_numConstraintRows; + } + m_tmpSolverNonContactConstraintPool.resizeNoInitialize(totalNumRows); + + + ///setup the btSolverConstraints + int currentRow = 0; + + for (int i=0;i<numConstraints;i++) + { + const btTypedConstraint::btConstraintInfo1& info1 = m_tmpConstraintSizesPool[i]; + + if (info1.m_numConstraintRows) + { + btAssert(currentRow<totalNumRows); + + btSolverConstraint* currentConstraintRow = &m_tmpSolverNonContactConstraintPool[currentRow]; + btTypedConstraint* constraint = constraints[i]; + btRigidBody& rbA = constraint->getRigidBodyA(); + btRigidBody& rbB = constraint->getRigidBodyB(); + + int solverBodyIdA = getOrInitSolverBody(rbA,infoGlobal.m_timeStep); + int solverBodyIdB = getOrInitSolverBody(rbB,infoGlobal.m_timeStep); + + convertJoint(currentConstraintRow, constraint, info1, solverBodyIdA, solverBodyIdB, infoGlobal); + } + currentRow+=info1.m_numConstraintRows; + } +} + + +void btSequentialImpulseConstraintSolver::convertBodies(btCollisionObject** bodies, int numBodies, const btContactSolverInfo& infoGlobal) +{ + BT_PROFILE("convertBodies"); + for (int i = 0; i < numBodies; i++) + { + bodies[i]->setCompanionId(-1); + } +#if BT_THREADSAFE + m_kinematicBodyUniqueIdToSolverBodyTable.resize( 0 ); +#endif // BT_THREADSAFE + + m_tmpSolverBodyPool.reserve(numBodies+1); + m_tmpSolverBodyPool.resize(0); + + //btSolverBody& fixedBody = m_tmpSolverBodyPool.expand(); + //initSolverBody(&fixedBody,0); + + for (int i=0;i<numBodies;i++) + { + int bodyId = getOrInitSolverBody(*bodies[i],infoGlobal.m_timeStep); + + btRigidBody* body = btRigidBody::upcast(bodies[i]); + if (body && body->getInvMass()) + { + btSolverBody& solverBody = m_tmpSolverBodyPool[bodyId]; + btVector3 gyroForce (0,0,0); + if (body->getFlags()&BT_ENABLE_GYROSCOPIC_FORCE_EXPLICIT) + { + gyroForce = body->computeGyroscopicForceExplicit(infoGlobal.m_maxGyroscopicForce); + solverBody.m_externalTorqueImpulse -= gyroForce*body->getInvInertiaTensorWorld()*infoGlobal.m_timeStep; + } + if (body->getFlags()&BT_ENABLE_GYROSCOPIC_FORCE_IMPLICIT_WORLD) + { + gyroForce = body->computeGyroscopicImpulseImplicit_World(infoGlobal.m_timeStep); + solverBody.m_externalTorqueImpulse += gyroForce; + } + if (body->getFlags()&BT_ENABLE_GYROSCOPIC_FORCE_IMPLICIT_BODY) + { + gyroForce = body->computeGyroscopicImpulseImplicit_Body(infoGlobal.m_timeStep); + solverBody.m_externalTorqueImpulse += gyroForce; + + } + } + } +} + + btScalar btSequentialImpulseConstraintSolver::solveGroupCacheFriendlySetup(btCollisionObject** bodies, int numBodies, btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer) { m_fixedBodyId = -1; @@ -1344,254 +1602,14 @@ btScalar btSequentialImpulseConstraintSolver::solveGroupCacheFriendlySetup(btCol #endif //BT_ADDITIONAL_DEBUG - for (int i = 0; i < numBodies; i++) - { - bodies[i]->setCompanionId(-1); - } -#if BT_THREADSAFE - m_kinematicBodyUniqueIdToSolverBodyTable.resize( 0 ); -#endif // BT_THREADSAFE - - m_tmpSolverBodyPool.reserve(numBodies+1); - m_tmpSolverBodyPool.resize(0); - - //btSolverBody& fixedBody = m_tmpSolverBodyPool.expand(); - //initSolverBody(&fixedBody,0); - //convert all bodies + convertBodies(bodies, numBodies, infoGlobal); + convertJoints(constraints, numConstraints, infoGlobal); - for (int i=0;i<numBodies;i++) - { - int bodyId = getOrInitSolverBody(*bodies[i],infoGlobal.m_timeStep); - - btRigidBody* body = btRigidBody::upcast(bodies[i]); - if (body && body->getInvMass()) - { - btSolverBody& solverBody = m_tmpSolverBodyPool[bodyId]; - btVector3 gyroForce (0,0,0); - if (body->getFlags()&BT_ENABLE_GYROSCOPIC_FORCE_EXPLICIT) - { - gyroForce = body->computeGyroscopicForceExplicit(infoGlobal.m_maxGyroscopicForce); - solverBody.m_externalTorqueImpulse -= gyroForce*body->getInvInertiaTensorWorld()*infoGlobal.m_timeStep; - } - if (body->getFlags()&BT_ENABLE_GYROSCOPIC_FORCE_IMPLICIT_WORLD) - { - gyroForce = body->computeGyroscopicImpulseImplicit_World(infoGlobal.m_timeStep); - solverBody.m_externalTorqueImpulse += gyroForce; - } - if (body->getFlags()&BT_ENABLE_GYROSCOPIC_FORCE_IMPLICIT_BODY) - { - gyroForce = body->computeGyroscopicImpulseImplicit_Body(infoGlobal.m_timeStep); - solverBody.m_externalTorqueImpulse += gyroForce; - - } - - - } - } - - if (1) - { - int j; - for (j=0;j<numConstraints;j++) - { - btTypedConstraint* constraint = constraints[j]; - constraint->buildJacobian(); - constraint->internalSetAppliedImpulse(0.0f); - } - } - - //btRigidBody* rb0=0,*rb1=0; - - //if (1) - { - { - - int totalNumRows = 0; - int i; - - m_tmpConstraintSizesPool.resizeNoInitialize(numConstraints); - //calculate the total number of contraint rows - for (i=0;i<numConstraints;i++) - { - btTypedConstraint::btConstraintInfo1& info1 = m_tmpConstraintSizesPool[i]; - btJointFeedback* fb = constraints[i]->getJointFeedback(); - if (fb) - { - fb->m_appliedForceBodyA.setZero(); - fb->m_appliedTorqueBodyA.setZero(); - fb->m_appliedForceBodyB.setZero(); - fb->m_appliedTorqueBodyB.setZero(); - } - - if (constraints[i]->isEnabled()) - { - } - if (constraints[i]->isEnabled()) - { - constraints[i]->getInfo1(&info1); - } else - { - info1.m_numConstraintRows = 0; - info1.nub = 0; - } - totalNumRows += info1.m_numConstraintRows; - } - m_tmpSolverNonContactConstraintPool.resizeNoInitialize(totalNumRows); - - - ///setup the btSolverConstraints - int currentRow = 0; - - for (i=0;i<numConstraints;i++) - { - const btTypedConstraint::btConstraintInfo1& info1 = m_tmpConstraintSizesPool[i]; - - if (info1.m_numConstraintRows) - { - btAssert(currentRow<totalNumRows); - - btSolverConstraint* currentConstraintRow = &m_tmpSolverNonContactConstraintPool[currentRow]; - btTypedConstraint* constraint = constraints[i]; - btRigidBody& rbA = constraint->getRigidBodyA(); - btRigidBody& rbB = constraint->getRigidBodyB(); - - int solverBodyIdA = getOrInitSolverBody(rbA,infoGlobal.m_timeStep); - int solverBodyIdB = getOrInitSolverBody(rbB,infoGlobal.m_timeStep); - - btSolverBody* bodyAPtr = &m_tmpSolverBodyPool[solverBodyIdA]; - btSolverBody* bodyBPtr = &m_tmpSolverBodyPool[solverBodyIdB]; + convertContacts(manifoldPtr,numManifolds,infoGlobal); - - - int overrideNumSolverIterations = constraint->getOverrideNumSolverIterations() > 0 ? constraint->getOverrideNumSolverIterations() : infoGlobal.m_numIterations; - if (overrideNumSolverIterations>m_maxOverrideNumSolverIterations) - m_maxOverrideNumSolverIterations = overrideNumSolverIterations; - - - int j; - for ( j=0;j<info1.m_numConstraintRows;j++) - { - memset(¤tConstraintRow[j],0,sizeof(btSolverConstraint)); - currentConstraintRow[j].m_lowerLimit = -SIMD_INFINITY; - currentConstraintRow[j].m_upperLimit = SIMD_INFINITY; - currentConstraintRow[j].m_appliedImpulse = 0.f; - currentConstraintRow[j].m_appliedPushImpulse = 0.f; - currentConstraintRow[j].m_solverBodyIdA = solverBodyIdA; - currentConstraintRow[j].m_solverBodyIdB = solverBodyIdB; - currentConstraintRow[j].m_overrideNumSolverIterations = overrideNumSolverIterations; - } - - bodyAPtr->internalGetDeltaLinearVelocity().setValue(0.f,0.f,0.f); - bodyAPtr->internalGetDeltaAngularVelocity().setValue(0.f,0.f,0.f); - bodyAPtr->internalGetPushVelocity().setValue(0.f,0.f,0.f); - bodyAPtr->internalGetTurnVelocity().setValue(0.f,0.f,0.f); - bodyBPtr->internalGetDeltaLinearVelocity().setValue(0.f,0.f,0.f); - bodyBPtr->internalGetDeltaAngularVelocity().setValue(0.f,0.f,0.f); - bodyBPtr->internalGetPushVelocity().setValue(0.f,0.f,0.f); - bodyBPtr->internalGetTurnVelocity().setValue(0.f,0.f,0.f); - - - btTypedConstraint::btConstraintInfo2 info2; - info2.fps = 1.f/infoGlobal.m_timeStep; - info2.erp = infoGlobal.m_erp; - info2.m_J1linearAxis = currentConstraintRow->m_contactNormal1; - info2.m_J1angularAxis = currentConstraintRow->m_relpos1CrossNormal; - info2.m_J2linearAxis = currentConstraintRow->m_contactNormal2; - info2.m_J2angularAxis = currentConstraintRow->m_relpos2CrossNormal; - info2.rowskip = sizeof(btSolverConstraint)/sizeof(btScalar);//check this - ///the size of btSolverConstraint needs be a multiple of btScalar - btAssert(info2.rowskip*sizeof(btScalar)== sizeof(btSolverConstraint)); - info2.m_constraintError = ¤tConstraintRow->m_rhs; - currentConstraintRow->m_cfm = infoGlobal.m_globalCfm; - info2.m_damping = infoGlobal.m_damping; - info2.cfm = ¤tConstraintRow->m_cfm; - info2.m_lowerLimit = ¤tConstraintRow->m_lowerLimit; - info2.m_upperLimit = ¤tConstraintRow->m_upperLimit; - info2.m_numIterations = infoGlobal.m_numIterations; - constraints[i]->getInfo2(&info2); - - ///finalize the constraint setup - for ( j=0;j<info1.m_numConstraintRows;j++) - { - btSolverConstraint& solverConstraint = currentConstraintRow[j]; - - if (solverConstraint.m_upperLimit>=constraints[i]->getBreakingImpulseThreshold()) - { - solverConstraint.m_upperLimit = constraints[i]->getBreakingImpulseThreshold(); - } - - if (solverConstraint.m_lowerLimit<=-constraints[i]->getBreakingImpulseThreshold()) - { - solverConstraint.m_lowerLimit = -constraints[i]->getBreakingImpulseThreshold(); - } - - solverConstraint.m_originalContactPoint = constraint; - - { - const btVector3& ftorqueAxis1 = solverConstraint.m_relpos1CrossNormal; - solverConstraint.m_angularComponentA = constraint->getRigidBodyA().getInvInertiaTensorWorld()*ftorqueAxis1*constraint->getRigidBodyA().getAngularFactor(); - } - { - const btVector3& ftorqueAxis2 = solverConstraint.m_relpos2CrossNormal; - solverConstraint.m_angularComponentB = constraint->getRigidBodyB().getInvInertiaTensorWorld()*ftorqueAxis2*constraint->getRigidBodyB().getAngularFactor(); - } - - { - btVector3 iMJlA = solverConstraint.m_contactNormal1*rbA.getInvMass(); - btVector3 iMJaA = rbA.getInvInertiaTensorWorld()*solverConstraint.m_relpos1CrossNormal; - btVector3 iMJlB = solverConstraint.m_contactNormal2*rbB.getInvMass();//sign of normal? - btVector3 iMJaB = rbB.getInvInertiaTensorWorld()*solverConstraint.m_relpos2CrossNormal; - - btScalar sum = iMJlA.dot(solverConstraint.m_contactNormal1); - sum += iMJaA.dot(solverConstraint.m_relpos1CrossNormal); - sum += iMJlB.dot(solverConstraint.m_contactNormal2); - sum += iMJaB.dot(solverConstraint.m_relpos2CrossNormal); - btScalar fsum = btFabs(sum); - btAssert(fsum > SIMD_EPSILON); - btScalar sorRelaxation = 1.f;//todo: get from globalInfo? - solverConstraint.m_jacDiagABInv = fsum>SIMD_EPSILON?sorRelaxation/sum : 0.f; - } - - - - { - btScalar rel_vel; - btVector3 externalForceImpulseA = bodyAPtr->m_originalBody ? bodyAPtr->m_externalForceImpulse : btVector3(0,0,0); - btVector3 externalTorqueImpulseA = bodyAPtr->m_originalBody ? bodyAPtr->m_externalTorqueImpulse : btVector3(0,0,0); - - btVector3 externalForceImpulseB = bodyBPtr->m_originalBody ? bodyBPtr->m_externalForceImpulse : btVector3(0,0,0); - btVector3 externalTorqueImpulseB = bodyBPtr->m_originalBody ?bodyBPtr->m_externalTorqueImpulse : btVector3(0,0,0); - - btScalar vel1Dotn = solverConstraint.m_contactNormal1.dot(rbA.getLinearVelocity()+externalForceImpulseA) - + solverConstraint.m_relpos1CrossNormal.dot(rbA.getAngularVelocity()+externalTorqueImpulseA); - - btScalar vel2Dotn = solverConstraint.m_contactNormal2.dot(rbB.getLinearVelocity()+externalForceImpulseB) - + solverConstraint.m_relpos2CrossNormal.dot(rbB.getAngularVelocity()+externalTorqueImpulseB); - - rel_vel = vel1Dotn+vel2Dotn; - btScalar restitution = 0.f; - btScalar positionalError = solverConstraint.m_rhs;//already filled in by getConstraintInfo2 - btScalar velocityError = restitution - rel_vel * info2.m_damping; - btScalar penetrationImpulse = positionalError*solverConstraint.m_jacDiagABInv; - btScalar velocityImpulse = velocityError *solverConstraint.m_jacDiagABInv; - solverConstraint.m_rhs = penetrationImpulse+velocityImpulse; - solverConstraint.m_appliedImpulse = 0.f; - - - } - } - } - currentRow+=m_tmpConstraintSizesPool[i].m_numConstraintRows; - } - } - - convertContacts(manifoldPtr,numManifolds,infoGlobal); - - } - // btContactSolverInfo info = infoGlobal; @@ -1630,6 +1648,7 @@ btScalar btSequentialImpulseConstraintSolver::solveGroupCacheFriendlySetup(btCol btScalar btSequentialImpulseConstraintSolver::solveSingleIteration(int iteration, btCollisionObject** /*bodies */,int /*numBodies*/,btPersistentManifold** /*manifoldPtr*/, int /*numManifolds*/,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* /*debugDrawer*/) { + BT_PROFILE("solveSingleIteration"); btScalar leastSquaresResidual = 0.f; int numNonContactPool = m_tmpSolverNonContactConstraintPool.size(); @@ -1675,7 +1694,7 @@ btScalar btSequentialImpulseConstraintSolver::solveSingleIteration(int iteration if (iteration < constraint.m_overrideNumSolverIterations) { btScalar residual = resolveSingleConstraintRowGeneric(m_tmpSolverBodyPool[constraint.m_solverBodyIdA],m_tmpSolverBodyPool[constraint.m_solverBodyIdB],constraint); - leastSquaresResidual += residual*residual; + leastSquaresResidual = btMax(leastSquaresResidual, residual*residual); } } @@ -1706,7 +1725,7 @@ btScalar btSequentialImpulseConstraintSolver::solveSingleIteration(int iteration { const btSolverConstraint& solveManifold = m_tmpSolverContactConstraintPool[m_orderTmpConstraintPool[c]]; btScalar residual = resolveSingleConstraintRowLowerLimit(m_tmpSolverBodyPool[solveManifold.m_solverBodyIdA],m_tmpSolverBodyPool[solveManifold.m_solverBodyIdB],solveManifold); - leastSquaresResidual += residual*residual; + leastSquaresResidual = btMax(leastSquaresResidual, residual*residual); totalImpulse = solveManifold.m_appliedImpulse; } @@ -1723,7 +1742,7 @@ btScalar btSequentialImpulseConstraintSolver::solveSingleIteration(int iteration solveManifold.m_upperLimit = solveManifold.m_friction*totalImpulse; btScalar residual = resolveSingleConstraintRowGeneric(m_tmpSolverBodyPool[solveManifold.m_solverBodyIdA],m_tmpSolverBodyPool[solveManifold.m_solverBodyIdB],solveManifold); - leastSquaresResidual += residual*residual; + leastSquaresResidual = btMax(leastSquaresResidual, residual*residual); } } @@ -1738,7 +1757,7 @@ btScalar btSequentialImpulseConstraintSolver::solveSingleIteration(int iteration solveManifold.m_upperLimit = solveManifold.m_friction*totalImpulse; btScalar residual = resolveSingleConstraintRowGeneric(m_tmpSolverBodyPool[solveManifold.m_solverBodyIdA],m_tmpSolverBodyPool[solveManifold.m_solverBodyIdB],solveManifold); - leastSquaresResidual += residual*residual; + leastSquaresResidual = btMax(leastSquaresResidual, residual*residual); } } } @@ -1755,7 +1774,7 @@ btScalar btSequentialImpulseConstraintSolver::solveSingleIteration(int iteration { const btSolverConstraint& solveManifold = m_tmpSolverContactConstraintPool[m_orderTmpConstraintPool[j]]; btScalar residual = resolveSingleConstraintRowLowerLimit(m_tmpSolverBodyPool[solveManifold.m_solverBodyIdA],m_tmpSolverBodyPool[solveManifold.m_solverBodyIdB],solveManifold); - leastSquaresResidual += residual*residual; + leastSquaresResidual = btMax(leastSquaresResidual, residual*residual); } @@ -1774,7 +1793,7 @@ btScalar btSequentialImpulseConstraintSolver::solveSingleIteration(int iteration solveManifold.m_upperLimit = solveManifold.m_friction*totalImpulse; btScalar residual = resolveSingleConstraintRowGeneric(m_tmpSolverBodyPool[solveManifold.m_solverBodyIdA],m_tmpSolverBodyPool[solveManifold.m_solverBodyIdB],solveManifold); - leastSquaresResidual += residual*residual; + leastSquaresResidual = btMax(leastSquaresResidual, residual*residual); } } } @@ -1796,7 +1815,7 @@ btScalar btSequentialImpulseConstraintSolver::solveSingleIteration(int iteration rollingFrictionConstraint.m_upperLimit = rollingFrictionMagnitude; btScalar residual = resolveSingleConstraintRowGeneric(m_tmpSolverBodyPool[rollingFrictionConstraint.m_solverBodyIdA],m_tmpSolverBodyPool[rollingFrictionConstraint.m_solverBodyIdB],rollingFrictionConstraint); - leastSquaresResidual += residual*residual; + leastSquaresResidual = btMax(leastSquaresResidual, residual*residual); } } @@ -1808,6 +1827,7 @@ btScalar btSequentialImpulseConstraintSolver::solveSingleIteration(int iteration void btSequentialImpulseConstraintSolver::solveGroupCacheFriendlySplitImpulseIterations(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer) { + BT_PROFILE("solveGroupCacheFriendlySplitImpulseIterations"); int iteration; if (infoGlobal.m_splitImpulse) { @@ -1823,7 +1843,7 @@ void btSequentialImpulseConstraintSolver::solveGroupCacheFriendlySplitImpulseIte const btSolverConstraint& solveManifold = m_tmpSolverContactConstraintPool[m_orderTmpConstraintPool[j]]; btScalar residual = resolveSplitPenetrationImpulse(m_tmpSolverBodyPool[solveManifold.m_solverBodyIdA],m_tmpSolverBodyPool[solveManifold.m_solverBodyIdB],solveManifold); - leastSquaresResidual += residual*residual; + leastSquaresResidual = btMax(leastSquaresResidual, residual*residual); } } if (leastSquaresResidual <= infoGlobal.m_leastSquaresResidualThreshold || iteration>=(infoGlobal.m_numIterations-1)) @@ -1866,14 +1886,9 @@ btScalar btSequentialImpulseConstraintSolver::solveGroupCacheFriendlyIterations( return 0.f; } -btScalar btSequentialImpulseConstraintSolver::solveGroupCacheFriendlyFinish(btCollisionObject** bodies,int numBodies,const btContactSolverInfo& infoGlobal) +void btSequentialImpulseConstraintSolver::writeBackContacts(int iBegin, int iEnd, const btContactSolverInfo& infoGlobal) { - int numPoolConstraints = m_tmpSolverContactConstraintPool.size(); - int i,j; - - if (infoGlobal.m_solverMode & SOLVER_USE_WARMSTARTING) - { - for (j=0;j<numPoolConstraints;j++) + for (int j=iBegin; j<iEnd; j++) { const btSolverConstraint& solveManifold = m_tmpSolverContactConstraintPool[j]; btManifoldPoint* pt = (btManifoldPoint*) solveManifold.m_originalContactPoint; @@ -1889,10 +1904,11 @@ btScalar btSequentialImpulseConstraintSolver::solveGroupCacheFriendlyFinish(btCo } //do a callback here? } - } +} - numPoolConstraints = m_tmpSolverNonContactConstraintPool.size(); - for (j=0;j<numPoolConstraints;j++) +void btSequentialImpulseConstraintSolver::writeBackJoints(int iBegin, int iEnd, const btContactSolverInfo& infoGlobal) +{ + for (int j=iBegin; j<iEnd; j++) { const btSolverConstraint& solverConstr = m_tmpSolverNonContactConstraintPool[j]; btTypedConstraint* constr = (btTypedConstraint*)solverConstr.m_originalContactPoint; @@ -1912,10 +1928,12 @@ btScalar btSequentialImpulseConstraintSolver::solveGroupCacheFriendlyFinish(btCo constr->setEnabled(false); } } +} - - for ( i=0;i<m_tmpSolverBodyPool.size();i++) +void btSequentialImpulseConstraintSolver::writeBackBodies(int iBegin, int iEnd, const btContactSolverInfo& infoGlobal) +{ + for (int i=iBegin; i<iEnd; i++) { btRigidBody* body = m_tmpSolverBodyPool[i].m_originalBody; if (body) @@ -1939,6 +1957,19 @@ btScalar btSequentialImpulseConstraintSolver::solveGroupCacheFriendlyFinish(btCo m_tmpSolverBodyPool[i].m_originalBody->setCompanionId(-1); } } +} + +btScalar btSequentialImpulseConstraintSolver::solveGroupCacheFriendlyFinish(btCollisionObject** bodies,int numBodies,const btContactSolverInfo& infoGlobal) +{ + BT_PROFILE("solveGroupCacheFriendlyFinish"); + + if (infoGlobal.m_solverMode & SOLVER_USE_WARMSTARTING) + { + writeBackContacts(0, m_tmpSolverContactConstraintPool.size(), infoGlobal); + } + + writeBackJoints(0, m_tmpSolverNonContactConstraintPool.size(), infoGlobal); + writeBackBodies(0, m_tmpSolverBodyPool.size(), infoGlobal); m_tmpSolverContactConstraintPool.resizeNoInitialize(0); m_tmpSolverNonContactConstraintPool.resizeNoInitialize(0); diff --git a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h index 16c7eb74c1..b834c3dac3 100644 --- a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h +++ b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h @@ -4,8 +4,8 @@ Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. @@ -27,7 +27,7 @@ class btCollisionObject; #include "BulletCollision/NarrowPhaseCollision/btManifoldPoint.h" #include "BulletDynamics/ConstraintSolver/btConstraintSolver.h" -typedef btSimdScalar(*btSingleConstraintRowSolver)(btSolverBody&, btSolverBody&, const btSolverConstraint&); +typedef btScalar(*btSingleConstraintRowSolver)(btSolverBody&, btSolverBody&, const btSolverConstraint&); ///The btSequentialImpulseConstraintSolver is a fast SIMD implementation of the Projected Gauss Seidel (iterative LCP) method. ATTRIBUTE_ALIGNED16(class) btSequentialImpulseConstraintSolver : public btConstraintSolver @@ -64,44 +64,48 @@ protected: void setupFrictionConstraint( btSolverConstraint& solverConstraint, const btVector3& normalAxis,int solverBodyIdA,int solverBodyIdB, btManifoldPoint& cp,const btVector3& rel_pos1,const btVector3& rel_pos2, - btCollisionObject* colObj0,btCollisionObject* colObj1, btScalar relaxation, + btCollisionObject* colObj0,btCollisionObject* colObj1, btScalar relaxation, const btContactSolverInfo& infoGlobal, btScalar desiredVelocity=0., btScalar cfmSlip=0.); void setupTorsionalFrictionConstraint( btSolverConstraint& solverConstraint, const btVector3& normalAxis,int solverBodyIdA,int solverBodyIdB, btManifoldPoint& cp,btScalar combinedTorsionalFriction, const btVector3& rel_pos1,const btVector3& rel_pos2, - btCollisionObject* colObj0,btCollisionObject* colObj1, btScalar relaxation, + btCollisionObject* colObj0,btCollisionObject* colObj1, btScalar relaxation, btScalar desiredVelocity=0., btScalar cfmSlip=0.); btSolverConstraint& addFrictionConstraint(const btVector3& normalAxis,int solverBodyIdA,int solverBodyIdB,int frictionIndex,btManifoldPoint& cp,const btVector3& rel_pos1,const btVector3& rel_pos2,btCollisionObject* colObj0,btCollisionObject* colObj1, btScalar relaxation, const btContactSolverInfo& infoGlobal, btScalar desiredVelocity=0., btScalar cfmSlip=0.); btSolverConstraint& addTorsionalFrictionConstraint(const btVector3& normalAxis,int solverBodyIdA,int solverBodyIdB,int frictionIndex,btManifoldPoint& cp,btScalar torsionalFriction, const btVector3& rel_pos1,const btVector3& rel_pos2,btCollisionObject* colObj0,btCollisionObject* colObj1, btScalar relaxation, btScalar desiredVelocity=0, btScalar cfmSlip=0.f); - - void setupContactConstraint(btSolverConstraint& solverConstraint, int solverBodyIdA, int solverBodyIdB, btManifoldPoint& cp, + + void setupContactConstraint(btSolverConstraint& solverConstraint, int solverBodyIdA, int solverBodyIdB, btManifoldPoint& cp, const btContactSolverInfo& infoGlobal,btScalar& relaxation, const btVector3& rel_pos1, const btVector3& rel_pos2); static void applyAnisotropicFriction(btCollisionObject* colObj,btVector3& frictionDirection, int frictionMode); - void setFrictionConstraintImpulse( btSolverConstraint& solverConstraint, int solverBodyIdA,int solverBodyIdB, + void setFrictionConstraintImpulse( btSolverConstraint& solverConstraint, int solverBodyIdA,int solverBodyIdB, btManifoldPoint& cp, const btContactSolverInfo& infoGlobal); ///m_btSeed2 is used for re-arranging the constraint rows. improves convergence/quality of friction unsigned long m_btSeed2; - + btScalar restitutionCurve(btScalar rel_vel, btScalar restitution, btScalar velocityThreshold); virtual void convertContacts(btPersistentManifold** manifoldPtr, int numManifolds, const btContactSolverInfo& infoGlobal); void convertContact(btPersistentManifold* manifold,const btContactSolverInfo& infoGlobal); + virtual void convertJoints(btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal); + void convertJoint(btSolverConstraint* currentConstraintRow, btTypedConstraint* constraint, const btTypedConstraint::btConstraintInfo1& info1, int solverBodyIdA, int solverBodyIdB, const btContactSolverInfo& infoGlobal); + + virtual void convertBodies(btCollisionObject** bodies, int numBodies, const btContactSolverInfo& infoGlobal); - btSimdScalar resolveSplitPenetrationSIMD(btSolverBody& bodyA,btSolverBody& bodyB, const btSolverConstraint& contactConstraint) + btScalar resolveSplitPenetrationSIMD(btSolverBody& bodyA,btSolverBody& bodyB, const btSolverConstraint& contactConstraint) { return m_resolveSplitPenetrationImpulse( bodyA, bodyB, contactConstraint ); } - btSimdScalar resolveSplitPenetrationImpulseCacheFriendly(btSolverBody& bodyA,btSolverBody& bodyB, const btSolverConstraint& contactConstraint) + btScalar resolveSplitPenetrationImpulseCacheFriendly(btSolverBody& bodyA,btSolverBody& bodyB, const btSolverConstraint& contactConstraint) { return m_resolveSplitPenetrationImpulse( bodyA, bodyB, contactConstraint ); } @@ -110,18 +114,20 @@ protected: int getOrInitSolverBody(btCollisionObject& body,btScalar timeStep); void initSolverBody(btSolverBody* solverBody, btCollisionObject* collisionObject, btScalar timeStep); - btSimdScalar resolveSingleConstraintRowGeneric(btSolverBody& bodyA,btSolverBody& bodyB,const btSolverConstraint& contactConstraint); - btSimdScalar resolveSingleConstraintRowGenericSIMD(btSolverBody& bodyA,btSolverBody& bodyB,const btSolverConstraint& contactConstraint); - btSimdScalar resolveSingleConstraintRowLowerLimit(btSolverBody& bodyA,btSolverBody& bodyB,const btSolverConstraint& contactConstraint); - btSimdScalar resolveSingleConstraintRowLowerLimitSIMD(btSolverBody& bodyA,btSolverBody& bodyB,const btSolverConstraint& contactConstraint); - btSimdScalar resolveSplitPenetrationImpulse(btSolverBody& bodyA,btSolverBody& bodyB, const btSolverConstraint& contactConstraint) + btScalar resolveSingleConstraintRowGeneric(btSolverBody& bodyA,btSolverBody& bodyB,const btSolverConstraint& contactConstraint); + btScalar resolveSingleConstraintRowGenericSIMD(btSolverBody& bodyA,btSolverBody& bodyB,const btSolverConstraint& contactConstraint); + btScalar resolveSingleConstraintRowLowerLimit(btSolverBody& bodyA,btSolverBody& bodyB,const btSolverConstraint& contactConstraint); + btScalar resolveSingleConstraintRowLowerLimitSIMD(btSolverBody& bodyA,btSolverBody& bodyB,const btSolverConstraint& contactConstraint); + btScalar resolveSplitPenetrationImpulse(btSolverBody& bodyA,btSolverBody& bodyB, const btSolverConstraint& contactConstraint) { return m_resolveSplitPenetrationImpulse( bodyA, bodyB, contactConstraint ); } - + protected: - - + + void writeBackContacts(int iBegin, int iEnd, const btContactSolverInfo& infoGlobal); + void writeBackJoints(int iBegin, int iEnd, const btContactSolverInfo& infoGlobal); + void writeBackBodies(int iBegin, int iEnd, const btContactSolverInfo& infoGlobal); virtual void solveGroupCacheFriendlySplitImpulseIterations(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer); virtual btScalar solveGroupCacheFriendlyFinish(btCollisionObject** bodies,int numBodies,const btContactSolverInfo& infoGlobal); virtual btScalar solveSingleIteration(int iteration, btCollisionObject** bodies ,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer); @@ -133,15 +139,15 @@ protected: public: BT_DECLARE_ALIGNED_ALLOCATOR(); - + btSequentialImpulseConstraintSolver(); virtual ~btSequentialImpulseConstraintSolver(); virtual btScalar solveGroup(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifold,int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& info, btIDebugDraw* debugDrawer,btDispatcher* dispatcher); - + ///clear internal cached data and reset random seed virtual void reset(); - + unsigned long btRand2(); int btRandInt2 (int n); @@ -155,7 +161,7 @@ public: return m_btSeed2; } - + virtual btConstraintSolverType getSolverType() const { return BT_SEQUENTIAL_IMPULSE_SOLVER; diff --git a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolverMt.cpp b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolverMt.cpp new file mode 100644 index 0000000000..4306c37e49 --- /dev/null +++ b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolverMt.cpp @@ -0,0 +1,1621 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + + +#include "btSequentialImpulseConstraintSolverMt.h" + +#include "LinearMath/btQuickprof.h" + +#include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h" + +#include "BulletDynamics/ConstraintSolver/btTypedConstraint.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" + + + +bool btSequentialImpulseConstraintSolverMt::s_allowNestedParallelForLoops = false; // some task schedulers don't like nested loops +int btSequentialImpulseConstraintSolverMt::s_minimumContactManifoldsForBatching = 250; +int btSequentialImpulseConstraintSolverMt::s_minBatchSize = 50; +int btSequentialImpulseConstraintSolverMt::s_maxBatchSize = 100; +btBatchedConstraints::BatchingMethod btSequentialImpulseConstraintSolverMt::s_contactBatchingMethod = btBatchedConstraints::BATCHING_METHOD_SPATIAL_GRID_2D; +btBatchedConstraints::BatchingMethod btSequentialImpulseConstraintSolverMt::s_jointBatchingMethod = btBatchedConstraints::BATCHING_METHOD_SPATIAL_GRID_2D; + + +btSequentialImpulseConstraintSolverMt::btSequentialImpulseConstraintSolverMt() +{ + m_numFrictionDirections = 1; + m_useBatching = false; + m_useObsoleteJointConstraints = false; +} + + +btSequentialImpulseConstraintSolverMt::~btSequentialImpulseConstraintSolverMt() +{ +} + + +void btSequentialImpulseConstraintSolverMt::setupBatchedContactConstraints() +{ + BT_PROFILE("setupBatchedContactConstraints"); + m_batchedContactConstraints.setup( &m_tmpSolverContactConstraintPool, + m_tmpSolverBodyPool, + s_contactBatchingMethod, + s_minBatchSize, + s_maxBatchSize, + &m_scratchMemory + ); +} + + +void btSequentialImpulseConstraintSolverMt::setupBatchedJointConstraints() +{ + BT_PROFILE("setupBatchedJointConstraints"); + m_batchedJointConstraints.setup( &m_tmpSolverNonContactConstraintPool, + m_tmpSolverBodyPool, + s_jointBatchingMethod, + s_minBatchSize, + s_maxBatchSize, + &m_scratchMemory + ); +} + + +void btSequentialImpulseConstraintSolverMt::internalSetupContactConstraints(int iContactConstraint, const btContactSolverInfo& infoGlobal) +{ + btSolverConstraint& contactConstraint = m_tmpSolverContactConstraintPool[iContactConstraint]; + + btVector3 rel_pos1; + btVector3 rel_pos2; + btScalar relaxation; + + int solverBodyIdA = contactConstraint.m_solverBodyIdA; + int solverBodyIdB = contactConstraint.m_solverBodyIdB; + + btSolverBody* solverBodyA = &m_tmpSolverBodyPool[ solverBodyIdA ]; + btSolverBody* solverBodyB = &m_tmpSolverBodyPool[ solverBodyIdB ]; + + btRigidBody* colObj0 = solverBodyA->m_originalBody; + btRigidBody* colObj1 = solverBodyB->m_originalBody; + + btManifoldPoint& cp = *static_cast<btManifoldPoint*>( contactConstraint.m_originalContactPoint ); + + const btVector3& pos1 = cp.getPositionWorldOnA(); + const btVector3& pos2 = cp.getPositionWorldOnB(); + + rel_pos1 = pos1 - solverBodyA->getWorldTransform().getOrigin(); + rel_pos2 = pos2 - solverBodyB->getWorldTransform().getOrigin(); + + btVector3 vel1; + btVector3 vel2; + + solverBodyA->getVelocityInLocalPointNoDelta( rel_pos1, vel1 ); + solverBodyB->getVelocityInLocalPointNoDelta( rel_pos2, vel2 ); + + btVector3 vel = vel1 - vel2; + btScalar rel_vel = cp.m_normalWorldOnB.dot( vel ); + + setupContactConstraint( contactConstraint, solverBodyIdA, solverBodyIdB, cp, infoGlobal, relaxation, rel_pos1, rel_pos2 ); + + // setup rolling friction constraints + int rollingFrictionIndex = m_rollingFrictionIndexTable[iContactConstraint]; + if (rollingFrictionIndex >= 0) + { + btSolverConstraint& spinningFrictionConstraint = m_tmpSolverContactRollingFrictionConstraintPool[ rollingFrictionIndex ]; + btAssert( spinningFrictionConstraint.m_frictionIndex == iContactConstraint ); + setupTorsionalFrictionConstraint( spinningFrictionConstraint, + cp.m_normalWorldOnB, + solverBodyIdA, + solverBodyIdB, + cp, + cp.m_combinedSpinningFriction, + rel_pos1, + rel_pos2, + colObj0, + colObj1, + relaxation, + 0.0f, + 0.0f + ); + btVector3 axis[2]; + btPlaneSpace1( cp.m_normalWorldOnB, axis[0], axis[1] ); + axis[0].normalize(); + axis[1].normalize(); + + applyAnisotropicFriction( colObj0, axis[0], btCollisionObject::CF_ANISOTROPIC_ROLLING_FRICTION ); + applyAnisotropicFriction( colObj1, axis[0], btCollisionObject::CF_ANISOTROPIC_ROLLING_FRICTION ); + applyAnisotropicFriction( colObj0, axis[1], btCollisionObject::CF_ANISOTROPIC_ROLLING_FRICTION ); + applyAnisotropicFriction( colObj1, axis[1], btCollisionObject::CF_ANISOTROPIC_ROLLING_FRICTION ); + // put the largest axis first + if (axis[1].length2() > axis[0].length2()) + { + btSwap(axis[0], axis[1]); + } + const btScalar kRollingFrictionThreshold = 0.001f; + for (int i = 0; i < 2; ++i) + { + int iRollingFric = rollingFrictionIndex + 1 + i; + btSolverConstraint& rollingFrictionConstraint = m_tmpSolverContactRollingFrictionConstraintPool[ iRollingFric ]; + btAssert(rollingFrictionConstraint.m_frictionIndex == iContactConstraint); + btVector3 dir = axis[i]; + if ( dir.length() > kRollingFrictionThreshold ) + { + setupTorsionalFrictionConstraint( rollingFrictionConstraint, + dir, + solverBodyIdA, + solverBodyIdB, + cp, + cp.m_combinedRollingFriction, + rel_pos1, + rel_pos2, + colObj0, + colObj1, + relaxation, + 0.0f, + 0.0f + ); + } + else + { + rollingFrictionConstraint.m_frictionIndex = -1; // disable constraint + } + } + } + + // setup friction constraints + // setupFrictionConstraint(solverConstraint, normalAxis, solverBodyIdA, solverBodyIdB, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation, infoGlobal, desiredVelocity, cfmSlip); + { + ///Bullet has several options to set the friction directions + ///By default, each contact has only a single friction direction that is recomputed automatically very frame + ///based on the relative linear velocity. + ///If the relative velocity it zero, it will automatically compute a friction direction. + + ///You can also enable two friction directions, using the SOLVER_USE_2_FRICTION_DIRECTIONS. + ///In that case, the second friction direction will be orthogonal to both contact normal and first friction direction. + /// + ///If you choose SOLVER_DISABLE_VELOCITY_DEPENDENT_FRICTION_DIRECTION, then the friction will be independent from the relative projected velocity. + /// + ///The user can manually override the friction directions for certain contacts using a contact callback, + ///and set the cp.m_lateralFrictionInitialized to true + ///In that case, you can set the target relative motion in each friction direction (cp.m_contactMotion1 and cp.m_contactMotion2) + ///this will give a conveyor belt effect + /// + btSolverConstraint* frictionConstraint1 = &m_tmpSolverContactFrictionConstraintPool[contactConstraint.m_frictionIndex]; + btAssert(frictionConstraint1->m_frictionIndex == iContactConstraint); + + btSolverConstraint* frictionConstraint2 = NULL; + if ( infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS ) + { + frictionConstraint2 = &m_tmpSolverContactFrictionConstraintPool[contactConstraint.m_frictionIndex + 1]; + btAssert( frictionConstraint2->m_frictionIndex == iContactConstraint ); + } + + if ( !( infoGlobal.m_solverMode & SOLVER_ENABLE_FRICTION_DIRECTION_CACHING ) || !( cp.m_contactPointFlags&BT_CONTACT_FLAG_LATERAL_FRICTION_INITIALIZED ) ) + { + cp.m_lateralFrictionDir1 = vel - cp.m_normalWorldOnB * rel_vel; + btScalar lat_rel_vel = cp.m_lateralFrictionDir1.length2(); + if ( !( infoGlobal.m_solverMode & SOLVER_DISABLE_VELOCITY_DEPENDENT_FRICTION_DIRECTION ) && lat_rel_vel > SIMD_EPSILON ) + { + cp.m_lateralFrictionDir1 *= 1.f / btSqrt( lat_rel_vel ); + applyAnisotropicFriction( colObj0, cp.m_lateralFrictionDir1, btCollisionObject::CF_ANISOTROPIC_FRICTION ); + applyAnisotropicFriction( colObj1, cp.m_lateralFrictionDir1, btCollisionObject::CF_ANISOTROPIC_FRICTION ); + setupFrictionConstraint( *frictionConstraint1, cp.m_lateralFrictionDir1, solverBodyIdA, solverBodyIdB, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation, infoGlobal ); + + if ( frictionConstraint2 ) + { + cp.m_lateralFrictionDir2 = cp.m_lateralFrictionDir1.cross( cp.m_normalWorldOnB ); + cp.m_lateralFrictionDir2.normalize();//?? + applyAnisotropicFriction( colObj0, cp.m_lateralFrictionDir2, btCollisionObject::CF_ANISOTROPIC_FRICTION ); + applyAnisotropicFriction( colObj1, cp.m_lateralFrictionDir2, btCollisionObject::CF_ANISOTROPIC_FRICTION ); + setupFrictionConstraint( *frictionConstraint2, cp.m_lateralFrictionDir2, solverBodyIdA, solverBodyIdB, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation, infoGlobal ); + } + } + else + { + btPlaneSpace1( cp.m_normalWorldOnB, cp.m_lateralFrictionDir1, cp.m_lateralFrictionDir2 ); + + applyAnisotropicFriction( colObj0, cp.m_lateralFrictionDir1, btCollisionObject::CF_ANISOTROPIC_FRICTION ); + applyAnisotropicFriction( colObj1, cp.m_lateralFrictionDir1, btCollisionObject::CF_ANISOTROPIC_FRICTION ); + setupFrictionConstraint( *frictionConstraint1, cp.m_lateralFrictionDir1, solverBodyIdA, solverBodyIdB, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation, infoGlobal ); + + if ( frictionConstraint2 ) + { + applyAnisotropicFriction( colObj0, cp.m_lateralFrictionDir2, btCollisionObject::CF_ANISOTROPIC_FRICTION ); + applyAnisotropicFriction( colObj1, cp.m_lateralFrictionDir2, btCollisionObject::CF_ANISOTROPIC_FRICTION ); + setupFrictionConstraint( *frictionConstraint2, cp.m_lateralFrictionDir2, solverBodyIdA, solverBodyIdB, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation, infoGlobal ); + } + + if ( ( infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS ) && ( infoGlobal.m_solverMode & SOLVER_DISABLE_VELOCITY_DEPENDENT_FRICTION_DIRECTION ) ) + { + cp.m_contactPointFlags |= BT_CONTACT_FLAG_LATERAL_FRICTION_INITIALIZED; + } + } + } + else + { + setupFrictionConstraint( *frictionConstraint1, cp.m_lateralFrictionDir1, solverBodyIdA, solverBodyIdB, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation, infoGlobal, cp.m_contactMotion1, cp.m_frictionCFM ); + if ( frictionConstraint2 ) + { + setupFrictionConstraint( *frictionConstraint2, cp.m_lateralFrictionDir2, solverBodyIdA, solverBodyIdB, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation, infoGlobal, cp.m_contactMotion2, cp.m_frictionCFM ); + } + } + } + + setFrictionConstraintImpulse( contactConstraint, solverBodyIdA, solverBodyIdB, cp, infoGlobal ); +} + + +struct SetupContactConstraintsLoop : public btIParallelForBody +{ + btSequentialImpulseConstraintSolverMt* m_solver; + const btBatchedConstraints* m_bc; + const btContactSolverInfo* m_infoGlobal; + + SetupContactConstraintsLoop( btSequentialImpulseConstraintSolverMt* solver, const btBatchedConstraints* bc, const btContactSolverInfo& infoGlobal ) + { + m_solver = solver; + m_bc = bc; + m_infoGlobal = &infoGlobal; + } + void forLoop( int iBegin, int iEnd ) const BT_OVERRIDE + { + BT_PROFILE( "SetupContactConstraintsLoop" ); + for ( int iBatch = iBegin; iBatch < iEnd; ++iBatch ) + { + const btBatchedConstraints::Range& batch = m_bc->m_batches[ iBatch ]; + for (int i = batch.begin; i < batch.end; ++i) + { + int iContact = m_bc->m_constraintIndices[i]; + m_solver->internalSetupContactConstraints( iContact, *m_infoGlobal ); + } + } + } +}; + + +void btSequentialImpulseConstraintSolverMt::setupAllContactConstraints(const btContactSolverInfo& infoGlobal) +{ + BT_PROFILE( "setupAllContactConstraints" ); + if ( m_useBatching ) + { + const btBatchedConstraints& batchedCons = m_batchedContactConstraints; + SetupContactConstraintsLoop loop( this, &batchedCons, infoGlobal ); + for ( int iiPhase = 0; iiPhase < batchedCons.m_phases.size(); ++iiPhase ) + { + int iPhase = batchedCons.m_phaseOrder[ iiPhase ]; + const btBatchedConstraints::Range& phase = batchedCons.m_phases[ iPhase ]; + int grainSize = 1; + btParallelFor( phase.begin, phase.end, grainSize, loop ); + } + } + else + { + for ( int i = 0; i < m_tmpSolverContactConstraintPool.size(); ++i ) + { + internalSetupContactConstraints( i, infoGlobal ); + } + } +} + + +int btSequentialImpulseConstraintSolverMt::getOrInitSolverBodyThreadsafe(btCollisionObject& body,btScalar timeStep) +{ + // + // getOrInitSolverBody is threadsafe only for a single thread per solver (with potentially multiple solvers) + // + // getOrInitSolverBodyThreadsafe -- attempts to be fully threadsafe (however may affect determinism) + // + int solverBodyId = -1; + bool isRigidBodyType = btRigidBody::upcast( &body ) != NULL; + if ( isRigidBodyType && !body.isStaticOrKinematicObject() ) + { + // dynamic body + // Dynamic bodies can only be in one island, so it's safe to write to the companionId + solverBodyId = body.getCompanionId(); + if ( solverBodyId < 0 ) + { + m_bodySolverArrayMutex.lock(); + // now that we have the lock, check again + solverBodyId = body.getCompanionId(); + if ( solverBodyId < 0 ) + { + solverBodyId = m_tmpSolverBodyPool.size(); + btSolverBody& solverBody = m_tmpSolverBodyPool.expand(); + initSolverBody( &solverBody, &body, timeStep ); + body.setCompanionId( solverBodyId ); + } + m_bodySolverArrayMutex.unlock(); + } + } + else if (isRigidBodyType && body.isKinematicObject()) + { + // + // NOTE: must test for kinematic before static because some kinematic objects also + // identify as "static" + // + // Kinematic bodies can be in multiple islands at once, so it is a + // race condition to write to them, so we use an alternate method + // to record the solverBodyId + int uniqueId = body.getWorldArrayIndex(); + const int INVALID_SOLVER_BODY_ID = -1; + if (m_kinematicBodyUniqueIdToSolverBodyTable.size() <= uniqueId ) + { + m_kinematicBodyUniqueIdToSolverBodyTableMutex.lock(); + // now that we have the lock, check again + if ( m_kinematicBodyUniqueIdToSolverBodyTable.size() <= uniqueId ) + { + m_kinematicBodyUniqueIdToSolverBodyTable.resize( uniqueId + 1, INVALID_SOLVER_BODY_ID ); + } + m_kinematicBodyUniqueIdToSolverBodyTableMutex.unlock(); + } + solverBodyId = m_kinematicBodyUniqueIdToSolverBodyTable[ uniqueId ]; + // if no table entry yet, + if ( INVALID_SOLVER_BODY_ID == solverBodyId ) + { + // need to acquire both locks + m_kinematicBodyUniqueIdToSolverBodyTableMutex.lock(); + m_bodySolverArrayMutex.lock(); + // now that we have the lock, check again + solverBodyId = m_kinematicBodyUniqueIdToSolverBodyTable[ uniqueId ]; + if ( INVALID_SOLVER_BODY_ID == solverBodyId ) + { + // create a table entry for this body + solverBodyId = m_tmpSolverBodyPool.size(); + btSolverBody& solverBody = m_tmpSolverBodyPool.expand(); + initSolverBody( &solverBody, &body, timeStep ); + m_kinematicBodyUniqueIdToSolverBodyTable[ uniqueId ] = solverBodyId; + } + m_bodySolverArrayMutex.unlock(); + m_kinematicBodyUniqueIdToSolverBodyTableMutex.unlock(); + } + } + else + { + // all fixed bodies (inf mass) get mapped to a single solver id + if ( m_fixedBodyId < 0 ) + { + m_bodySolverArrayMutex.lock(); + // now that we have the lock, check again + if ( m_fixedBodyId < 0 ) + { + m_fixedBodyId = m_tmpSolverBodyPool.size(); + btSolverBody& fixedBody = m_tmpSolverBodyPool.expand(); + initSolverBody( &fixedBody, 0, timeStep ); + } + m_bodySolverArrayMutex.unlock(); + } + solverBodyId = m_fixedBodyId; + } + btAssert( solverBodyId >= 0 && solverBodyId < m_tmpSolverBodyPool.size() ); + return solverBodyId; +} + + +void btSequentialImpulseConstraintSolverMt::internalCollectContactManifoldCachedInfo(btContactManifoldCachedInfo* cachedInfoArray, btPersistentManifold** manifoldPtr, int numManifolds, const btContactSolverInfo& infoGlobal) +{ + BT_PROFILE("internalCollectContactManifoldCachedInfo"); + for (int i = 0; i < numManifolds; ++i) + { + btContactManifoldCachedInfo* cachedInfo = &cachedInfoArray[i]; + btPersistentManifold* manifold = manifoldPtr[i]; + btCollisionObject* colObj0 = (btCollisionObject*) manifold->getBody0(); + btCollisionObject* colObj1 = (btCollisionObject*) manifold->getBody1(); + + int solverBodyIdA = getOrInitSolverBodyThreadsafe( *colObj0, infoGlobal.m_timeStep ); + int solverBodyIdB = getOrInitSolverBodyThreadsafe( *colObj1, infoGlobal.m_timeStep ); + + cachedInfo->solverBodyIds[ 0 ] = solverBodyIdA; + cachedInfo->solverBodyIds[ 1 ] = solverBodyIdB; + cachedInfo->numTouchingContacts = 0; + + btSolverBody* solverBodyA = &m_tmpSolverBodyPool[ solverBodyIdA ]; + btSolverBody* solverBodyB = &m_tmpSolverBodyPool[ solverBodyIdB ]; + + // A contact manifold between 2 static object should not exist! + // check the collision flags of your objects if this assert fires. + // Incorrectly set collision object flags can degrade performance in various ways. + btAssert( !m_tmpSolverBodyPool[ solverBodyIdA ].m_invMass.isZero() || !m_tmpSolverBodyPool[ solverBodyIdB ].m_invMass.isZero() ); + + int iContact = 0; + for ( int j = 0; j < manifold->getNumContacts(); j++ ) + { + btManifoldPoint& cp = manifold->getContactPoint( j ); + + if ( cp.getDistance() <= manifold->getContactProcessingThreshold() ) + { + cachedInfo->contactPoints[ iContact ] = &cp; + cachedInfo->contactHasRollingFriction[ iContact ] = ( cp.m_combinedRollingFriction > 0.f ); + iContact++; + } + } + cachedInfo->numTouchingContacts = iContact; + } +} + + +struct CollectContactManifoldCachedInfoLoop : public btIParallelForBody +{ + btSequentialImpulseConstraintSolverMt* m_solver; + btSequentialImpulseConstraintSolverMt::btContactManifoldCachedInfo* m_cachedInfoArray; + btPersistentManifold** m_manifoldPtr; + const btContactSolverInfo* m_infoGlobal; + + CollectContactManifoldCachedInfoLoop( btSequentialImpulseConstraintSolverMt* solver, btSequentialImpulseConstraintSolverMt::btContactManifoldCachedInfo* cachedInfoArray, btPersistentManifold** manifoldPtr, const btContactSolverInfo& infoGlobal ) + { + m_solver = solver; + m_cachedInfoArray = cachedInfoArray; + m_manifoldPtr = manifoldPtr; + m_infoGlobal = &infoGlobal; + } + void forLoop( int iBegin, int iEnd ) const BT_OVERRIDE + { + m_solver->internalCollectContactManifoldCachedInfo( m_cachedInfoArray + iBegin, m_manifoldPtr + iBegin, iEnd - iBegin, *m_infoGlobal ); + } +}; + + +void btSequentialImpulseConstraintSolverMt::internalAllocContactConstraints(const btContactManifoldCachedInfo* cachedInfoArray, int numManifolds) +{ + BT_PROFILE("internalAllocContactConstraints"); + // possibly parallel part + for ( int iManifold = 0; iManifold < numManifolds; ++iManifold ) + { + const btContactManifoldCachedInfo& cachedInfo = cachedInfoArray[ iManifold ]; + int contactIndex = cachedInfo.contactIndex; + int frictionIndex = contactIndex * m_numFrictionDirections; + int rollingFrictionIndex = cachedInfo.rollingFrictionIndex; + for ( int i = 0; i < cachedInfo.numTouchingContacts; i++ ) + { + btSolverConstraint& contactConstraint = m_tmpSolverContactConstraintPool[contactIndex]; + contactConstraint.m_solverBodyIdA = cachedInfo.solverBodyIds[ 0 ]; + contactConstraint.m_solverBodyIdB = cachedInfo.solverBodyIds[ 1 ]; + contactConstraint.m_originalContactPoint = cachedInfo.contactPoints[ i ]; + + // allocate the friction constraints + contactConstraint.m_frictionIndex = frictionIndex; + for ( int iDir = 0; iDir < m_numFrictionDirections; ++iDir ) + { + btSolverConstraint& frictionConstraint = m_tmpSolverContactFrictionConstraintPool[frictionIndex]; + frictionConstraint.m_frictionIndex = contactIndex; + frictionIndex++; + } + + // allocate rolling friction constraints + if ( cachedInfo.contactHasRollingFriction[ i ] ) + { + m_rollingFrictionIndexTable[ contactIndex ] = rollingFrictionIndex; + // allocate 3 (although we may use only 2 sometimes) + for ( int i = 0; i < 3; i++ ) + { + m_tmpSolverContactRollingFrictionConstraintPool[ rollingFrictionIndex ].m_frictionIndex = contactIndex; + rollingFrictionIndex++; + } + } + else + { + // indicate there is no rolling friction for this contact point + m_rollingFrictionIndexTable[ contactIndex ] = -1; + } + contactIndex++; + } + } +} + + +struct AllocContactConstraintsLoop : public btIParallelForBody +{ + btSequentialImpulseConstraintSolverMt* m_solver; + const btSequentialImpulseConstraintSolverMt::btContactManifoldCachedInfo* m_cachedInfoArray; + + AllocContactConstraintsLoop( btSequentialImpulseConstraintSolverMt* solver, btSequentialImpulseConstraintSolverMt::btContactManifoldCachedInfo* cachedInfoArray ) + { + m_solver = solver; + m_cachedInfoArray = cachedInfoArray; + } + void forLoop( int iBegin, int iEnd ) const BT_OVERRIDE + { + m_solver->internalAllocContactConstraints( m_cachedInfoArray + iBegin, iEnd - iBegin ); + } +}; + + +void btSequentialImpulseConstraintSolverMt::allocAllContactConstraints(btPersistentManifold** manifoldPtr, int numManifolds, const btContactSolverInfo& infoGlobal) +{ + BT_PROFILE( "allocAllContactConstraints" ); + btAlignedObjectArray<btContactManifoldCachedInfo> cachedInfoArray; // = m_manifoldCachedInfoArray; + cachedInfoArray.resizeNoInitialize( numManifolds ); + if (/* DISABLES CODE */ (false)) + { + // sequential + internalCollectContactManifoldCachedInfo(&cachedInfoArray[ 0 ], manifoldPtr, numManifolds, infoGlobal); + } + else + { + // may alter ordering of bodies which affects determinism + CollectContactManifoldCachedInfoLoop loop( this, &cachedInfoArray[ 0 ], manifoldPtr, infoGlobal ); + int grainSize = 200; + btParallelFor( 0, numManifolds, grainSize, loop ); + } + + { + // serial part + int numContacts = 0; + int numRollingFrictionConstraints = 0; + for ( int iManifold = 0; iManifold < numManifolds; ++iManifold ) + { + btContactManifoldCachedInfo& cachedInfo = cachedInfoArray[ iManifold ]; + cachedInfo.contactIndex = numContacts; + cachedInfo.rollingFrictionIndex = numRollingFrictionConstraints; + numContacts += cachedInfo.numTouchingContacts; + for (int i = 0; i < cachedInfo.numTouchingContacts; ++i) + { + if (cachedInfo.contactHasRollingFriction[i]) + { + numRollingFrictionConstraints += 3; + } + } + } + { + BT_PROFILE( "allocPools" ); + if ( m_tmpSolverContactConstraintPool.capacity() < numContacts ) + { + // if we need to reallocate, reserve some extra so we don't have to reallocate again next frame + int extraReserve = numContacts / 16; + m_tmpSolverContactConstraintPool.reserve( numContacts + extraReserve ); + m_rollingFrictionIndexTable.reserve( numContacts + extraReserve ); + m_tmpSolverContactFrictionConstraintPool.reserve( ( numContacts + extraReserve )*m_numFrictionDirections ); + m_tmpSolverContactRollingFrictionConstraintPool.reserve( numRollingFrictionConstraints + extraReserve ); + } + m_tmpSolverContactConstraintPool.resizeNoInitialize( numContacts ); + m_rollingFrictionIndexTable.resizeNoInitialize( numContacts ); + m_tmpSolverContactFrictionConstraintPool.resizeNoInitialize( numContacts*m_numFrictionDirections ); + m_tmpSolverContactRollingFrictionConstraintPool.resizeNoInitialize( numRollingFrictionConstraints ); + } + } + { + AllocContactConstraintsLoop loop(this, &cachedInfoArray[0]); + int grainSize = 200; + btParallelFor( 0, numManifolds, grainSize, loop ); + } +} + + +void btSequentialImpulseConstraintSolverMt::convertContacts(btPersistentManifold** manifoldPtr, int numManifolds, const btContactSolverInfo& infoGlobal) +{ + if (!m_useBatching) + { + btSequentialImpulseConstraintSolver::convertContacts(manifoldPtr, numManifolds, infoGlobal); + return; + } + BT_PROFILE( "convertContacts" ); + if (numManifolds > 0) + { + if ( m_fixedBodyId < 0 ) + { + m_fixedBodyId = m_tmpSolverBodyPool.size(); + btSolverBody& fixedBody = m_tmpSolverBodyPool.expand(); + initSolverBody( &fixedBody, 0, infoGlobal.m_timeStep ); + } + allocAllContactConstraints( manifoldPtr, numManifolds, infoGlobal ); + if ( m_useBatching ) + { + setupBatchedContactConstraints(); + } + setupAllContactConstraints( infoGlobal ); + } +} + + +void btSequentialImpulseConstraintSolverMt::internalInitMultipleJoints( btTypedConstraint** constraints, int iBegin, int iEnd ) +{ + BT_PROFILE("internalInitMultipleJoints"); + for ( int i = iBegin; i < iEnd; i++ ) + { + btTypedConstraint* constraint = constraints[i]; + btTypedConstraint::btConstraintInfo1& info1 = m_tmpConstraintSizesPool[i]; + if (constraint->isEnabled()) + { + constraint->buildJacobian(); + constraint->internalSetAppliedImpulse( 0.0f ); + btJointFeedback* fb = constraint->getJointFeedback(); + if ( fb ) + { + fb->m_appliedForceBodyA.setZero(); + fb->m_appliedTorqueBodyA.setZero(); + fb->m_appliedForceBodyB.setZero(); + fb->m_appliedTorqueBodyB.setZero(); + } + constraint->getInfo1( &info1 ); + } + else + { + info1.m_numConstraintRows = 0; + info1.nub = 0; + } + } +} + + +struct InitJointsLoop : public btIParallelForBody +{ + btSequentialImpulseConstraintSolverMt* m_solver; + btTypedConstraint** m_constraints; + + InitJointsLoop( btSequentialImpulseConstraintSolverMt* solver, btTypedConstraint** constraints ) + { + m_solver = solver; + m_constraints = constraints; + } + void forLoop( int iBegin, int iEnd ) const BT_OVERRIDE + { + m_solver->internalInitMultipleJoints( m_constraints, iBegin, iEnd ); + } +}; + + +void btSequentialImpulseConstraintSolverMt::internalConvertMultipleJoints( const btAlignedObjectArray<JointParams>& jointParamsArray, btTypedConstraint** constraints, int iBegin, int iEnd, const btContactSolverInfo& infoGlobal ) +{ + BT_PROFILE("internalConvertMultipleJoints"); + for ( int i = iBegin; i < iEnd; ++i ) + { + const JointParams& jointParams = jointParamsArray[ i ]; + int currentRow = jointParams.m_solverConstraint; + if ( currentRow != -1 ) + { + const btTypedConstraint::btConstraintInfo1& info1 = m_tmpConstraintSizesPool[ i ]; + btAssert( currentRow < m_tmpSolverNonContactConstraintPool.size() ); + btAssert( info1.m_numConstraintRows > 0 ); + + btSolverConstraint* currentConstraintRow = &m_tmpSolverNonContactConstraintPool[ currentRow ]; + btTypedConstraint* constraint = constraints[ i ]; + + convertJoint( currentConstraintRow, constraint, info1, jointParams.m_solverBodyA, jointParams.m_solverBodyB, infoGlobal ); + } + } +} + + +struct ConvertJointsLoop : public btIParallelForBody +{ + btSequentialImpulseConstraintSolverMt* m_solver; + const btAlignedObjectArray<btSequentialImpulseConstraintSolverMt::JointParams>& m_jointParamsArray; + btTypedConstraint** m_srcConstraints; + const btContactSolverInfo& m_infoGlobal; + + ConvertJointsLoop( btSequentialImpulseConstraintSolverMt* solver, + const btAlignedObjectArray<btSequentialImpulseConstraintSolverMt::JointParams>& jointParamsArray, + btTypedConstraint** srcConstraints, + const btContactSolverInfo& infoGlobal + ) : + m_jointParamsArray(jointParamsArray), + m_infoGlobal(infoGlobal) + { + m_solver = solver; + m_srcConstraints = srcConstraints; + } + void forLoop( int iBegin, int iEnd ) const BT_OVERRIDE + { + m_solver->internalConvertMultipleJoints( m_jointParamsArray, m_srcConstraints, iBegin, iEnd, m_infoGlobal ); + } +}; + + +void btSequentialImpulseConstraintSolverMt::convertJoints(btTypedConstraint** constraints, int numConstraints, const btContactSolverInfo& infoGlobal) +{ + if ( !m_useBatching ) + { + btSequentialImpulseConstraintSolver::convertJoints(constraints, numConstraints, infoGlobal); + return; + } + BT_PROFILE("convertJoints"); + bool parallelJointSetup = true; + m_tmpConstraintSizesPool.resizeNoInitialize(numConstraints); + if (parallelJointSetup) + { + InitJointsLoop loop(this, constraints); + int grainSize = 40; + btParallelFor(0, numConstraints, grainSize, loop); + } + else + { + internalInitMultipleJoints( constraints, 0, numConstraints ); + } + + int totalNumRows = 0; + btAlignedObjectArray<JointParams> jointParamsArray; + jointParamsArray.resizeNoInitialize(numConstraints); + + //calculate the total number of contraint rows + for (int i=0;i<numConstraints;i++) + { + btTypedConstraint* constraint = constraints[ i ]; + + JointParams& params = jointParamsArray[ i ]; + const btTypedConstraint::btConstraintInfo1& info1 = m_tmpConstraintSizesPool[i]; + + if (info1.m_numConstraintRows) + { + params.m_solverConstraint = totalNumRows; + params.m_solverBodyA = getOrInitSolverBody( constraint->getRigidBodyA(), infoGlobal.m_timeStep ); + params.m_solverBodyB = getOrInitSolverBody( constraint->getRigidBodyB(), infoGlobal.m_timeStep ); + } + else + { + params.m_solverConstraint = -1; + } + totalNumRows += info1.m_numConstraintRows; + } + m_tmpSolverNonContactConstraintPool.resizeNoInitialize(totalNumRows); + + ///setup the btSolverConstraints + if ( parallelJointSetup ) + { + ConvertJointsLoop loop(this, jointParamsArray, constraints, infoGlobal); + int grainSize = 20; + btParallelFor(0, numConstraints, grainSize, loop); + } + else + { + internalConvertMultipleJoints( jointParamsArray, constraints, 0, numConstraints, infoGlobal ); + } + setupBatchedJointConstraints(); +} + + +void btSequentialImpulseConstraintSolverMt::internalConvertBodies(btCollisionObject** bodies, int iBegin, int iEnd, const btContactSolverInfo& infoGlobal) +{ + BT_PROFILE("internalConvertBodies"); + for (int i=iBegin; i < iEnd; i++) + { + btCollisionObject* obj = bodies[i]; + obj->setCompanionId(i); + btSolverBody& solverBody = m_tmpSolverBodyPool[i]; + initSolverBody(&solverBody, obj, infoGlobal.m_timeStep); + + btRigidBody* body = btRigidBody::upcast(obj); + if (body && body->getInvMass()) + { + btVector3 gyroForce (0,0,0); + if (body->getFlags()&BT_ENABLE_GYROSCOPIC_FORCE_EXPLICIT) + { + gyroForce = body->computeGyroscopicForceExplicit(infoGlobal.m_maxGyroscopicForce); + solverBody.m_externalTorqueImpulse -= gyroForce*body->getInvInertiaTensorWorld()*infoGlobal.m_timeStep; + } + if (body->getFlags()&BT_ENABLE_GYROSCOPIC_FORCE_IMPLICIT_WORLD) + { + gyroForce = body->computeGyroscopicImpulseImplicit_World(infoGlobal.m_timeStep); + solverBody.m_externalTorqueImpulse += gyroForce; + } + if (body->getFlags()&BT_ENABLE_GYROSCOPIC_FORCE_IMPLICIT_BODY) + { + gyroForce = body->computeGyroscopicImpulseImplicit_Body(infoGlobal.m_timeStep); + solverBody.m_externalTorqueImpulse += gyroForce; + } + } + } +} + + +struct ConvertBodiesLoop : public btIParallelForBody +{ + btSequentialImpulseConstraintSolverMt* m_solver; + btCollisionObject** m_bodies; + int m_numBodies; + const btContactSolverInfo& m_infoGlobal; + + ConvertBodiesLoop( btSequentialImpulseConstraintSolverMt* solver, + btCollisionObject** bodies, + int numBodies, + const btContactSolverInfo& infoGlobal + ) : + m_infoGlobal(infoGlobal) + { + m_solver = solver; + m_bodies = bodies; + m_numBodies = numBodies; + } + void forLoop( int iBegin, int iEnd ) const BT_OVERRIDE + { + m_solver->internalConvertBodies( m_bodies, iBegin, iEnd, m_infoGlobal ); + } +}; + + +void btSequentialImpulseConstraintSolverMt::convertBodies(btCollisionObject** bodies, int numBodies, const btContactSolverInfo& infoGlobal) +{ + BT_PROFILE("convertBodies"); + m_kinematicBodyUniqueIdToSolverBodyTable.resize( 0 ); + + m_tmpSolverBodyPool.resizeNoInitialize(numBodies+1); + + m_fixedBodyId = numBodies; + { + btSolverBody& fixedBody = m_tmpSolverBodyPool[ m_fixedBodyId ]; + initSolverBody( &fixedBody, NULL, infoGlobal.m_timeStep ); + } + + bool parallelBodySetup = true; + if (parallelBodySetup) + { + ConvertBodiesLoop loop(this, bodies, numBodies, infoGlobal); + int grainSize = 40; + btParallelFor(0, numBodies, grainSize, loop); + } + else + { + internalConvertBodies( bodies, 0, numBodies, infoGlobal ); + } +} + + +btScalar btSequentialImpulseConstraintSolverMt::solveGroupCacheFriendlySetup( + btCollisionObject** bodies, + int numBodies, + btPersistentManifold** manifoldPtr, + int numManifolds, + btTypedConstraint** constraints, + int numConstraints, + const btContactSolverInfo& infoGlobal, + btIDebugDraw* debugDrawer + ) +{ + m_numFrictionDirections = (infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS) ? 2 : 1; + m_useBatching = false; + if ( numManifolds >= s_minimumContactManifoldsForBatching && + (s_allowNestedParallelForLoops || !btThreadsAreRunning()) + ) + { + m_useBatching = true; + m_batchedContactConstraints.m_debugDrawer = debugDrawer; + m_batchedJointConstraints.m_debugDrawer = debugDrawer; + } + btSequentialImpulseConstraintSolver::solveGroupCacheFriendlySetup( bodies, + numBodies, + manifoldPtr, + numManifolds, + constraints, + numConstraints, + infoGlobal, + debugDrawer + ); + return 0.0f; +} + + +btScalar btSequentialImpulseConstraintSolverMt::resolveMultipleContactSplitPenetrationImpulseConstraints( const btAlignedObjectArray<int>& consIndices, int batchBegin, int batchEnd ) +{ + btScalar leastSquaresResidual = 0.f; + for ( int iiCons = batchBegin; iiCons < batchEnd; ++iiCons ) + { + int iCons = consIndices[ iiCons ]; + const btSolverConstraint& solveManifold = m_tmpSolverContactConstraintPool[ iCons ]; + btSolverBody& bodyA = m_tmpSolverBodyPool[ solveManifold.m_solverBodyIdA ]; + btSolverBody& bodyB = m_tmpSolverBodyPool[ solveManifold.m_solverBodyIdB ]; + btScalar residual = resolveSplitPenetrationImpulse( bodyA, bodyB, solveManifold ); + leastSquaresResidual += residual*residual; + } + return leastSquaresResidual; +} + + +struct ContactSplitPenetrationImpulseSolverLoop : public btIParallelSumBody +{ + btSequentialImpulseConstraintSolverMt* m_solver; + const btBatchedConstraints* m_bc; + + ContactSplitPenetrationImpulseSolverLoop( btSequentialImpulseConstraintSolverMt* solver, const btBatchedConstraints* bc ) + { + m_solver = solver; + m_bc = bc; + } + btScalar sumLoop( int iBegin, int iEnd ) const BT_OVERRIDE + { + BT_PROFILE( "ContactSplitPenetrationImpulseSolverLoop" ); + btScalar sum = 0; + for ( int iBatch = iBegin; iBatch < iEnd; ++iBatch ) + { + const btBatchedConstraints::Range& batch = m_bc->m_batches[ iBatch ]; + sum += m_solver->resolveMultipleContactSplitPenetrationImpulseConstraints( m_bc->m_constraintIndices, batch.begin, batch.end ); + } + return sum; + } +}; + + +void btSequentialImpulseConstraintSolverMt::solveGroupCacheFriendlySplitImpulseIterations(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer) +{ + BT_PROFILE("solveGroupCacheFriendlySplitImpulseIterations"); + if (infoGlobal.m_splitImpulse) + { + for ( int iteration = 0; iteration < infoGlobal.m_numIterations; iteration++ ) + { + btScalar leastSquaresResidual = 0.f; + if (m_useBatching) + { + const btBatchedConstraints& batchedCons = m_batchedContactConstraints; + ContactSplitPenetrationImpulseSolverLoop loop( this, &batchedCons ); + btScalar leastSquaresResidual = 0.f; + for ( int iiPhase = 0; iiPhase < batchedCons.m_phases.size(); ++iiPhase ) + { + int iPhase = batchedCons.m_phaseOrder[ iiPhase ]; + const btBatchedConstraints::Range& phase = batchedCons.m_phases[ iPhase ]; + int grainSize = batchedCons.m_phaseGrainSize[iPhase]; + leastSquaresResidual += btParallelSum( phase.begin, phase.end, grainSize, loop ); + } + } + else + { + // non-batched + leastSquaresResidual = resolveMultipleContactSplitPenetrationImpulseConstraints(m_orderTmpConstraintPool, 0, m_tmpSolverContactConstraintPool.size()); + } + if ( leastSquaresResidual <= infoGlobal.m_leastSquaresResidualThreshold || iteration >= ( infoGlobal.m_numIterations - 1 ) ) + { +#ifdef VERBOSE_RESIDUAL_PRINTF + printf( "residual = %f at iteration #%d\n", leastSquaresResidual, iteration ); +#endif + break; + } + } + } +} + + +btScalar btSequentialImpulseConstraintSolverMt::solveSingleIteration(int iteration, btCollisionObject** bodies, int numBodies, btPersistentManifold** manifoldPtr, int numManifolds, btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer) +{ + if ( !m_useBatching ) + { + return btSequentialImpulseConstraintSolver::solveSingleIteration( iteration, bodies, numBodies, manifoldPtr, numManifolds, constraints, numConstraints, infoGlobal, debugDrawer ); + } + BT_PROFILE( "solveSingleIterationMt" ); + btScalar leastSquaresResidual = 0.f; + + if (infoGlobal.m_solverMode & SOLVER_RANDMIZE_ORDER) + { + if (1) // uncomment this for a bit less random ((iteration & 7) == 0) + { + randomizeConstraintOrdering(iteration, infoGlobal.m_numIterations); + } + } + + { + ///solve all joint constraints + leastSquaresResidual += resolveAllJointConstraints(iteration); + + if (iteration< infoGlobal.m_numIterations) + { + // this loop is only used for cone-twist constraints, + // it would be nice to skip this loop if none of the constraints need it + if ( m_useObsoleteJointConstraints ) + { + for ( int j = 0; j<numConstraints; j++ ) + { + if ( constraints[ j ]->isEnabled() ) + { + int bodyAid = getOrInitSolverBody( constraints[ j ]->getRigidBodyA(), infoGlobal.m_timeStep ); + int bodyBid = getOrInitSolverBody( constraints[ j ]->getRigidBodyB(), infoGlobal.m_timeStep ); + btSolverBody& bodyA = m_tmpSolverBodyPool[ bodyAid ]; + btSolverBody& bodyB = m_tmpSolverBodyPool[ bodyBid ]; + constraints[ j ]->solveConstraintObsolete( bodyA, bodyB, infoGlobal.m_timeStep ); + } + } + } + + if (infoGlobal.m_solverMode & SOLVER_INTERLEAVE_CONTACT_AND_FRICTION_CONSTRAINTS) + { + // solve all contact, contact-friction, and rolling friction constraints interleaved + leastSquaresResidual += resolveAllContactConstraintsInterleaved(); + } + else//SOLVER_INTERLEAVE_CONTACT_AND_FRICTION_CONSTRAINTS + { + // don't interleave them + // solve all contact constraints + leastSquaresResidual += resolveAllContactConstraints(); + + // solve all contact friction constraints + leastSquaresResidual += resolveAllContactFrictionConstraints(); + + // solve all rolling friction constraints + leastSquaresResidual += resolveAllRollingFrictionConstraints(); + } + } + } + return leastSquaresResidual; +} + + +btScalar btSequentialImpulseConstraintSolverMt::resolveMultipleJointConstraints( const btAlignedObjectArray<int>& consIndices, int batchBegin, int batchEnd, int iteration ) +{ + btScalar leastSquaresResidual = 0.f; + for ( int iiCons = batchBegin; iiCons < batchEnd; ++iiCons ) + { + int iCons = consIndices[ iiCons ]; + const btSolverConstraint& constraint = m_tmpSolverNonContactConstraintPool[ iCons ]; + if ( iteration < constraint.m_overrideNumSolverIterations ) + { + btSolverBody& bodyA = m_tmpSolverBodyPool[ constraint.m_solverBodyIdA ]; + btSolverBody& bodyB = m_tmpSolverBodyPool[ constraint.m_solverBodyIdB ]; + btScalar residual = resolveSingleConstraintRowGeneric( bodyA, bodyB, constraint ); + leastSquaresResidual += residual*residual; + } + } + return leastSquaresResidual; +} + + +btScalar btSequentialImpulseConstraintSolverMt::resolveMultipleContactConstraints( const btAlignedObjectArray<int>& consIndices, int batchBegin, int batchEnd ) +{ + btScalar leastSquaresResidual = 0.f; + for ( int iiCons = batchBegin; iiCons < batchEnd; ++iiCons ) + { + int iCons = consIndices[ iiCons ]; + const btSolverConstraint& solveManifold = m_tmpSolverContactConstraintPool[ iCons ]; + btSolverBody& bodyA = m_tmpSolverBodyPool[ solveManifold.m_solverBodyIdA ]; + btSolverBody& bodyB = m_tmpSolverBodyPool[ solveManifold.m_solverBodyIdB ]; + btScalar residual = resolveSingleConstraintRowLowerLimit( bodyA, bodyB, solveManifold ); + leastSquaresResidual += residual*residual; + } + return leastSquaresResidual; +} + + +btScalar btSequentialImpulseConstraintSolverMt::resolveMultipleContactFrictionConstraints( const btAlignedObjectArray<int>& consIndices, int batchBegin, int batchEnd ) +{ + btScalar leastSquaresResidual = 0.f; + for ( int iiCons = batchBegin; iiCons < batchEnd; ++iiCons ) + { + int iContact = consIndices[ iiCons ]; + btScalar totalImpulse = m_tmpSolverContactConstraintPool[ iContact ].m_appliedImpulse; + + // apply sliding friction + if ( totalImpulse > 0.0f ) + { + int iBegin = iContact * m_numFrictionDirections; + int iEnd = iBegin + m_numFrictionDirections; + for ( int iFriction = iBegin; iFriction < iEnd; ++iFriction ) + { + btSolverConstraint& solveManifold = m_tmpSolverContactFrictionConstraintPool[ iFriction++ ]; + btAssert( solveManifold.m_frictionIndex == iContact ); + + solveManifold.m_lowerLimit = -( solveManifold.m_friction*totalImpulse ); + solveManifold.m_upperLimit = solveManifold.m_friction*totalImpulse; + + btSolverBody& bodyA = m_tmpSolverBodyPool[ solveManifold.m_solverBodyIdA ]; + btSolverBody& bodyB = m_tmpSolverBodyPool[ solveManifold.m_solverBodyIdB ]; + btScalar residual = resolveSingleConstraintRowGeneric( bodyA, bodyB, solveManifold ); + leastSquaresResidual += residual*residual; + } + } + } + return leastSquaresResidual; +} + + +btScalar btSequentialImpulseConstraintSolverMt::resolveMultipleContactRollingFrictionConstraints( const btAlignedObjectArray<int>& consIndices, int batchBegin, int batchEnd ) +{ + btScalar leastSquaresResidual = 0.f; + for ( int iiCons = batchBegin; iiCons < batchEnd; ++iiCons ) + { + int iContact = consIndices[ iiCons ]; + int iFirstRollingFriction = m_rollingFrictionIndexTable[ iContact ]; + if ( iFirstRollingFriction >= 0 ) + { + btScalar totalImpulse = m_tmpSolverContactConstraintPool[ iContact ].m_appliedImpulse; + // apply rolling friction + if ( totalImpulse > 0.0f ) + { + int iBegin = iFirstRollingFriction; + int iEnd = iBegin + 3; + for ( int iRollingFric = iBegin; iRollingFric < iEnd; ++iRollingFric ) + { + btSolverConstraint& rollingFrictionConstraint = m_tmpSolverContactRollingFrictionConstraintPool[ iRollingFric ]; + if ( rollingFrictionConstraint.m_frictionIndex != iContact ) + { + break; + } + btScalar rollingFrictionMagnitude = rollingFrictionConstraint.m_friction*totalImpulse; + if ( rollingFrictionMagnitude > rollingFrictionConstraint.m_friction ) + { + rollingFrictionMagnitude = rollingFrictionConstraint.m_friction; + } + + rollingFrictionConstraint.m_lowerLimit = -rollingFrictionMagnitude; + rollingFrictionConstraint.m_upperLimit = rollingFrictionMagnitude; + + btScalar residual = resolveSingleConstraintRowGeneric( m_tmpSolverBodyPool[ rollingFrictionConstraint.m_solverBodyIdA ], m_tmpSolverBodyPool[ rollingFrictionConstraint.m_solverBodyIdB ], rollingFrictionConstraint ); + leastSquaresResidual += residual*residual; + } + } + } + } + return leastSquaresResidual; +} + + +btScalar btSequentialImpulseConstraintSolverMt::resolveMultipleContactConstraintsInterleaved( const btAlignedObjectArray<int>& contactIndices, + int batchBegin, + int batchEnd + ) +{ + btScalar leastSquaresResidual = 0.f; + int numPoolConstraints = m_tmpSolverContactConstraintPool.size(); + + for ( int iiCons = batchBegin; iiCons < batchEnd; iiCons++ ) + { + btScalar totalImpulse = 0; + int iContact = contactIndices[ iiCons ]; + // apply penetration constraint + { + const btSolverConstraint& solveManifold = m_tmpSolverContactConstraintPool[ iContact ]; + btScalar residual = resolveSingleConstraintRowLowerLimit( m_tmpSolverBodyPool[ solveManifold.m_solverBodyIdA ], m_tmpSolverBodyPool[ solveManifold.m_solverBodyIdB ], solveManifold ); + leastSquaresResidual += residual*residual; + totalImpulse = solveManifold.m_appliedImpulse; + } + + // apply sliding friction + if ( totalImpulse > 0.0f ) + { + int iBegin = iContact * m_numFrictionDirections; + int iEnd = iBegin + m_numFrictionDirections; + for ( int iFriction = iBegin; iFriction < iEnd; ++iFriction ) + { + btSolverConstraint& solveManifold = m_tmpSolverContactFrictionConstraintPool[ iFriction ]; + btAssert( solveManifold.m_frictionIndex == iContact ); + + solveManifold.m_lowerLimit = -( solveManifold.m_friction*totalImpulse ); + solveManifold.m_upperLimit = solveManifold.m_friction*totalImpulse; + + btSolverBody& bodyA = m_tmpSolverBodyPool[ solveManifold.m_solverBodyIdA ]; + btSolverBody& bodyB = m_tmpSolverBodyPool[ solveManifold.m_solverBodyIdB ]; + btScalar residual = resolveSingleConstraintRowGeneric( bodyA, bodyB, solveManifold ); + leastSquaresResidual += residual*residual; + } + } + + // apply rolling friction + int iFirstRollingFriction = m_rollingFrictionIndexTable[ iContact ]; + if ( totalImpulse > 0.0f && iFirstRollingFriction >= 0) + { + int iBegin = iFirstRollingFriction; + int iEnd = iBegin + 3; + for ( int iRollingFric = iBegin; iRollingFric < iEnd; ++iRollingFric ) + { + btSolverConstraint& rollingFrictionConstraint = m_tmpSolverContactRollingFrictionConstraintPool[ iRollingFric ]; + if ( rollingFrictionConstraint.m_frictionIndex != iContact ) + { + break; + } + btScalar rollingFrictionMagnitude = rollingFrictionConstraint.m_friction*totalImpulse; + if ( rollingFrictionMagnitude > rollingFrictionConstraint.m_friction ) + { + rollingFrictionMagnitude = rollingFrictionConstraint.m_friction; + } + + rollingFrictionConstraint.m_lowerLimit = -rollingFrictionMagnitude; + rollingFrictionConstraint.m_upperLimit = rollingFrictionMagnitude; + + btScalar residual = resolveSingleConstraintRowGeneric( m_tmpSolverBodyPool[ rollingFrictionConstraint.m_solverBodyIdA ], m_tmpSolverBodyPool[ rollingFrictionConstraint.m_solverBodyIdB ], rollingFrictionConstraint ); + leastSquaresResidual += residual*residual; + } + } + } + return leastSquaresResidual; +} + + +void btSequentialImpulseConstraintSolverMt::randomizeBatchedConstraintOrdering( btBatchedConstraints* batchedConstraints ) +{ + btBatchedConstraints& bc = *batchedConstraints; + // randomize ordering of phases + for ( int ii = 1; ii < bc.m_phaseOrder.size(); ++ii ) + { + int iSwap = btRandInt2( ii + 1 ); + bc.m_phaseOrder.swap( ii, iSwap ); + } + + // for each batch, + for ( int iBatch = 0; iBatch < bc.m_batches.size(); ++iBatch ) + { + // randomize ordering of constraints within the batch + const btBatchedConstraints::Range& batch = bc.m_batches[ iBatch ]; + for ( int iiCons = batch.begin; iiCons < batch.end; ++iiCons ) + { + int iSwap = batch.begin + btRandInt2( iiCons - batch.begin + 1 ); + btAssert(iSwap >= batch.begin && iSwap < batch.end); + bc.m_constraintIndices.swap( iiCons, iSwap ); + } + } +} + + +void btSequentialImpulseConstraintSolverMt::randomizeConstraintOrdering(int iteration, int numIterations) +{ + // randomize ordering of joint constraints + randomizeBatchedConstraintOrdering( &m_batchedJointConstraints ); + + //contact/friction constraints are not solved more than numIterations + if ( iteration < numIterations ) + { + randomizeBatchedConstraintOrdering( &m_batchedContactConstraints ); + } +} + + +struct JointSolverLoop : public btIParallelSumBody +{ + btSequentialImpulseConstraintSolverMt* m_solver; + const btBatchedConstraints* m_bc; + int m_iteration; + + JointSolverLoop( btSequentialImpulseConstraintSolverMt* solver, const btBatchedConstraints* bc, int iteration ) + { + m_solver = solver; + m_bc = bc; + m_iteration = iteration; + } + btScalar sumLoop( int iBegin, int iEnd ) const BT_OVERRIDE + { + BT_PROFILE( "JointSolverLoop" ); + btScalar sum = 0; + for ( int iBatch = iBegin; iBatch < iEnd; ++iBatch ) + { + const btBatchedConstraints::Range& batch = m_bc->m_batches[ iBatch ]; + sum += m_solver->resolveMultipleJointConstraints( m_bc->m_constraintIndices, batch.begin, batch.end, m_iteration ); + } + return sum; + } +}; + + +btScalar btSequentialImpulseConstraintSolverMt::resolveAllJointConstraints(int iteration) +{ + BT_PROFILE( "resolveAllJointConstraints" ); + const btBatchedConstraints& batchedCons = m_batchedJointConstraints; + JointSolverLoop loop( this, &batchedCons, iteration ); + btScalar leastSquaresResidual = 0.f; + for ( int iiPhase = 0; iiPhase < batchedCons.m_phases.size(); ++iiPhase ) + { + int iPhase = batchedCons.m_phaseOrder[ iiPhase ]; + const btBatchedConstraints::Range& phase = batchedCons.m_phases[ iPhase ]; + int grainSize = 1; + leastSquaresResidual += btParallelSum( phase.begin, phase.end, grainSize, loop ); + } + return leastSquaresResidual; +} + + +struct ContactSolverLoop : public btIParallelSumBody +{ + btSequentialImpulseConstraintSolverMt* m_solver; + const btBatchedConstraints* m_bc; + + ContactSolverLoop( btSequentialImpulseConstraintSolverMt* solver, const btBatchedConstraints* bc ) + { + m_solver = solver; + m_bc = bc; + } + btScalar sumLoop( int iBegin, int iEnd ) const BT_OVERRIDE + { + BT_PROFILE( "ContactSolverLoop" ); + btScalar sum = 0; + for ( int iBatch = iBegin; iBatch < iEnd; ++iBatch ) + { + const btBatchedConstraints::Range& batch = m_bc->m_batches[ iBatch ]; + sum += m_solver->resolveMultipleContactConstraints( m_bc->m_constraintIndices, batch.begin, batch.end ); + } + return sum; + } +}; + + +btScalar btSequentialImpulseConstraintSolverMt::resolveAllContactConstraints() +{ + BT_PROFILE( "resolveAllContactConstraints" ); + const btBatchedConstraints& batchedCons = m_batchedContactConstraints; + ContactSolverLoop loop( this, &batchedCons ); + btScalar leastSquaresResidual = 0.f; + for ( int iiPhase = 0; iiPhase < batchedCons.m_phases.size(); ++iiPhase ) + { + int iPhase = batchedCons.m_phaseOrder[ iiPhase ]; + const btBatchedConstraints::Range& phase = batchedCons.m_phases[ iPhase ]; + int grainSize = batchedCons.m_phaseGrainSize[iPhase]; + leastSquaresResidual += btParallelSum( phase.begin, phase.end, grainSize, loop ); + } + return leastSquaresResidual; +} + + +struct ContactFrictionSolverLoop : public btIParallelSumBody +{ + btSequentialImpulseConstraintSolverMt* m_solver; + const btBatchedConstraints* m_bc; + + ContactFrictionSolverLoop( btSequentialImpulseConstraintSolverMt* solver, const btBatchedConstraints* bc ) + { + m_solver = solver; + m_bc = bc; + } + btScalar sumLoop( int iBegin, int iEnd ) const BT_OVERRIDE + { + BT_PROFILE( "ContactFrictionSolverLoop" ); + btScalar sum = 0; + for ( int iBatch = iBegin; iBatch < iEnd; ++iBatch ) + { + const btBatchedConstraints::Range& batch = m_bc->m_batches[ iBatch ]; + sum += m_solver->resolveMultipleContactFrictionConstraints( m_bc->m_constraintIndices, batch.begin, batch.end ); + } + return sum; + } +}; + + +btScalar btSequentialImpulseConstraintSolverMt::resolveAllContactFrictionConstraints() +{ + BT_PROFILE( "resolveAllContactFrictionConstraints" ); + const btBatchedConstraints& batchedCons = m_batchedContactConstraints; + ContactFrictionSolverLoop loop( this, &batchedCons ); + btScalar leastSquaresResidual = 0.f; + for ( int iiPhase = 0; iiPhase < batchedCons.m_phases.size(); ++iiPhase ) + { + int iPhase = batchedCons.m_phaseOrder[ iiPhase ]; + const btBatchedConstraints::Range& phase = batchedCons.m_phases[ iPhase ]; + int grainSize = batchedCons.m_phaseGrainSize[iPhase]; + leastSquaresResidual += btParallelSum( phase.begin, phase.end, grainSize, loop ); + } + return leastSquaresResidual; +} + + +struct InterleavedContactSolverLoop : public btIParallelSumBody +{ + btSequentialImpulseConstraintSolverMt* m_solver; + const btBatchedConstraints* m_bc; + + InterleavedContactSolverLoop( btSequentialImpulseConstraintSolverMt* solver, const btBatchedConstraints* bc ) + { + m_solver = solver; + m_bc = bc; + } + btScalar sumLoop( int iBegin, int iEnd ) const BT_OVERRIDE + { + BT_PROFILE( "InterleavedContactSolverLoop" ); + btScalar sum = 0; + for ( int iBatch = iBegin; iBatch < iEnd; ++iBatch ) + { + const btBatchedConstraints::Range& batch = m_bc->m_batches[ iBatch ]; + sum += m_solver->resolveMultipleContactConstraintsInterleaved( m_bc->m_constraintIndices, batch.begin, batch.end ); + } + return sum; + } +}; + + +btScalar btSequentialImpulseConstraintSolverMt::resolveAllContactConstraintsInterleaved() +{ + BT_PROFILE( "resolveAllContactConstraintsInterleaved" ); + const btBatchedConstraints& batchedCons = m_batchedContactConstraints; + InterleavedContactSolverLoop loop( this, &batchedCons ); + btScalar leastSquaresResidual = 0.f; + for ( int iiPhase = 0; iiPhase < batchedCons.m_phases.size(); ++iiPhase ) + { + int iPhase = batchedCons.m_phaseOrder[ iiPhase ]; + const btBatchedConstraints::Range& phase = batchedCons.m_phases[ iPhase ]; + int grainSize = 1; + leastSquaresResidual += btParallelSum( phase.begin, phase.end, grainSize, loop ); + } + return leastSquaresResidual; +} + + +struct ContactRollingFrictionSolverLoop : public btIParallelSumBody +{ + btSequentialImpulseConstraintSolverMt* m_solver; + const btBatchedConstraints* m_bc; + + ContactRollingFrictionSolverLoop( btSequentialImpulseConstraintSolverMt* solver, const btBatchedConstraints* bc ) + { + m_solver = solver; + m_bc = bc; + } + btScalar sumLoop( int iBegin, int iEnd ) const BT_OVERRIDE + { + BT_PROFILE( "ContactFrictionSolverLoop" ); + btScalar sum = 0; + for ( int iBatch = iBegin; iBatch < iEnd; ++iBatch ) + { + const btBatchedConstraints::Range& batch = m_bc->m_batches[ iBatch ]; + sum += m_solver->resolveMultipleContactRollingFrictionConstraints( m_bc->m_constraintIndices, batch.begin, batch.end ); + } + return sum; + } +}; + + +btScalar btSequentialImpulseConstraintSolverMt::resolveAllRollingFrictionConstraints() +{ + BT_PROFILE( "resolveAllRollingFrictionConstraints" ); + btScalar leastSquaresResidual = 0.f; + // + // We do not generate batches for rolling friction constraints. We assume that + // one of two cases is true: + // + // 1. either most bodies in the simulation have rolling friction, in which case we can use the + // batches for contacts and use a lookup table to translate contact indices to rolling friction + // (ignoring any contact indices that don't map to a rolling friction constraint). As long as + // most contacts have a corresponding rolling friction constraint, this should parallelize well. + // + // -OR- + // + // 2. few bodies in the simulation have rolling friction, so it is not worth trying to use the + // batches from contacts as most of the contacts won't have corresponding rolling friction + // constraints and most threads would end up doing very little work. Most of the time would + // go to threading overhead, so we don't bother with threading. + // + int numRollingFrictionPoolConstraints = m_tmpSolverContactRollingFrictionConstraintPool.size(); + if (numRollingFrictionPoolConstraints >= m_tmpSolverContactConstraintPool.size()) + { + // use batching if there are many rolling friction constraints + const btBatchedConstraints& batchedCons = m_batchedContactConstraints; + ContactRollingFrictionSolverLoop loop( this, &batchedCons ); + btScalar leastSquaresResidual = 0.f; + for ( int iiPhase = 0; iiPhase < batchedCons.m_phases.size(); ++iiPhase ) + { + int iPhase = batchedCons.m_phaseOrder[ iiPhase ]; + const btBatchedConstraints::Range& phase = batchedCons.m_phases[ iPhase ]; + int grainSize = 1; + leastSquaresResidual += btParallelSum( phase.begin, phase.end, grainSize, loop ); + } + } + else + { + // no batching, also ignores SOLVER_RANDMIZE_ORDER + for ( int j = 0; j < numRollingFrictionPoolConstraints; j++ ) + { + btSolverConstraint& rollingFrictionConstraint = m_tmpSolverContactRollingFrictionConstraintPool[ j ]; + if ( rollingFrictionConstraint.m_frictionIndex >= 0 ) + { + btScalar totalImpulse = m_tmpSolverContactConstraintPool[ rollingFrictionConstraint.m_frictionIndex ].m_appliedImpulse; + if ( totalImpulse > 0.0f ) + { + btScalar rollingFrictionMagnitude = rollingFrictionConstraint.m_friction*totalImpulse; + if ( rollingFrictionMagnitude > rollingFrictionConstraint.m_friction ) + rollingFrictionMagnitude = rollingFrictionConstraint.m_friction; + + rollingFrictionConstraint.m_lowerLimit = -rollingFrictionMagnitude; + rollingFrictionConstraint.m_upperLimit = rollingFrictionMagnitude; + + btScalar residual = resolveSingleConstraintRowGeneric( m_tmpSolverBodyPool[ rollingFrictionConstraint.m_solverBodyIdA ], m_tmpSolverBodyPool[ rollingFrictionConstraint.m_solverBodyIdB ], rollingFrictionConstraint ); + leastSquaresResidual += residual*residual; + } + } + } + } + return leastSquaresResidual; +} + + +void btSequentialImpulseConstraintSolverMt::internalWriteBackContacts( int iBegin, int iEnd, const btContactSolverInfo& infoGlobal ) +{ + BT_PROFILE("internalWriteBackContacts"); + writeBackContacts(iBegin, iEnd, infoGlobal); + //for ( int iContact = iBegin; iContact < iEnd; ++iContact) + //{ + // const btSolverConstraint& contactConstraint = m_tmpSolverContactConstraintPool[ iContact ]; + // btManifoldPoint* pt = (btManifoldPoint*) contactConstraint.m_originalContactPoint; + // btAssert( pt ); + // pt->m_appliedImpulse = contactConstraint.m_appliedImpulse; + // pt->m_appliedImpulseLateral1 = m_tmpSolverContactFrictionConstraintPool[ contactConstraint.m_frictionIndex ].m_appliedImpulse; + // if ( m_numFrictionDirections == 2 ) + // { + // pt->m_appliedImpulseLateral2 = m_tmpSolverContactFrictionConstraintPool[ contactConstraint.m_frictionIndex + 1 ].m_appliedImpulse; + // } + //} +} + + +void btSequentialImpulseConstraintSolverMt::internalWriteBackJoints( int iBegin, int iEnd, const btContactSolverInfo& infoGlobal ) +{ + BT_PROFILE("internalWriteBackJoints"); + writeBackJoints(iBegin, iEnd, infoGlobal); +} + + +void btSequentialImpulseConstraintSolverMt::internalWriteBackBodies( int iBegin, int iEnd, const btContactSolverInfo& infoGlobal ) +{ + BT_PROFILE("internalWriteBackBodies"); + writeBackBodies( iBegin, iEnd, infoGlobal ); +} + + +struct WriteContactPointsLoop : public btIParallelForBody +{ + btSequentialImpulseConstraintSolverMt* m_solver; + const btContactSolverInfo* m_infoGlobal; + + WriteContactPointsLoop( btSequentialImpulseConstraintSolverMt* solver, const btContactSolverInfo& infoGlobal ) + { + m_solver = solver; + m_infoGlobal = &infoGlobal; + } + void forLoop( int iBegin, int iEnd ) const BT_OVERRIDE + { + m_solver->internalWriteBackContacts( iBegin, iEnd, *m_infoGlobal ); + } +}; + + +struct WriteJointsLoop : public btIParallelForBody +{ + btSequentialImpulseConstraintSolverMt* m_solver; + const btContactSolverInfo* m_infoGlobal; + + WriteJointsLoop( btSequentialImpulseConstraintSolverMt* solver, const btContactSolverInfo& infoGlobal ) + { + m_solver = solver; + m_infoGlobal = &infoGlobal; + } + void forLoop( int iBegin, int iEnd ) const BT_OVERRIDE + { + m_solver->internalWriteBackJoints( iBegin, iEnd, *m_infoGlobal ); + } +}; + + +struct WriteBodiesLoop : public btIParallelForBody +{ + btSequentialImpulseConstraintSolverMt* m_solver; + const btContactSolverInfo* m_infoGlobal; + + WriteBodiesLoop( btSequentialImpulseConstraintSolverMt* solver, const btContactSolverInfo& infoGlobal ) + { + m_solver = solver; + m_infoGlobal = &infoGlobal; + } + void forLoop( int iBegin, int iEnd ) const BT_OVERRIDE + { + m_solver->internalWriteBackBodies( iBegin, iEnd, *m_infoGlobal ); + } +}; + + +btScalar btSequentialImpulseConstraintSolverMt::solveGroupCacheFriendlyFinish(btCollisionObject** bodies, int numBodies, const btContactSolverInfo& infoGlobal) +{ + BT_PROFILE("solveGroupCacheFriendlyFinish"); + + if (infoGlobal.m_solverMode & SOLVER_USE_WARMSTARTING) + { + WriteContactPointsLoop loop( this, infoGlobal ); + int grainSize = 500; + btParallelFor( 0, m_tmpSolverContactConstraintPool.size(), grainSize, loop ); + } + + { + WriteJointsLoop loop( this, infoGlobal ); + int grainSize = 400; + btParallelFor( 0, m_tmpSolverNonContactConstraintPool.size(), grainSize, loop ); + } + { + WriteBodiesLoop loop( this, infoGlobal ); + int grainSize = 100; + btParallelFor( 0, m_tmpSolverBodyPool.size(), grainSize, loop ); + } + + m_tmpSolverContactConstraintPool.resizeNoInitialize(0); + m_tmpSolverNonContactConstraintPool.resizeNoInitialize(0); + m_tmpSolverContactFrictionConstraintPool.resizeNoInitialize(0); + m_tmpSolverContactRollingFrictionConstraintPool.resizeNoInitialize(0); + + m_tmpSolverBodyPool.resizeNoInitialize(0); + return 0.f; +} + diff --git a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolverMt.h b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolverMt.h new file mode 100644 index 0000000000..55d53474c4 --- /dev/null +++ b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolverMt.h @@ -0,0 +1,154 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef BT_SEQUENTIAL_IMPULSE_CONSTRAINT_SOLVER_MT_H +#define BT_SEQUENTIAL_IMPULSE_CONSTRAINT_SOLVER_MT_H + +#include "btSequentialImpulseConstraintSolver.h" +#include "btBatchedConstraints.h" +#include "LinearMath/btThreads.h" + +/// +/// btSequentialImpulseConstraintSolverMt +/// +/// A multithreaded variant of the sequential impulse constraint solver. The constraints to be solved are grouped into +/// batches and phases where each batch of constraints within a given phase can be solved in parallel with the rest. +/// Ideally we want as few phases as possible, and each phase should have many batches, and all of the batches should +/// have about the same number of constraints. +/// This method works best on a large island of many constraints. +/// +/// Supports all of the features of the normal sequential impulse solver such as: +/// - split penetration impulse +/// - rolling friction +/// - interleaving constraints +/// - warmstarting +/// - 2 friction directions +/// - randomized constraint ordering +/// - early termination when leastSquaresResidualThreshold is satisfied +/// +/// When the SOLVER_INTERLEAVE_CONTACT_AND_FRICTION_CONSTRAINTS flag is enabled, unlike the normal SequentialImpulse solver, +/// the rolling friction is interleaved as well. +/// Interleaving the contact penetration constraints with friction reduces the number of parallel loops that need to be done, +/// which reduces threading overhead so it can be a performance win, however, it does seem to produce a less stable simulation, +/// at least on stacks of blocks. +/// +/// When the SOLVER_RANDMIZE_ORDER flag is enabled, the ordering of phases, and the ordering of constraints within each batch +/// is randomized, however it does not swap constraints between batches. +/// This is to avoid regenerating the batches for each solver iteration which would be quite costly in performance. +/// +/// Note that a non-zero leastSquaresResidualThreshold could possibly affect the determinism of the simulation +/// if the task scheduler's parallelSum operation is non-deterministic. The parallelSum operation can be non-deterministic +/// because floating point addition is not associative due to rounding errors. +/// The task scheduler can and should ensure that the result of any parallelSum operation is deterministic. +/// +ATTRIBUTE_ALIGNED16(class) btSequentialImpulseConstraintSolverMt : public btSequentialImpulseConstraintSolver +{ +public: + virtual void solveGroupCacheFriendlySplitImpulseIterations(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer) BT_OVERRIDE; + virtual btScalar solveSingleIteration(int iteration, btCollisionObject** bodies ,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer) BT_OVERRIDE; + virtual btScalar solveGroupCacheFriendlySetup(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer) BT_OVERRIDE; + virtual btScalar solveGroupCacheFriendlyFinish(btCollisionObject** bodies, int numBodies, const btContactSolverInfo& infoGlobal) BT_OVERRIDE; + + // temp struct used to collect info from persistent manifolds into a cache-friendly struct using multiple threads + struct btContactManifoldCachedInfo + { + static const int MAX_NUM_CONTACT_POINTS = 4; + + int numTouchingContacts; + int solverBodyIds[ 2 ]; + int contactIndex; + int rollingFrictionIndex; + bool contactHasRollingFriction[ MAX_NUM_CONTACT_POINTS ]; + btManifoldPoint* contactPoints[ MAX_NUM_CONTACT_POINTS ]; + }; + // temp struct used for setting up joint constraints in parallel + struct JointParams + { + int m_solverConstraint; + int m_solverBodyA; + int m_solverBodyB; + }; + void internalInitMultipleJoints(btTypedConstraint** constraints, int iBegin, int iEnd); + void internalConvertMultipleJoints( const btAlignedObjectArray<JointParams>& jointParamsArray, btTypedConstraint** constraints, int iBegin, int iEnd, const btContactSolverInfo& infoGlobal ); + + // parameters to control batching + static bool s_allowNestedParallelForLoops; // whether to allow nested parallel operations + static int s_minimumContactManifoldsForBatching; // don't even try to batch if fewer manifolds than this + static btBatchedConstraints::BatchingMethod s_contactBatchingMethod; + static btBatchedConstraints::BatchingMethod s_jointBatchingMethod; + static int s_minBatchSize; // desired number of constraints per batch + static int s_maxBatchSize; + +protected: + static const int CACHE_LINE_SIZE = 64; + + btBatchedConstraints m_batchedContactConstraints; + btBatchedConstraints m_batchedJointConstraints; + int m_numFrictionDirections; + bool m_useBatching; + bool m_useObsoleteJointConstraints; + btAlignedObjectArray<btContactManifoldCachedInfo> m_manifoldCachedInfoArray; + btAlignedObjectArray<int> m_rollingFrictionIndexTable; // lookup table mapping contact index to rolling friction index + btSpinMutex m_bodySolverArrayMutex; + char m_antiFalseSharingPadding[CACHE_LINE_SIZE]; // padding to keep mutexes in separate cachelines + btSpinMutex m_kinematicBodyUniqueIdToSolverBodyTableMutex; + btAlignedObjectArray<char> m_scratchMemory; + + virtual void randomizeConstraintOrdering( int iteration, int numIterations ); + virtual btScalar resolveAllJointConstraints( int iteration ); + virtual btScalar resolveAllContactConstraints(); + virtual btScalar resolveAllContactFrictionConstraints(); + virtual btScalar resolveAllContactConstraintsInterleaved(); + virtual btScalar resolveAllRollingFrictionConstraints(); + + virtual void setupBatchedContactConstraints(); + virtual void setupBatchedJointConstraints(); + virtual void convertJoints(btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal) BT_OVERRIDE; + virtual void convertContacts(btPersistentManifold** manifoldPtr, int numManifolds, const btContactSolverInfo& infoGlobal) BT_OVERRIDE; + virtual void convertBodies(btCollisionObject** bodies, int numBodies, const btContactSolverInfo& infoGlobal) BT_OVERRIDE; + + int getOrInitSolverBodyThreadsafe(btCollisionObject& body, btScalar timeStep); + void allocAllContactConstraints(btPersistentManifold** manifoldPtr, int numManifolds, const btContactSolverInfo& infoGlobal); + void setupAllContactConstraints(const btContactSolverInfo& infoGlobal); + void randomizeBatchedConstraintOrdering( btBatchedConstraints* batchedConstraints ); + +public: + + BT_DECLARE_ALIGNED_ALLOCATOR(); + + btSequentialImpulseConstraintSolverMt(); + virtual ~btSequentialImpulseConstraintSolverMt(); + + btScalar resolveMultipleJointConstraints( const btAlignedObjectArray<int>& consIndices, int batchBegin, int batchEnd, int iteration ); + btScalar resolveMultipleContactConstraints( const btAlignedObjectArray<int>& consIndices, int batchBegin, int batchEnd ); + btScalar resolveMultipleContactSplitPenetrationImpulseConstraints( const btAlignedObjectArray<int>& consIndices, int batchBegin, int batchEnd ); + btScalar resolveMultipleContactFrictionConstraints( const btAlignedObjectArray<int>& consIndices, int batchBegin, int batchEnd ); + btScalar resolveMultipleContactRollingFrictionConstraints( const btAlignedObjectArray<int>& consIndices, int batchBegin, int batchEnd ); + btScalar resolveMultipleContactConstraintsInterleaved( const btAlignedObjectArray<int>& contactIndices, int batchBegin, int batchEnd ); + + void internalCollectContactManifoldCachedInfo(btContactManifoldCachedInfo* cachedInfoArray, btPersistentManifold** manifoldPtr, int numManifolds, const btContactSolverInfo& infoGlobal); + void internalAllocContactConstraints(const btContactManifoldCachedInfo* cachedInfoArray, int numManifolds); + void internalSetupContactConstraints(int iContactConstraint, const btContactSolverInfo& infoGlobal); + void internalConvertBodies(btCollisionObject** bodies, int iBegin, int iEnd, const btContactSolverInfo& infoGlobal); + void internalWriteBackContacts(int iBegin, int iEnd, const btContactSolverInfo& infoGlobal); + void internalWriteBackJoints(int iBegin, int iEnd, const btContactSolverInfo& infoGlobal); + void internalWriteBackBodies(int iBegin, int iEnd, const btContactSolverInfo& infoGlobal); +}; + + + + +#endif //BT_SEQUENTIAL_IMPULSE_CONSTRAINT_SOLVER_MT_H + diff --git a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btSliderConstraint.cpp b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btSliderConstraint.cpp index d63cef0316..d63cef0316 100644..100755 --- a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btSliderConstraint.cpp +++ b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btSliderConstraint.cpp diff --git a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btSliderConstraint.h b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btSliderConstraint.h index 1957f08a96..1957f08a96 100644..100755 --- a/thirdparty/bullet/BulletDynamics/ConstraintSolver/btSliderConstraint.h +++ b/thirdparty/bullet/BulletDynamics/ConstraintSolver/btSliderConstraint.h diff --git a/thirdparty/bullet/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.cpp b/thirdparty/bullet/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.cpp index a196d4522e..b9944c138b 100644 --- a/thirdparty/bullet/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.cpp +++ b/thirdparty/bullet/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.cpp @@ -842,6 +842,9 @@ public: btCollisionObject* otherObj = (btCollisionObject*) proxy0->m_clientObject; + if(!m_dispatcher->needsCollision(m_me, otherObj)) + return false; + //call needsResponse, see http://code.google.com/p/bullet/issues/detail?id=179 if (m_dispatcher->needsResponse(m_me,otherObj)) { @@ -1342,9 +1345,12 @@ void btDiscreteDynamicsWorld::debugDrawConstraint(btTypedConstraint* constraint) btVector3 axis = tr.getBasis().getColumn(0); btScalar minTh = p6DOF->getRotationalLimitMotor(1)->m_loLimit; btScalar maxTh = p6DOF->getRotationalLimitMotor(1)->m_hiLimit; - btScalar minPs = p6DOF->getRotationalLimitMotor(2)->m_loLimit; - btScalar maxPs = p6DOF->getRotationalLimitMotor(2)->m_hiLimit; - getDebugDrawer()->drawSpherePatch(center, up, axis, dbgDrawSize * btScalar(.9f), minTh, maxTh, minPs, maxPs, btVector3(0, 0, 0)); + if (minTh <= maxTh) + { + btScalar minPs = p6DOF->getRotationalLimitMotor(2)->m_loLimit; + btScalar maxPs = p6DOF->getRotationalLimitMotor(2)->m_hiLimit; + getDebugDrawer()->drawSpherePatch(center, up, axis, dbgDrawSize * btScalar(.9f), minTh, maxTh, minPs, maxPs, btVector3(0, 0, 0)); + } axis = tr.getBasis().getColumn(1); btScalar ay = p6DOF->getAngle(1); btScalar az = p6DOF->getAngle(2); @@ -1533,6 +1539,8 @@ void btDiscreteDynamicsWorld::serialize(btSerializer* serializer) serializeRigidBodies(serializer); + serializeContactManifolds(serializer); + serializer->finishSerialization(); } diff --git a/thirdparty/bullet/BulletDynamics/Dynamics/btDiscreteDynamicsWorldMt.cpp b/thirdparty/bullet/BulletDynamics/Dynamics/btDiscreteDynamicsWorldMt.cpp index 1d10bad922..d705bf2381 100644 --- a/thirdparty/bullet/BulletDynamics/Dynamics/btDiscreteDynamicsWorldMt.cpp +++ b/thirdparty/bullet/BulletDynamics/Dynamics/btDiscreteDynamicsWorldMt.cpp @@ -50,63 +50,6 @@ subject to the following restrictions: #include "LinearMath/btSerializer.h" -struct InplaceSolverIslandCallbackMt : public btSimulationIslandManagerMt::IslandCallback -{ - btContactSolverInfo* m_solverInfo; - btConstraintSolver* m_solver; - btIDebugDraw* m_debugDrawer; - btDispatcher* m_dispatcher; - - InplaceSolverIslandCallbackMt( - btConstraintSolver* solver, - btStackAlloc* stackAlloc, - btDispatcher* dispatcher) - :m_solverInfo(NULL), - m_solver(solver), - m_debugDrawer(NULL), - m_dispatcher(dispatcher) - { - - } - - InplaceSolverIslandCallbackMt& operator=(InplaceSolverIslandCallbackMt& other) - { - btAssert(0); - (void)other; - return *this; - } - - SIMD_FORCE_INLINE void setup ( btContactSolverInfo* solverInfo, btIDebugDraw* debugDrawer) - { - btAssert(solverInfo); - m_solverInfo = solverInfo; - m_debugDrawer = debugDrawer; - } - - - virtual void processIsland( btCollisionObject** bodies, - int numBodies, - btPersistentManifold** manifolds, - int numManifolds, - btTypedConstraint** constraints, - int numConstraints, - int islandId - ) - { - m_solver->solveGroup( bodies, - numBodies, - manifolds, - numManifolds, - constraints, - numConstraints, - *m_solverInfo, - m_debugDrawer, - m_dispatcher - ); - } - -}; - /// /// btConstraintSolverPoolMt @@ -209,7 +152,12 @@ void btConstraintSolverPoolMt::reset() /// btDiscreteDynamicsWorldMt /// -btDiscreteDynamicsWorldMt::btDiscreteDynamicsWorldMt(btDispatcher* dispatcher, btBroadphaseInterface* pairCache, btConstraintSolverPoolMt* constraintSolver, btCollisionConfiguration* collisionConfiguration) +btDiscreteDynamicsWorldMt::btDiscreteDynamicsWorldMt(btDispatcher* dispatcher, + btBroadphaseInterface* pairCache, + btConstraintSolverPoolMt* constraintSolver, + btConstraintSolver* constraintSolverMt, + btCollisionConfiguration* collisionConfiguration +) : btDiscreteDynamicsWorld(dispatcher,pairCache,constraintSolver,collisionConfiguration) { if (m_ownsIslandManager) @@ -217,31 +165,18 @@ btDiscreteDynamicsWorldMt::btDiscreteDynamicsWorldMt(btDispatcher* dispatcher, b m_islandManager->~btSimulationIslandManager(); btAlignedFree( m_islandManager); } - { - void* mem = btAlignedAlloc(sizeof(InplaceSolverIslandCallbackMt),16); - m_solverIslandCallbackMt = new (mem) InplaceSolverIslandCallbackMt (m_constraintSolver, 0, dispatcher); - } { void* mem = btAlignedAlloc(sizeof(btSimulationIslandManagerMt),16); btSimulationIslandManagerMt* im = new (mem) btSimulationIslandManagerMt(); im->setMinimumSolverBatchSize( m_solverInfo.m_minimumSolverBatchSize ); m_islandManager = im; } + m_constraintSolverMt = constraintSolverMt; } btDiscreteDynamicsWorldMt::~btDiscreteDynamicsWorldMt() { - if (m_solverIslandCallbackMt) - { - m_solverIslandCallbackMt->~InplaceSolverIslandCallbackMt(); - btAlignedFree(m_solverIslandCallbackMt); - } - if (m_ownsConstraintSolver) - { - m_constraintSolver->~btConstraintSolver(); - btAlignedFree(m_constraintSolver); - } } @@ -249,12 +184,17 @@ void btDiscreteDynamicsWorldMt::solveConstraints(btContactSolverInfo& solverInfo { BT_PROFILE("solveConstraints"); - m_solverIslandCallbackMt->setup(&solverInfo, getDebugDrawer()); m_constraintSolver->prepareSolve(getCollisionWorld()->getNumCollisionObjects(), getCollisionWorld()->getDispatcher()->getNumManifolds()); /// solve all the constraints for this island btSimulationIslandManagerMt* im = static_cast<btSimulationIslandManagerMt*>(m_islandManager); - im->buildAndProcessIslands( getCollisionWorld()->getDispatcher(), getCollisionWorld(), m_constraints, m_solverIslandCallbackMt ); + btSimulationIslandManagerMt::SolverParams solverParams; + solverParams.m_solverPool = m_constraintSolver; + solverParams.m_solverMt = m_constraintSolverMt; + solverParams.m_solverInfo = &solverInfo; + solverParams.m_debugDrawer = m_debugDrawer; + solverParams.m_dispatcher = getCollisionWorld()->getDispatcher(); + im->buildAndProcessIslands( getCollisionWorld()->getDispatcher(), getCollisionWorld(), m_constraints, solverParams ); m_constraintSolver->allSolved(solverInfo, m_debugDrawer); } @@ -325,3 +265,14 @@ void btDiscreteDynamicsWorldMt::integrateTransforms( btScalar timeStep ) } } + +int btDiscreteDynamicsWorldMt::stepSimulation( btScalar timeStep, int maxSubSteps, btScalar fixedTimeStep ) +{ + int numSubSteps = btDiscreteDynamicsWorld::stepSimulation(timeStep, maxSubSteps, fixedTimeStep); + if (btITaskScheduler* scheduler = btGetTaskScheduler()) + { + // tell Bullet's threads to sleep, so other threads can run + scheduler->sleepWorkerThreadsHint(); + } + return numSubSteps; +} diff --git a/thirdparty/bullet/BulletDynamics/Dynamics/btDiscreteDynamicsWorldMt.h b/thirdparty/bullet/BulletDynamics/Dynamics/btDiscreteDynamicsWorldMt.h index 2f144cdda4..667fe5800e 100644 --- a/thirdparty/bullet/BulletDynamics/Dynamics/btDiscreteDynamicsWorldMt.h +++ b/thirdparty/bullet/BulletDynamics/Dynamics/btDiscreteDynamicsWorldMt.h @@ -21,7 +21,6 @@ subject to the following restrictions: #include "btSimulationIslandManagerMt.h" #include "BulletDynamics/ConstraintSolver/btConstraintSolver.h" -struct InplaceSolverIslandCallbackMt; /// /// btConstraintSolverPoolMt - masquerades as a constraint solver, but really it is a threadsafe pool of them. @@ -88,7 +87,7 @@ private: ATTRIBUTE_ALIGNED16(class) btDiscreteDynamicsWorldMt : public btDiscreteDynamicsWorld { protected: - InplaceSolverIslandCallbackMt* m_solverIslandCallbackMt; + btConstraintSolver* m_constraintSolverMt; virtual void solveConstraints(btContactSolverInfo& solverInfo) BT_OVERRIDE; @@ -126,9 +125,12 @@ public: btDiscreteDynamicsWorldMt(btDispatcher* dispatcher, btBroadphaseInterface* pairCache, btConstraintSolverPoolMt* constraintSolver, // Note this should be a solver-pool for multi-threading + btConstraintSolver* constraintSolverMt, // single multi-threaded solver for large islands (or NULL) btCollisionConfiguration* collisionConfiguration ); virtual ~btDiscreteDynamicsWorldMt(); + + virtual int stepSimulation( btScalar timeStep, int maxSubSteps, btScalar fixedTimeStep ) BT_OVERRIDE; }; #endif //BT_DISCRETE_DYNAMICS_WORLD_H diff --git a/thirdparty/bullet/BulletDynamics/Dynamics/btSimulationIslandManagerMt.cpp b/thirdparty/bullet/BulletDynamics/Dynamics/btSimulationIslandManagerMt.cpp index 99b34353c7..fc54f0ba6e 100644 --- a/thirdparty/bullet/BulletDynamics/Dynamics/btSimulationIslandManagerMt.cpp +++ b/thirdparty/bullet/BulletDynamics/Dynamics/btSimulationIslandManagerMt.cpp @@ -22,6 +22,7 @@ subject to the following restrictions: #include "BulletCollision/CollisionDispatch/btCollisionObject.h" #include "BulletCollision/CollisionDispatch/btCollisionWorld.h" #include "BulletDynamics/ConstraintSolver/btTypedConstraint.h" +#include "BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolverMt.h" // for s_minimumContactManifoldsForBatching //#include <stdio.h> #include "LinearMath/btQuickprof.h" @@ -275,7 +276,7 @@ btSimulationIslandManagerMt::Island* btSimulationIslandManagerMt::allocateIsland void btSimulationIslandManagerMt::buildIslands( btDispatcher* dispatcher, btCollisionWorld* collisionWorld ) { - BT_PROFILE("islandUnionFindAndQuickSort"); + BT_PROFILE("buildIslands"); btCollisionObjectArray& collisionObjects = collisionWorld->getCollisionObjectArray(); @@ -314,13 +315,11 @@ void btSimulationIslandManagerMt::buildIslands( btDispatcher* dispatcher, btColl btAssert((colObj0->getIslandTag() == islandId) || (colObj0->getIslandTag() == -1)); if (colObj0->getIslandTag() == islandId) { - if (colObj0->getActivationState()== ACTIVE_TAG) - { - allSleeping = false; - } - if (colObj0->getActivationState()== DISABLE_DEACTIVATION) + if (colObj0->getActivationState()== ACTIVE_TAG || + colObj0->getActivationState()== DISABLE_DEACTIVATION) { allSleeping = false; + break; } } } @@ -546,59 +545,103 @@ void btSimulationIslandManagerMt::mergeIslands() } -void btSimulationIslandManagerMt::serialIslandDispatch( btAlignedObjectArray<Island*>* islandsPtr, IslandCallback* callback ) +void btSimulationIslandManagerMt::solveIsland(btConstraintSolver* solver, Island& island, const SolverParams& solverParams) +{ + btPersistentManifold** manifolds = island.manifoldArray.size() ? &island.manifoldArray[ 0 ] : NULL; + btTypedConstraint** constraintsPtr = island.constraintArray.size() ? &island.constraintArray[ 0 ] : NULL; + solver->solveGroup( &island.bodyArray[ 0 ], + island.bodyArray.size(), + manifolds, + island.manifoldArray.size(), + constraintsPtr, + island.constraintArray.size(), + *solverParams.m_solverInfo, + solverParams.m_debugDrawer, + solverParams.m_dispatcher + ); +} + + +void btSimulationIslandManagerMt::serialIslandDispatch( btAlignedObjectArray<Island*>* islandsPtr, const SolverParams& solverParams ) { BT_PROFILE( "serialIslandDispatch" ); // serial dispatch btAlignedObjectArray<Island*>& islands = *islandsPtr; + btConstraintSolver* solver = solverParams.m_solverMt ? solverParams.m_solverMt : solverParams.m_solverPool; for ( int i = 0; i < islands.size(); ++i ) { - Island* island = islands[ i ]; - btPersistentManifold** manifolds = island->manifoldArray.size() ? &island->manifoldArray[ 0 ] : NULL; - btTypedConstraint** constraintsPtr = island->constraintArray.size() ? &island->constraintArray[ 0 ] : NULL; - callback->processIsland( &island->bodyArray[ 0 ], - island->bodyArray.size(), - manifolds, - island->manifoldArray.size(), - constraintsPtr, - island->constraintArray.size(), - island->id - ); + solveIsland(solver, *islands[ i ], solverParams); } } + struct UpdateIslandDispatcher : public btIParallelForBody { - btAlignedObjectArray<btSimulationIslandManagerMt::Island*>* islandsPtr; - btSimulationIslandManagerMt::IslandCallback* callback; + btAlignedObjectArray<btSimulationIslandManagerMt::Island*>& m_islandsPtr; + const btSimulationIslandManagerMt::SolverParams& m_solverParams; + + UpdateIslandDispatcher(btAlignedObjectArray<btSimulationIslandManagerMt::Island*>& islandsPtr, const btSimulationIslandManagerMt::SolverParams& solverParams) + : m_islandsPtr(islandsPtr), m_solverParams(solverParams) + {} void forLoop( int iBegin, int iEnd ) const BT_OVERRIDE { + btConstraintSolver* solver = m_solverParams.m_solverPool; for ( int i = iBegin; i < iEnd; ++i ) { - btSimulationIslandManagerMt::Island* island = ( *islandsPtr )[ i ]; - btPersistentManifold** manifolds = island->manifoldArray.size() ? &island->manifoldArray[ 0 ] : NULL; - btTypedConstraint** constraintsPtr = island->constraintArray.size() ? &island->constraintArray[ 0 ] : NULL; - callback->processIsland( &island->bodyArray[ 0 ], - island->bodyArray.size(), - manifolds, - island->manifoldArray.size(), - constraintsPtr, - island->constraintArray.size(), - island->id - ); + btSimulationIslandManagerMt::Island* island = m_islandsPtr[ i ]; + btSimulationIslandManagerMt::solveIsland( solver, *island, m_solverParams ); } } }; -void btSimulationIslandManagerMt::parallelIslandDispatch( btAlignedObjectArray<Island*>* islandsPtr, IslandCallback* callback ) + +void btSimulationIslandManagerMt::parallelIslandDispatch( btAlignedObjectArray<Island*>* islandsPtr, const SolverParams& solverParams ) { BT_PROFILE( "parallelIslandDispatch" ); - int grainSize = 1; // iterations per task - UpdateIslandDispatcher dispatcher; - dispatcher.islandsPtr = islandsPtr; - dispatcher.callback = callback; - btParallelFor( 0, islandsPtr->size(), grainSize, dispatcher ); + // + // if there are islands with many contacts, it may be faster to submit these + // large islands *serially* to a single parallel constraint solver, and then later + // submit the remaining smaller islands in parallel to multiple sequential solvers. + // + // Some task schedulers do not deal well with nested parallelFor loops. One implementation + // of OpenMP was actually slower than doing everything single-threaded. Intel TBB + // on the other hand, seems to do a pretty respectable job with it. + // + // When solving islands in parallel, the worst case performance happens when there + // is one very large island and then perhaps a smattering of very small + // islands -- one worker thread takes the large island and the remaining workers + // tear through the smaller islands and then sit idle waiting for the first worker + // to finish. Solving islands in parallel works best when there are numerous small + // islands, roughly equal in size. + // + // By contrast, the other approach -- the parallel constraint solver -- is only + // able to deliver a worthwhile speedup when the island is large. For smaller islands, + // it is difficult to extract a useful amount of parallelism -- the overhead of grouping + // the constraints into batches and sending the batches to worker threads can nullify + // any gains from parallelism. + // + + UpdateIslandDispatcher dispatcher(*islandsPtr, solverParams); + // We take advantage of the fact the islands are sorted in order of decreasing size + int iBegin = 0; + if (solverParams.m_solverMt) + { + while ( iBegin < islandsPtr->size() ) + { + btSimulationIslandManagerMt::Island* island = ( *islandsPtr )[ iBegin ]; + if ( island->manifoldArray.size() < btSequentialImpulseConstraintSolverMt::s_minimumContactManifoldsForBatching ) + { + // OK to submit the rest of the array in parallel + break; + } + // serial dispatch to parallel solver for large islands (if any) + solveIsland(solverParams.m_solverMt, *island, solverParams); + ++iBegin; + } + } + // parallel dispatch to sequential solvers for rest + btParallelFor( iBegin, islandsPtr->size(), 1, dispatcher ); } @@ -606,15 +649,14 @@ void btSimulationIslandManagerMt::parallelIslandDispatch( btAlignedObjectArray<I void btSimulationIslandManagerMt::buildAndProcessIslands( btDispatcher* dispatcher, btCollisionWorld* collisionWorld, btAlignedObjectArray<btTypedConstraint*>& constraints, - IslandCallback* callback + const SolverParams& solverParams ) { + BT_PROFILE("buildAndProcessIslands"); btCollisionObjectArray& collisionObjects = collisionWorld->getCollisionObjectArray(); buildIslands(dispatcher,collisionWorld); - BT_PROFILE("processIslands"); - if(!getSplitIslands()) { btPersistentManifold** manifolds = dispatcher->getInternalManifoldPointer(); @@ -646,14 +688,17 @@ void btSimulationIslandManagerMt::buildAndProcessIslands( btDispatcher* dispatch } } btTypedConstraint** constraintsPtr = constraints.size() ? &constraints[ 0 ] : NULL; - callback->processIsland(&collisionObjects[0], - collisionObjects.size(), - manifolds, - maxNumManifolds, - constraintsPtr, - constraints.size(), - -1 - ); + btConstraintSolver* solver = solverParams.m_solverMt ? solverParams.m_solverMt : solverParams.m_solverPool; + solver->solveGroup(&collisionObjects[0], + collisionObjects.size(), + manifolds, + maxNumManifolds, + constraintsPtr, + constraints.size(), + *solverParams.m_solverInfo, + solverParams.m_debugDrawer, + solverParams.m_dispatcher + ); } else { @@ -673,6 +718,6 @@ void btSimulationIslandManagerMt::buildAndProcessIslands( btDispatcher* dispatch mergeIslands(); } // dispatch islands to solver - m_islandDispatch( &m_activeIslands, callback ); + m_islandDispatch( &m_activeIslands, solverParams ); } } diff --git a/thirdparty/bullet/BulletDynamics/Dynamics/btSimulationIslandManagerMt.h b/thirdparty/bullet/BulletDynamics/Dynamics/btSimulationIslandManagerMt.h index 9a781aaef1..563577a6f4 100644 --- a/thirdparty/bullet/BulletDynamics/Dynamics/btSimulationIslandManagerMt.h +++ b/thirdparty/bullet/BulletDynamics/Dynamics/btSimulationIslandManagerMt.h @@ -19,7 +19,9 @@ subject to the following restrictions: #include "BulletCollision/CollisionDispatch/btSimulationIslandManager.h" class btTypedConstraint; - +class btConstraintSolver; +struct btContactSolverInfo; +class btIDebugDraw; /// /// SimulationIslandManagerMt -- Multithread capable version of SimulationIslandManager @@ -45,22 +47,19 @@ public: void append( const Island& other ); // add bodies, manifolds, constraints to my own }; - struct IslandCallback + struct SolverParams { - virtual ~IslandCallback() {}; - - virtual void processIsland( btCollisionObject** bodies, - int numBodies, - btPersistentManifold** manifolds, - int numManifolds, - btTypedConstraint** constraints, - int numConstraints, - int islandId - ) = 0; + btConstraintSolver* m_solverPool; + btConstraintSolver* m_solverMt; + btContactSolverInfo* m_solverInfo; + btIDebugDraw* m_debugDrawer; + btDispatcher* m_dispatcher; }; - typedef void( *IslandDispatchFunc ) ( btAlignedObjectArray<Island*>* islands, IslandCallback* callback ); - static void serialIslandDispatch( btAlignedObjectArray<Island*>* islandsPtr, IslandCallback* callback ); - static void parallelIslandDispatch( btAlignedObjectArray<Island*>* islandsPtr, IslandCallback* callback ); + static void solveIsland(btConstraintSolver* solver, Island& island, const SolverParams& solverParams); + + typedef void( *IslandDispatchFunc ) ( btAlignedObjectArray<Island*>* islands, const SolverParams& solverParams ); + static void serialIslandDispatch( btAlignedObjectArray<Island*>* islandsPtr, const SolverParams& solverParams ); + static void parallelIslandDispatch( btAlignedObjectArray<Island*>* islandsPtr, const SolverParams& solverParams ); protected: btAlignedObjectArray<Island*> m_allocatedIslands; // owner of all Islands btAlignedObjectArray<Island*> m_activeIslands; // islands actively in use @@ -83,7 +82,11 @@ public: btSimulationIslandManagerMt(); virtual ~btSimulationIslandManagerMt(); - virtual void buildAndProcessIslands( btDispatcher* dispatcher, btCollisionWorld* collisionWorld, btAlignedObjectArray<btTypedConstraint*>& constraints, IslandCallback* callback ); + virtual void buildAndProcessIslands( btDispatcher* dispatcher, + btCollisionWorld* collisionWorld, + btAlignedObjectArray<btTypedConstraint*>& constraints, + const SolverParams& solverParams + ); virtual void buildIslands(btDispatcher* dispatcher,btCollisionWorld* colWorld); @@ -106,5 +109,6 @@ public: } }; + #endif //BT_SIMULATION_ISLAND_MANAGER_H diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBody.cpp b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBody.cpp index 62865e0c78..0e85b55728 100644 --- a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBody.cpp +++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBody.cpp @@ -112,14 +112,15 @@ btMultiBody::btMultiBody(int n_links, m_userObjectPointer(0), m_userIndex2(-1), m_userIndex(-1), + m_companionId(-1), m_linearDamping(0.04f), m_angularDamping(0.04f), m_useGyroTerm(true), - m_maxAppliedImpulse(1000.f), + m_maxAppliedImpulse(1000.f), m_maxCoordinateVelocity(100.f), - m_hasSelfCollision(true), + m_hasSelfCollision(true), __posUpdated(false), - m_dofCount(0), + m_dofCount(0), m_posVarCnt(0), m_useRK4(false), m_useGlobalVelocities(false), @@ -136,6 +137,9 @@ btMultiBody::btMultiBody(int n_links, m_baseForce.setValue(0, 0, 0); m_baseTorque.setValue(0, 0, 0); + + clearConstraintForces(); + clearForcesAndTorques(); } btMultiBody::~btMultiBody() @@ -740,13 +744,13 @@ void btMultiBody::computeAccelerationsArticulatedBodyAlgorithmMultiDof(btScalar const btScalar DAMPING_K1_ANGULAR = m_angularDamping; const btScalar DAMPING_K2_ANGULAR= m_angularDamping; - btVector3 base_vel = getBaseVel(); - btVector3 base_omega = getBaseOmega(); + const btVector3 base_vel = getBaseVel(); + const btVector3 base_omega = getBaseOmega(); // Temporary matrices/vectors -- use scratch space from caller // so that we don't have to keep reallocating every frame - scratch_r.resize(2*m_dofCount + 6); //multidof? ("Y"s use it and it is used to store qdd) => 2 x m_dofCount + scratch_r.resize(2*m_dofCount + 7); //multidof? ("Y"s use it and it is used to store qdd) => 2 x m_dofCount scratch_v.resize(8*num_links + 6); scratch_m.resize(4*num_links + 4); @@ -777,7 +781,7 @@ void btMultiBody::computeAccelerationsArticulatedBodyAlgorithmMultiDof(btScalar // hhat is NOT stored for the base (but ahat is) btSpatialForceVector * h = (btSpatialForceVector *)(m_dofCount > 0 ? &m_vectorBuf[0] : 0); btSpatialMotionVector * spatAcc = (btSpatialMotionVector *)v_ptr; - v_ptr += num_links * 2 + 2; + v_ptr += num_links * 2 + 2; // // Y_i, invD_i btScalar * invD = m_dofCount > 0 ? &m_realBuf[6 + m_dofCount] : 0; @@ -815,13 +819,13 @@ void btMultiBody::computeAccelerationsArticulatedBodyAlgorithmMultiDof(btScalar } else { - btVector3 baseForce = isConstraintPass? m_baseConstraintForce : m_baseForce; - btVector3 baseTorque = isConstraintPass? m_baseConstraintTorque : m_baseTorque; + const btVector3& baseForce = isConstraintPass? m_baseConstraintForce : m_baseForce; + const btVector3& baseTorque = isConstraintPass? m_baseConstraintTorque : m_baseTorque; //external forces zeroAccSpatFrc[0].setVector(-(rot_from_parent[0] * baseTorque), -(rot_from_parent[0] * baseForce)); //adding damping terms (only) - btScalar linDampMult = 1., angDampMult = 1.; + const btScalar linDampMult = 1., angDampMult = 1.; zeroAccSpatFrc[0].addVector(angDampMult * m_baseInertia * spatVel[0].getAngular() * (DAMPING_K1_ANGULAR + DAMPING_K2_ANGULAR * spatVel[0].getAngular().safeNorm()), linDampMult * m_baseMass * spatVel[0].getLinear() * (DAMPING_K1_LINEAR + DAMPING_K2_LINEAR * spatVel[0].getLinear().safeNorm())); @@ -963,16 +967,15 @@ void btMultiBody::computeAccelerationsArticulatedBodyAlgorithmMultiDof(btScalar // Y[m_links[i].m_dofOffset + dof] = m_links[i].m_jointTorque[dof] - m_links[i].m_axes[dof].dot(zeroAccSpatFrc[i+1]) - - spatCoriolisAcc[i].dot(hDof) - ; - } + - spatCoriolisAcc[i].dot(hDof); - for(int dof = 0; dof < m_links[i].m_dofCount; ++dof) - { - btScalar *D_row = &D[dof * m_links[i].m_dofCount]; + } + for(int dof = 0; dof < m_links[i].m_dofCount; ++dof) + { + btScalar *D_row = &D[dof * m_links[i].m_dofCount]; for(int dof2 = 0; dof2 < m_links[i].m_dofCount; ++dof2) { - btSpatialForceVector &hDof2 = h[m_links[i].m_dofOffset + dof2]; + const btSpatialForceVector &hDof2 = h[m_links[i].m_dofOffset + dof2]; D_row[dof2] = m_links[i].m_axes[dof].dot(hDof2); } } @@ -983,14 +986,20 @@ void btMultiBody::computeAccelerationsArticulatedBodyAlgorithmMultiDof(btScalar case btMultibodyLink::ePrismatic: case btMultibodyLink::eRevolute: { - invDi[0] = 1.0f / D[0]; + if (D[0]>=SIMD_EPSILON) + { + invDi[0] = 1.0f / D[0]; + } else + { + invDi[0] = 0; + } break; } case btMultibodyLink::eSpherical: case btMultibodyLink::ePlanar: { - btMatrix3x3 D3x3; D3x3.setValue(D[0], D[1], D[2], D[3], D[4], D[5], D[6], D[7], D[8]); - btMatrix3x3 invD3x3; invD3x3 = D3x3.inverse(); + const btMatrix3x3 D3x3(D[0], D[1], D[2], D[3], D[4], D[5], D[6], D[7], D[8]); + const btMatrix3x3 invD3x3(D3x3.inverse()); //unroll the loop? for(int row = 0; row < 3; ++row) @@ -1016,7 +1025,7 @@ void btMultiBody::computeAccelerationsArticulatedBodyAlgorithmMultiDof(btScalar for(int dof2 = 0; dof2 < m_links[i].m_dofCount; ++dof2) { - btSpatialForceVector &hDof2 = h[m_links[i].m_dofOffset + dof2]; + const btSpatialForceVector &hDof2 = h[m_links[i].m_dofOffset + dof2]; // spatForceVecTemps[dof] += hDof2 * invDi[dof2 * m_links[i].m_dofCount + dof]; } @@ -1027,7 +1036,7 @@ void btMultiBody::computeAccelerationsArticulatedBodyAlgorithmMultiDof(btScalar //determine (h*D^{-1}) * h^{T} for(int dof = 0; dof < m_links[i].m_dofCount; ++dof) { - btSpatialForceVector &hDof = h[m_links[i].m_dofOffset + dof]; + const btSpatialForceVector &hDof = h[m_links[i].m_dofOffset + dof]; // dyadTemp -= symmetricSpatialOuterProduct(hDof, spatForceVecTemps[dof]); } @@ -1048,7 +1057,7 @@ void btMultiBody::computeAccelerationsArticulatedBodyAlgorithmMultiDof(btScalar for(int dof = 0; dof < m_links[i].m_dofCount; ++dof) { - btSpatialForceVector &hDof = h[m_links[i].m_dofOffset + dof]; + const btSpatialForceVector &hDof = h[m_links[i].m_dofOffset + dof]; // spatForceVecTemps[0] += hDof * invD_times_Y[dof]; } @@ -1099,7 +1108,7 @@ void btMultiBody::computeAccelerationsArticulatedBodyAlgorithmMultiDof(btScalar for(int dof = 0; dof < m_links[i].m_dofCount; ++dof) { - btSpatialForceVector &hDof = h[m_links[i].m_dofOffset + dof]; + const btSpatialForceVector &hDof = h[m_links[i].m_dofOffset + dof]; // Y_minus_hT_a[dof] = Y[m_links[i].m_dofOffset + dof] - spatAcc[i+1].dot(hDof); } @@ -1159,12 +1168,12 @@ void btMultiBody::computeAccelerationsArticulatedBodyAlgorithmMultiDof(btScalar } // transform base accelerations back to the world frame. - btVector3 omegadot_out = rot_from_parent[0].transpose() * spatAcc[0].getAngular(); + const btVector3 omegadot_out = rot_from_parent[0].transpose() * spatAcc[0].getAngular(); output[0] = omegadot_out[0]; output[1] = omegadot_out[1]; output[2] = omegadot_out[2]; - btVector3 vdot_out = rot_from_parent[0].transpose() * (spatAcc[0].getLinear() + spatVel[0].getAngular().cross(spatVel[0].getLinear())); + const btVector3 vdot_out = rot_from_parent[0].transpose() * (spatAcc[0].getLinear() + spatVel[0].getAngular().cross(spatVel[0].getLinear())); output[3] = vdot_out[0]; output[4] = vdot_out[1]; output[5] = vdot_out[2]; @@ -1266,12 +1275,29 @@ void btMultiBody::solveImatrix(const btVector3& rhs_top, const btVector3& rhs_bo if (num_links == 0) { // in the case of 0 m_links (i.e. a plain rigid body, not a multibody) rhs * invI is easier - result[0] = rhs_bot[0] / m_baseInertia[0]; - result[1] = rhs_bot[1] / m_baseInertia[1]; + + if ((m_baseInertia[0] >= SIMD_EPSILON) && (m_baseInertia[1] >= SIMD_EPSILON) && (m_baseInertia[2] >= SIMD_EPSILON)) + { + result[0] = rhs_bot[0] / m_baseInertia[0]; + result[1] = rhs_bot[1] / m_baseInertia[1]; result[2] = rhs_bot[2] / m_baseInertia[2]; - result[3] = rhs_top[0] / m_baseMass; - result[4] = rhs_top[1] / m_baseMass; - result[5] = rhs_top[2] / m_baseMass; + } else + { + result[0] = 0; + result[1] = 0; + result[2] = 0; + } + if (m_baseMass>=SIMD_EPSILON) + { + result[3] = rhs_top[0] / m_baseMass; + result[4] = rhs_top[1] / m_baseMass; + result[5] = rhs_top[2] / m_baseMass; + } else + { + result[3] = 0; + result[4] = 0; + result[5] = 0; + } } else { if (!m_cachedInertiaValid) @@ -1322,9 +1348,21 @@ void btMultiBody::solveImatrix(const btSpatialForceVector &rhs, btSpatialMotionV if (num_links == 0) { // in the case of 0 m_links (i.e. a plain rigid body, not a multibody) rhs * invI is easier - result.setAngular(rhs.getAngular() / m_baseInertia); - result.setLinear(rhs.getLinear() / m_baseMass); - } else + if ((m_baseInertia[0] >= SIMD_EPSILON) && (m_baseInertia[1] >= SIMD_EPSILON) && (m_baseInertia[2] >= SIMD_EPSILON)) + { + result.setAngular(rhs.getAngular() / m_baseInertia); + } else + { + result.setAngular(btVector3(0,0,0)); + } + if (m_baseMass>=SIMD_EPSILON) + { + result.setLinear(rhs.getLinear() / m_baseMass); + } else + { + result.setLinear(btVector3(0,0,0)); + } + } else { /// Special routine for calculating the inverse of a spatial inertia matrix ///the 6x6 matrix is stored as 4 blocks of 3x3 matrices @@ -1808,6 +1846,7 @@ void btMultiBody::fillConstraintJacobianMultiDof(int link, void btMultiBody::wakeUp() { + m_sleepTimer = 0; m_awake = true; } @@ -1956,7 +1995,11 @@ int btMultiBody::calculateSerializeBufferSize() const const char* btMultiBody::serialize(void* dataBuffer, class btSerializer* serializer) const { btMultiBodyData* mbd = (btMultiBodyData*) dataBuffer; - getBaseWorldTransform().serialize(mbd->m_baseWorldTransform); + getBasePos().serialize(mbd->m_baseWorldPosition); + getWorldToBaseRot().inverse().serialize(mbd->m_baseWorldOrientation); + getBaseVel().serialize(mbd->m_baseLinearVelocity); + getBaseOmega().serialize(mbd->m_baseAngularVelocity); + mbd->m_baseMass = this->getBaseMass(); getBaseInertia().serialize(mbd->m_baseInertia); { @@ -1982,6 +2025,12 @@ const char* btMultiBody::serialize(void* dataBuffer, class btSerializer* seriali memPtr->m_posVarCount = getLink(i).m_posVarCount; getLink(i).m_inertiaLocal.serialize(memPtr->m_linkInertia); + + getLink(i).m_absFrameTotVelocity.m_topVec.serialize(memPtr->m_absFrameTotVelocityTop); + getLink(i).m_absFrameTotVelocity.m_bottomVec.serialize(memPtr->m_absFrameTotVelocityBottom); + getLink(i).m_absFrameLocVelocity.m_topVec.serialize(memPtr->m_absFrameLocVelocityTop); + getLink(i).m_absFrameLocVelocity.m_bottomVec.serialize(memPtr->m_absFrameLocVelocityBottom); + memPtr->m_linkMass = getLink(i).m_mass; memPtr->m_parentIndex = getLink(i).m_parent; memPtr->m_jointDamping = getLink(i).m_jointDamping; @@ -1991,7 +2040,7 @@ const char* btMultiBody::serialize(void* dataBuffer, class btSerializer* seriali memPtr->m_jointMaxForce = getLink(i).m_jointMaxForce; memPtr->m_jointMaxVelocity = getLink(i).m_jointMaxVelocity; - getLink(i).m_eVector.serialize(memPtr->m_parentComToThisComOffset); + getLink(i).m_eVector.serialize(memPtr->m_parentComToThisPivotOffset); getLink(i).m_dVector.serialize(memPtr->m_thisPivotToThisComOffset); getLink(i).m_zeroRotParentToThis.serialize(memPtr->m_zeroRotParentToThis); btAssert(memPtr->m_dofCount<=3); diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBody.h b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBody.h index 655165ac18..5cd00e5173 100644 --- a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBody.h +++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBody.h @@ -702,15 +702,18 @@ private: int m_companionId; btScalar m_linearDamping; btScalar m_angularDamping; - bool m_useGyroTerm; + bool m_useGyroTerm; btScalar m_maxAppliedImpulse; btScalar m_maxCoordinateVelocity; bool m_hasSelfCollision; - bool __posUpdated; - int m_dofCount, m_posVarCnt; + bool __posUpdated; + int m_dofCount, m_posVarCnt; + bool m_useRK4, m_useGlobalVelocities; - + //for global velocities, see 8.3.2B Proposed resolution in Jakub Stepien PhD Thesis + //https://drive.google.com/file/d/0Bz3vEa19XOYGNWdZWGpMdUdqVmZ5ZVBOaEh4ZnpNaUxxZFNV/view?usp=sharing + ///the m_needsJointFeedback gets updated/computed during the stepVelocitiesMultiDof and it for internal usage only bool m_internalNeedsJointFeedback; }; @@ -718,12 +721,17 @@ private: struct btMultiBodyLinkDoubleData { btQuaternionDoubleData m_zeroRotParentToThis; - btVector3DoubleData m_parentComToThisComOffset; + btVector3DoubleData m_parentComToThisPivotOffset; btVector3DoubleData m_thisPivotToThisComOffset; btVector3DoubleData m_jointAxisTop[6]; btVector3DoubleData m_jointAxisBottom[6]; btVector3DoubleData m_linkInertia; // inertia of the base (in local frame; diagonal) + btVector3DoubleData m_absFrameTotVelocityTop; + btVector3DoubleData m_absFrameTotVelocityBottom; + btVector3DoubleData m_absFrameLocVelocityTop; + btVector3DoubleData m_absFrameLocVelocityBottom; + double m_linkMass; int m_parentIndex; int m_jointType; @@ -751,11 +759,16 @@ struct btMultiBodyLinkDoubleData struct btMultiBodyLinkFloatData { btQuaternionFloatData m_zeroRotParentToThis; - btVector3FloatData m_parentComToThisComOffset; + btVector3FloatData m_parentComToThisPivotOffset; btVector3FloatData m_thisPivotToThisComOffset; btVector3FloatData m_jointAxisTop[6]; btVector3FloatData m_jointAxisBottom[6]; - btVector3FloatData m_linkInertia; // inertia of the base (in local frame; diagonal) + btVector3FloatData m_linkInertia; // inertia of the base (in local frame; diagonal) + btVector3FloatData m_absFrameTotVelocityTop; + btVector3FloatData m_absFrameTotVelocityBottom; + btVector3FloatData m_absFrameLocVelocityTop; + btVector3FloatData m_absFrameLocVelocityBottom; + int m_dofCount; float m_linkMass; int m_parentIndex; @@ -784,29 +797,38 @@ struct btMultiBodyLinkFloatData ///do not change those serialization structures, it requires an updated sBulletDNAstr/sBulletDNAstr64 struct btMultiBodyDoubleData { - btTransformDoubleData m_baseWorldTransform; + btVector3DoubleData m_baseWorldPosition; + btQuaternionDoubleData m_baseWorldOrientation; + btVector3DoubleData m_baseLinearVelocity; + btVector3DoubleData m_baseAngularVelocity; btVector3DoubleData m_baseInertia; // inertia of the base (in local frame; diagonal) double m_baseMass; + int m_numLinks; + char m_padding[4]; char *m_baseName; btMultiBodyLinkDoubleData *m_links; btCollisionObjectDoubleData *m_baseCollider; - char *m_paddingPtr; - int m_numLinks; - char m_padding[4]; + + }; ///do not change those serialization structures, it requires an updated sBulletDNAstr/sBulletDNAstr64 struct btMultiBodyFloatData { - char *m_baseName; - btMultiBodyLinkFloatData *m_links; - btCollisionObjectFloatData *m_baseCollider; - btTransformFloatData m_baseWorldTransform; + btVector3FloatData m_baseWorldPosition; + btQuaternionFloatData m_baseWorldOrientation; + btVector3FloatData m_baseLinearVelocity; + btVector3FloatData m_baseAngularVelocity; + btVector3FloatData m_baseInertia; // inertia of the base (in local frame; diagonal) - float m_baseMass; int m_numLinks; + + char *m_baseName; + btMultiBodyLinkFloatData *m_links; + btCollisionObjectFloatData *m_baseCollider; + }; diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraint.cpp b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraint.cpp index d52852dd8e..9f61874b83 100644 --- a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraint.cpp +++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraint.cpp @@ -253,7 +253,7 @@ btScalar btMultiBodyConstraint::fillMultiBodyConstraint( btMultiBodySolverConstr { vec = ( solverConstraint.m_angularComponentA).cross(rel_pos1); if (angConstraint) { - denom0 = rb0->getInvMass() + constraintNormalAng.dot(vec); + denom0 = constraintNormalAng.dot(solverConstraint.m_angularComponentA); } else { denom0 = rb0->getInvMass() + constraintNormalLin.dot(vec); @@ -277,7 +277,7 @@ btScalar btMultiBodyConstraint::fillMultiBodyConstraint( btMultiBodySolverConstr { vec = ( -solverConstraint.m_angularComponentB).cross(rel_pos2); if (angConstraint) { - denom1 = rb1->getInvMass() + constraintNormalAng.dot(vec); + denom1 = constraintNormalAng.dot(-solverConstraint.m_angularComponentB); } else { denom1 = rb1->getInvMass() + constraintNormalLin.dot(vec); @@ -315,7 +315,8 @@ btScalar btMultiBodyConstraint::fillMultiBodyConstraint( btMultiBodySolverConstr } else if(rb0) { - rel_vel += rb0->getVelocityInLocalPoint(rel_pos1).dot(solverConstraint.m_contactNormal1); + rel_vel += rb0->getLinearVelocity().dot(solverConstraint.m_contactNormal1); + rel_vel += rb0->getAngularVelocity().dot(solverConstraint.m_relpos1CrossNormal); } if (multiBodyB) { @@ -327,7 +328,8 @@ btScalar btMultiBodyConstraint::fillMultiBodyConstraint( btMultiBodySolverConstr } else if(rb1) { - rel_vel += rb1->getVelocityInLocalPoint(rel_pos2).dot(solverConstraint.m_contactNormal2); + rel_vel += rb1->getLinearVelocity().dot(solverConstraint.m_contactNormal2); + rel_vel += rb1->getAngularVelocity().dot(solverConstraint.m_relpos2CrossNormal); } solverConstraint.m_friction = 0.f;//cp.m_combinedFriction; diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraint.h b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraint.h index 83521b9501..a2ae571273 100644 --- a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraint.h +++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraint.h @@ -119,6 +119,14 @@ public: return m_bodyB; } + int getLinkA() const + { + return m_linkA; + } + int getLinkB() const + { + return m_linkB; + } void internalSetAppliedImpulse(int dof, btScalar appliedImpulse) { btAssert(dof>=0); diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraintSolver.cpp b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraintSolver.cpp index 1e2d074096..cd84826e1a 100644 --- a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraintSolver.cpp +++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraintSolver.cpp @@ -39,7 +39,7 @@ btScalar btMultiBodyConstraintSolver::solveSingleIteration(int iteration, btColl btMultiBodySolverConstraint& constraint = m_multiBodyNonContactConstraints[index]; btScalar residual = resolveSingleConstraintRowGeneric(constraint); - leastSquaredResidual += residual*residual; + leastSquaredResidual = btMax(leastSquaredResidual,residual*residual); if(constraint.m_multiBodyA) constraint.m_multiBodyA->setPosUpdated(false); @@ -60,36 +60,101 @@ btScalar btMultiBodyConstraintSolver::solveSingleIteration(int iteration, btColl residual = resolveSingleConstraintRowGeneric(constraint); } - leastSquaredResidual += residual*residual; + leastSquaredResidual = btMax(leastSquaredResidual,residual*residual); if(constraint.m_multiBodyA) constraint.m_multiBodyA->setPosUpdated(false); if(constraint.m_multiBodyB) constraint.m_multiBodyB->setPosUpdated(false); } - - //solve featherstone frictional contact - for (int j1=0;j1<this->m_multiBodyFrictionContactConstraints.size();j1++) + //solve featherstone frictional contact + if (infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS && ((infoGlobal.m_solverMode&SOLVER_DISABLE_IMPLICIT_CONE_FRICTION) == 0)) { - if (iteration < infoGlobal.m_numIterations) + for (int j1 = 0; j1<this->m_multiBodyTorsionalFrictionContactConstraints.size(); j1++) + { + if (iteration < infoGlobal.m_numIterations) + { + int index = j1;//iteration&1? j1 : m_multiBodyTorsionalFrictionContactConstraints.size()-1-j1; + + btMultiBodySolverConstraint& frictionConstraint = m_multiBodyTorsionalFrictionContactConstraints[index]; + btScalar totalImpulse = m_multiBodyNormalContactConstraints[frictionConstraint.m_frictionIndex].m_appliedImpulse; + //adjust friction limits here + if (totalImpulse>btScalar(0)) + { + frictionConstraint.m_lowerLimit = -(frictionConstraint.m_friction*totalImpulse); + frictionConstraint.m_upperLimit = frictionConstraint.m_friction*totalImpulse; + btScalar residual = resolveSingleConstraintRowGeneric(frictionConstraint); + leastSquaredResidual = btMax(leastSquaredResidual , residual*residual); + + if (frictionConstraint.m_multiBodyA) + frictionConstraint.m_multiBodyA->setPosUpdated(false); + if (frictionConstraint.m_multiBodyB) + frictionConstraint.m_multiBodyB->setPosUpdated(false); + } + } + } + + for (int j1 = 0; j1 < this->m_multiBodyFrictionContactConstraints.size(); j1++) { - int index = j1;//iteration&1? j1 : m_multiBodyFrictionContactConstraints.size()-1-j1; + if (iteration < infoGlobal.m_numIterations) + { + int index = j1;//iteration&1? j1 : m_multiBodyFrictionContactConstraints.size()-1-j1; + btMultiBodySolverConstraint& frictionConstraint = m_multiBodyFrictionContactConstraints[index]; + + btScalar totalImpulse = m_multiBodyNormalContactConstraints[frictionConstraint.m_frictionIndex].m_appliedImpulse; + j1++; + int index2 = j1;//iteration&1? j1 : m_multiBodyFrictionContactConstraints.size()-1-j1; + btMultiBodySolverConstraint& frictionConstraintB = m_multiBodyFrictionContactConstraints[index2]; + btAssert(frictionConstraint.m_frictionIndex == frictionConstraintB.m_frictionIndex); + + if (frictionConstraint.m_frictionIndex == frictionConstraintB.m_frictionIndex) + { + frictionConstraint.m_lowerLimit = -(frictionConstraint.m_friction*totalImpulse); + frictionConstraint.m_upperLimit = frictionConstraint.m_friction*totalImpulse; + frictionConstraintB.m_lowerLimit = -(frictionConstraintB.m_friction*totalImpulse); + frictionConstraintB.m_upperLimit = frictionConstraintB.m_friction*totalImpulse; + btScalar residual = resolveConeFrictionConstraintRows(frictionConstraint, frictionConstraintB); + leastSquaredResidual = btMax(leastSquaredResidual, residual*residual); + + if (frictionConstraintB.m_multiBodyA) + frictionConstraintB.m_multiBodyA->setPosUpdated(false); + if (frictionConstraintB.m_multiBodyB) + frictionConstraintB.m_multiBodyB->setPosUpdated(false); + + if (frictionConstraint.m_multiBodyA) + frictionConstraint.m_multiBodyA->setPosUpdated(false); + if (frictionConstraint.m_multiBodyB) + frictionConstraint.m_multiBodyB->setPosUpdated(false); + } + } + } - btMultiBodySolverConstraint& frictionConstraint = m_multiBodyFrictionContactConstraints[index]; - btScalar totalImpulse = m_multiBodyNormalContactConstraints[frictionConstraint.m_frictionIndex].m_appliedImpulse; - //adjust friction limits here - if (totalImpulse>btScalar(0)) + + } + else + { + for (int j1 = 0; j1<this->m_multiBodyFrictionContactConstraints.size(); j1++) + { + if (iteration < infoGlobal.m_numIterations) { - frictionConstraint.m_lowerLimit = -(frictionConstraint.m_friction*totalImpulse); - frictionConstraint.m_upperLimit = frictionConstraint.m_friction*totalImpulse; - btScalar residual = resolveSingleConstraintRowGeneric(frictionConstraint); - leastSquaredResidual += residual*residual; - - if(frictionConstraint.m_multiBodyA) - frictionConstraint.m_multiBodyA->setPosUpdated(false); - if(frictionConstraint.m_multiBodyB) - frictionConstraint.m_multiBodyB->setPosUpdated(false); + int index = j1;//iteration&1? j1 : m_multiBodyFrictionContactConstraints.size()-1-j1; + + btMultiBodySolverConstraint& frictionConstraint = m_multiBodyFrictionContactConstraints[index]; + btScalar totalImpulse = m_multiBodyNormalContactConstraints[frictionConstraint.m_frictionIndex].m_appliedImpulse; + //adjust friction limits here + if (totalImpulse>btScalar(0)) + { + frictionConstraint.m_lowerLimit = -(frictionConstraint.m_friction*totalImpulse); + frictionConstraint.m_upperLimit = frictionConstraint.m_friction*totalImpulse; + btScalar residual = resolveSingleConstraintRowGeneric(frictionConstraint); + leastSquaredResidual = btMax(leastSquaredResidual, residual*residual); + + if (frictionConstraint.m_multiBodyA) + frictionConstraint.m_multiBodyA->setPosUpdated(false); + if (frictionConstraint.m_multiBodyB) + frictionConstraint.m_multiBodyB->setPosUpdated(false); + } } } } @@ -101,6 +166,8 @@ btScalar btMultiBodyConstraintSolver::solveGroupCacheFriendlySetup(btCollisionOb m_multiBodyNonContactConstraints.resize(0); m_multiBodyNormalContactConstraints.resize(0); m_multiBodyFrictionContactConstraints.resize(0); + m_multiBodyTorsionalFrictionContactConstraints.resize(0); + m_data.m_jacobians.resize(0); m_data.m_deltaVelocitiesUnitImpulse.resize(0); m_data.m_deltaVelocities.resize(0); @@ -128,82 +195,267 @@ void btMultiBodyConstraintSolver::applyDeltaVee(btScalar* delta_vee, btScalar im btScalar btMultiBodyConstraintSolver::resolveSingleConstraintRowGeneric(const btMultiBodySolverConstraint& c) { - btScalar deltaImpulse = c.m_rhs-btScalar(c.m_appliedImpulse)*c.m_cfm; - btScalar deltaVelADotn=0; - btScalar deltaVelBDotn=0; + btScalar deltaImpulse = c.m_rhs - btScalar(c.m_appliedImpulse)*c.m_cfm; + btScalar deltaVelADotn = 0; + btScalar deltaVelBDotn = 0; btSolverBody* bodyA = 0; btSolverBody* bodyB = 0; - int ndofA=0; - int ndofB=0; + int ndofA = 0; + int ndofB = 0; if (c.m_multiBodyA) { - ndofA = c.m_multiBodyA->getNumDofs() + 6; - for (int i = 0; i < ndofA; ++i) - deltaVelADotn += m_data.m_jacobians[c.m_jacAindex+i] * m_data.m_deltaVelocities[c.m_deltaVelAindex+i]; - } else if(c.m_solverBodyIdA >= 0) + ndofA = c.m_multiBodyA->getNumDofs() + 6; + for (int i = 0; i < ndofA; ++i) + deltaVelADotn += m_data.m_jacobians[c.m_jacAindex + i] * m_data.m_deltaVelocities[c.m_deltaVelAindex + i]; + } + else if (c.m_solverBodyIdA >= 0) { bodyA = &m_tmpSolverBodyPool[c.m_solverBodyIdA]; - deltaVelADotn += c.m_contactNormal1.dot(bodyA->internalGetDeltaLinearVelocity()) + c.m_relpos1CrossNormal.dot(bodyA->internalGetDeltaAngularVelocity()); + deltaVelADotn += c.m_contactNormal1.dot(bodyA->internalGetDeltaLinearVelocity()) + c.m_relpos1CrossNormal.dot(bodyA->internalGetDeltaAngularVelocity()); } if (c.m_multiBodyB) { - ndofB = c.m_multiBodyB->getNumDofs() + 6; - for (int i = 0; i < ndofB; ++i) - deltaVelBDotn += m_data.m_jacobians[c.m_jacBindex+i] * m_data.m_deltaVelocities[c.m_deltaVelBindex+i]; - } else if(c.m_solverBodyIdB >= 0) + ndofB = c.m_multiBodyB->getNumDofs() + 6; + for (int i = 0; i < ndofB; ++i) + deltaVelBDotn += m_data.m_jacobians[c.m_jacBindex + i] * m_data.m_deltaVelocities[c.m_deltaVelBindex + i]; + } + else if (c.m_solverBodyIdB >= 0) { bodyB = &m_tmpSolverBodyPool[c.m_solverBodyIdB]; - deltaVelBDotn += c.m_contactNormal2.dot(bodyB->internalGetDeltaLinearVelocity()) + c.m_relpos2CrossNormal.dot(bodyB->internalGetDeltaAngularVelocity()); + deltaVelBDotn += c.m_contactNormal2.dot(bodyB->internalGetDeltaLinearVelocity()) + c.m_relpos2CrossNormal.dot(bodyB->internalGetDeltaAngularVelocity()); } - - deltaImpulse -= deltaVelADotn*c.m_jacDiagABInv;//m_jacDiagABInv = 1./denom - deltaImpulse -= deltaVelBDotn*c.m_jacDiagABInv; + + deltaImpulse -= deltaVelADotn*c.m_jacDiagABInv;//m_jacDiagABInv = 1./denom + deltaImpulse -= deltaVelBDotn*c.m_jacDiagABInv; const btScalar sum = btScalar(c.m_appliedImpulse) + deltaImpulse; - + if (sum < c.m_lowerLimit) { - deltaImpulse = c.m_lowerLimit-c.m_appliedImpulse; + deltaImpulse = c.m_lowerLimit - c.m_appliedImpulse; c.m_appliedImpulse = c.m_lowerLimit; } - else if (sum > c.m_upperLimit) + else if (sum > c.m_upperLimit) { - deltaImpulse = c.m_upperLimit-c.m_appliedImpulse; + deltaImpulse = c.m_upperLimit - c.m_appliedImpulse; c.m_appliedImpulse = c.m_upperLimit; } else { c.m_appliedImpulse = sum; } - + if (c.m_multiBodyA) { - applyDeltaVee(&m_data.m_deltaVelocitiesUnitImpulse[c.m_jacAindex],deltaImpulse,c.m_deltaVelAindex,ndofA); + applyDeltaVee(&m_data.m_deltaVelocitiesUnitImpulse[c.m_jacAindex], deltaImpulse, c.m_deltaVelAindex, ndofA); #ifdef DIRECTLY_UPDATE_VELOCITY_DURING_SOLVER_ITERATIONS //note: update of the actual velocities (below) in the multibody does not have to happen now since m_deltaVelocities can be applied after all iterations //it would make the multibody solver more like the regular one with m_deltaVelocities being equivalent to btSolverBody::m_deltaLinearVelocity/m_deltaAngularVelocity - c.m_multiBodyA->applyDeltaVeeMultiDof2(&m_data.m_deltaVelocitiesUnitImpulse[c.m_jacAindex],deltaImpulse); + c.m_multiBodyA->applyDeltaVeeMultiDof2(&m_data.m_deltaVelocitiesUnitImpulse[c.m_jacAindex], deltaImpulse); #endif //DIRECTLY_UPDATE_VELOCITY_DURING_SOLVER_ITERATIONS - } else if(c.m_solverBodyIdA >= 0) + } + else if (c.m_solverBodyIdA >= 0) { - bodyA->internalApplyImpulse(c.m_contactNormal1*bodyA->internalGetInvMass(),c.m_angularComponentA,deltaImpulse); + bodyA->internalApplyImpulse(c.m_contactNormal1*bodyA->internalGetInvMass(), c.m_angularComponentA, deltaImpulse); } if (c.m_multiBodyB) { - applyDeltaVee(&m_data.m_deltaVelocitiesUnitImpulse[c.m_jacBindex],deltaImpulse,c.m_deltaVelBindex,ndofB); + applyDeltaVee(&m_data.m_deltaVelocitiesUnitImpulse[c.m_jacBindex], deltaImpulse, c.m_deltaVelBindex, ndofB); #ifdef DIRECTLY_UPDATE_VELOCITY_DURING_SOLVER_ITERATIONS //note: update of the actual velocities (below) in the multibody does not have to happen now since m_deltaVelocities can be applied after all iterations //it would make the multibody solver more like the regular one with m_deltaVelocities being equivalent to btSolverBody::m_deltaLinearVelocity/m_deltaAngularVelocity - c.m_multiBodyB->applyDeltaVeeMultiDof2(&m_data.m_deltaVelocitiesUnitImpulse[c.m_jacBindex],deltaImpulse); + c.m_multiBodyB->applyDeltaVeeMultiDof2(&m_data.m_deltaVelocitiesUnitImpulse[c.m_jacBindex], deltaImpulse); #endif //DIRECTLY_UPDATE_VELOCITY_DURING_SOLVER_ITERATIONS - } else if(c.m_solverBodyIdB >= 0) + } + else if (c.m_solverBodyIdB >= 0) { - bodyB->internalApplyImpulse(c.m_contactNormal2*bodyB->internalGetInvMass(),c.m_angularComponentB,deltaImpulse); + bodyB->internalApplyImpulse(c.m_contactNormal2*bodyB->internalGetInvMass(), c.m_angularComponentB, deltaImpulse); } - return deltaImpulse; + btScalar deltaVel =deltaImpulse/c.m_jacDiagABInv; + return deltaVel; +} + + +btScalar btMultiBodyConstraintSolver::resolveConeFrictionConstraintRows(const btMultiBodySolverConstraint& cA1,const btMultiBodySolverConstraint& cB) +{ + int ndofA=0; + int ndofB=0; + btSolverBody* bodyA = 0; + btSolverBody* bodyB = 0; + btScalar deltaImpulseB = 0.f; + btScalar sumB = 0.f; + { + deltaImpulseB = cB.m_rhs-btScalar(cB.m_appliedImpulse)*cB.m_cfm; + btScalar deltaVelADotn=0; + btScalar deltaVelBDotn=0; + if (cB.m_multiBodyA) + { + ndofA = cB.m_multiBodyA->getNumDofs() + 6; + for (int i = 0; i < ndofA; ++i) + deltaVelADotn += m_data.m_jacobians[cB.m_jacAindex+i] * m_data.m_deltaVelocities[cB.m_deltaVelAindex+i]; + } else if(cB.m_solverBodyIdA >= 0) + { + bodyA = &m_tmpSolverBodyPool[cB.m_solverBodyIdA]; + deltaVelADotn += cB.m_contactNormal1.dot(bodyA->internalGetDeltaLinearVelocity()) + cB.m_relpos1CrossNormal.dot(bodyA->internalGetDeltaAngularVelocity()); + } + + if (cB.m_multiBodyB) + { + ndofB = cB.m_multiBodyB->getNumDofs() + 6; + for (int i = 0; i < ndofB; ++i) + deltaVelBDotn += m_data.m_jacobians[cB.m_jacBindex+i] * m_data.m_deltaVelocities[cB.m_deltaVelBindex+i]; + } else if(cB.m_solverBodyIdB >= 0) + { + bodyB = &m_tmpSolverBodyPool[cB.m_solverBodyIdB]; + deltaVelBDotn += cB.m_contactNormal2.dot(bodyB->internalGetDeltaLinearVelocity()) + cB.m_relpos2CrossNormal.dot(bodyB->internalGetDeltaAngularVelocity()); + } + + + deltaImpulseB -= deltaVelADotn*cB.m_jacDiagABInv;//m_jacDiagABInv = 1./denom + deltaImpulseB -= deltaVelBDotn*cB.m_jacDiagABInv; + sumB = btScalar(cB.m_appliedImpulse) + deltaImpulseB; + } + + btScalar deltaImpulseA = 0.f; + btScalar sumA = 0.f; + const btMultiBodySolverConstraint& cA = cA1; + { + { + deltaImpulseA = cA.m_rhs-btScalar(cA.m_appliedImpulse)*cA.m_cfm; + btScalar deltaVelADotn=0; + btScalar deltaVelBDotn=0; + if (cA.m_multiBodyA) + { + ndofA = cA.m_multiBodyA->getNumDofs() + 6; + for (int i = 0; i < ndofA; ++i) + deltaVelADotn += m_data.m_jacobians[cA.m_jacAindex+i] * m_data.m_deltaVelocities[cA.m_deltaVelAindex+i]; + } else if(cA.m_solverBodyIdA >= 0) + { + bodyA = &m_tmpSolverBodyPool[cA.m_solverBodyIdA]; + deltaVelADotn += cA.m_contactNormal1.dot(bodyA->internalGetDeltaLinearVelocity()) + cA.m_relpos1CrossNormal.dot(bodyA->internalGetDeltaAngularVelocity()); + } + + if (cA.m_multiBodyB) + { + ndofB = cA.m_multiBodyB->getNumDofs() + 6; + for (int i = 0; i < ndofB; ++i) + deltaVelBDotn += m_data.m_jacobians[cA.m_jacBindex+i] * m_data.m_deltaVelocities[cA.m_deltaVelBindex+i]; + } else if(cA.m_solverBodyIdB >= 0) + { + bodyB = &m_tmpSolverBodyPool[cA.m_solverBodyIdB]; + deltaVelBDotn += cA.m_contactNormal2.dot(bodyB->internalGetDeltaLinearVelocity()) + cA.m_relpos2CrossNormal.dot(bodyB->internalGetDeltaAngularVelocity()); + } + + + deltaImpulseA -= deltaVelADotn*cA.m_jacDiagABInv;//m_jacDiagABInv = 1./denom + deltaImpulseA -= deltaVelBDotn*cA.m_jacDiagABInv; + sumA = btScalar(cA.m_appliedImpulse) + deltaImpulseA; + } + } + + if (sumA*sumA+sumB*sumB>=cA.m_lowerLimit*cB.m_lowerLimit) + { + btScalar angle = btAtan2(sumA,sumB); + btScalar sumAclipped = btFabs(cA.m_lowerLimit*btSin(angle)); + btScalar sumBclipped = btFabs(cB.m_lowerLimit*btCos(angle)); + + + if (sumA < -sumAclipped) + { + deltaImpulseA = -sumAclipped - cA.m_appliedImpulse; + cA.m_appliedImpulse = -sumAclipped; + } + else if (sumA > sumAclipped) + { + deltaImpulseA = sumAclipped - cA.m_appliedImpulse; + cA.m_appliedImpulse = sumAclipped; + } + else + { + cA.m_appliedImpulse = sumA; + } + + if (sumB < -sumBclipped) + { + deltaImpulseB = -sumBclipped - cB.m_appliedImpulse; + cB.m_appliedImpulse = -sumBclipped; + } + else if (sumB > sumBclipped) + { + deltaImpulseB = sumBclipped - cB.m_appliedImpulse; + cB.m_appliedImpulse = sumBclipped; + } + else + { + cB.m_appliedImpulse = sumB; + } + //deltaImpulseA = sumAclipped-cA.m_appliedImpulse; + //cA.m_appliedImpulse = sumAclipped; + //deltaImpulseB = sumBclipped-cB.m_appliedImpulse; + //cB.m_appliedImpulse = sumBclipped; + } + else + { + cA.m_appliedImpulse = sumA; + cB.m_appliedImpulse = sumB; + } + + if (cA.m_multiBodyA) + { + applyDeltaVee(&m_data.m_deltaVelocitiesUnitImpulse[cA.m_jacAindex],deltaImpulseA,cA.m_deltaVelAindex,ndofA); +#ifdef DIRECTLY_UPDATE_VELOCITY_DURING_SOLVER_ITERATIONS + //note: update of the actual velocities (below) in the multibody does not have to happen now since m_deltaVelocities can be applied after all iterations + //it would make the multibody solver more like the regular one with m_deltaVelocities being equivalent to btSolverBody::m_deltaLinearVelocity/m_deltaAngularVelocity + cA.m_multiBodyA->applyDeltaVeeMultiDof2(&m_data.m_deltaVelocitiesUnitImpulse[cA.m_jacAindex],deltaImpulseA); +#endif //DIRECTLY_UPDATE_VELOCITY_DURING_SOLVER_ITERATIONS + } else if(cA.m_solverBodyIdA >= 0) + { + bodyA->internalApplyImpulse(cA.m_contactNormal1*bodyA->internalGetInvMass(),cA.m_angularComponentA,deltaImpulseA); + + } + if (cA.m_multiBodyB) + { + applyDeltaVee(&m_data.m_deltaVelocitiesUnitImpulse[cA.m_jacBindex],deltaImpulseA,cA.m_deltaVelBindex,ndofB); +#ifdef DIRECTLY_UPDATE_VELOCITY_DURING_SOLVER_ITERATIONS + //note: update of the actual velocities (below) in the multibody does not have to happen now since m_deltaVelocities can be applied after all iterations + //it would make the multibody solver more like the regular one with m_deltaVelocities being equivalent to btSolverBody::m_deltaLinearVelocity/m_deltaAngularVelocity + cA.m_multiBodyB->applyDeltaVeeMultiDof2(&m_data.m_deltaVelocitiesUnitImpulse[cA.m_jacBindex],deltaImpulseA); +#endif //DIRECTLY_UPDATE_VELOCITY_DURING_SOLVER_ITERATIONS + } else if(cA.m_solverBodyIdB >= 0) + { + bodyB->internalApplyImpulse(cA.m_contactNormal2*bodyB->internalGetInvMass(),cA.m_angularComponentB,deltaImpulseA); + } + + if (cB.m_multiBodyA) + { + applyDeltaVee(&m_data.m_deltaVelocitiesUnitImpulse[cB.m_jacAindex],deltaImpulseB,cB.m_deltaVelAindex,ndofA); +#ifdef DIRECTLY_UPDATE_VELOCITY_DURING_SOLVER_ITERATIONS + //note: update of the actual velocities (below) in the multibody does not have to happen now since m_deltaVelocities can be applied after all iterations + //it would make the multibody solver more like the regular one with m_deltaVelocities being equivalent to btSolverBody::m_deltaLinearVelocity/m_deltaAngularVelocity + cB.m_multiBodyA->applyDeltaVeeMultiDof2(&m_data.m_deltaVelocitiesUnitImpulse[cB.m_jacAindex],deltaImpulseB); +#endif //DIRECTLY_UPDATE_VELOCITY_DURING_SOLVER_ITERATIONS + } else if(cB.m_solverBodyIdA >= 0) + { + bodyA->internalApplyImpulse(cB.m_contactNormal1*bodyA->internalGetInvMass(),cB.m_angularComponentA,deltaImpulseB); + } + if (cB.m_multiBodyB) + { + applyDeltaVee(&m_data.m_deltaVelocitiesUnitImpulse[cB.m_jacBindex],deltaImpulseB,cB.m_deltaVelBindex,ndofB); +#ifdef DIRECTLY_UPDATE_VELOCITY_DURING_SOLVER_ITERATIONS + //note: update of the actual velocities (below) in the multibody does not have to happen now since m_deltaVelocities can be applied after all iterations + //it would make the multibody solver more like the regular one with m_deltaVelocities being equivalent to btSolverBody::m_deltaLinearVelocity/m_deltaAngularVelocity + cB.m_multiBodyB->applyDeltaVeeMultiDof2(&m_data.m_deltaVelocitiesUnitImpulse[cB.m_jacBindex],deltaImpulseB); +#endif //DIRECTLY_UPDATE_VELOCITY_DURING_SOLVER_ITERATIONS + } else if(cB.m_solverBodyIdB >= 0) + { + bodyB->internalApplyImpulse(cB.m_contactNormal2*bodyB->internalGetInvMass(),cB.m_angularComponentB,deltaImpulseB); + } + + btScalar deltaVel =deltaImpulseA/cA.m_jacDiagABInv+deltaImpulseB/cB.m_jacDiagABInv; + return deltaVel; } @@ -908,7 +1160,10 @@ btMultiBodySolverConstraint& btMultiBodyConstraintSolver::addMultiBodyTorsionalF btCollisionObject* colObj0,btCollisionObject* colObj1, btScalar relaxation, const btContactSolverInfo& infoGlobal, btScalar desiredVelocity, btScalar cfmSlip) { BT_PROFILE("addMultiBodyRollingFrictionConstraint"); - btMultiBodySolverConstraint& solverConstraint = m_multiBodyFrictionContactConstraints.expandNonInitializing(); + + bool useTorsionalAndConeFriction = (infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS && ((infoGlobal.m_solverMode&SOLVER_DISABLE_IMPLICIT_CONE_FRICTION) == 0)); + + btMultiBodySolverConstraint& solverConstraint = useTorsionalAndConeFriction? m_multiBodyTorsionalFrictionContactConstraints.expandNonInitializing() : m_multiBodyFrictionContactConstraints.expandNonInitializing(); solverConstraint.m_orgConstraint = 0; solverConstraint.m_orgDofIndex = -1; @@ -1151,6 +1406,7 @@ void btMultiBodyConstraintSolver::convertContacts(btPersistentManifold** manifol btScalar btMultiBodyConstraintSolver::solveGroup(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifold,int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& info, btIDebugDraw* debugDrawer,btDispatcher* dispatcher) { + //printf("btMultiBodyConstraintSolver::solveGroup: numBodies=%d, numConstraints=%d\n", numBodies, numConstraints); return btSequentialImpulseConstraintSolver::solveGroup(bodies,numBodies,manifold,numManifolds,constraints,numConstraints,info,debugDrawer,dispatcher); } @@ -1234,27 +1490,12 @@ void btMultiBodyConstraintSolver::writeBackSolverBodyToMultiBody(btMultiBodySolv if (c.m_multiBodyA) { - - if(c.m_multiBodyA->isMultiDof()) - { - c.m_multiBodyA->applyDeltaVeeMultiDof(&m_data.m_deltaVelocitiesUnitImpulse[c.m_jacAindex],c.m_appliedImpulse); - } - else - { - c.m_multiBodyA->applyDeltaVee(&m_data.m_deltaVelocitiesUnitImpulse[c.m_jacAindex],c.m_appliedImpulse); - } + c.m_multiBodyA->applyDeltaVeeMultiDof(&m_data.m_deltaVelocitiesUnitImpulse[c.m_jacAindex],c.m_appliedImpulse); } if (c.m_multiBodyB) { - if(c.m_multiBodyB->isMultiDof()) - { - c.m_multiBodyB->applyDeltaVeeMultiDof(&m_data.m_deltaVelocitiesUnitImpulse[c.m_jacBindex],c.m_appliedImpulse); - } - else - { - c.m_multiBodyB->applyDeltaVee(&m_data.m_deltaVelocitiesUnitImpulse[c.m_jacBindex],c.m_appliedImpulse); - } + c.m_multiBodyB->applyDeltaVeeMultiDof(&m_data.m_deltaVelocitiesUnitImpulse[c.m_jacBindex],c.m_appliedImpulse); } #endif @@ -1416,6 +1657,8 @@ btScalar btMultiBodyConstraintSolver::solveGroupCacheFriendlyFinish(btCollisionO void btMultiBodyConstraintSolver::solveMultiBodyGroup(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifold,int numManifolds,btTypedConstraint** constraints,int numConstraints,btMultiBodyConstraint** multiBodyConstraints, int numMultiBodyConstraints, const btContactSolverInfo& info, btIDebugDraw* debugDrawer,btDispatcher* dispatcher) { + //printf("solveMultiBodyGroup: numBodies=%d, numConstraints=%d, numManifolds=%d, numMultiBodyConstraints=%d\n", numBodies, numConstraints, numManifolds, numMultiBodyConstraints); + //printf("solveMultiBodyGroup start\n"); m_tmpMultiBodyConstraints = multiBodyConstraints; m_tmpNumMultiBodyConstraints = numMultiBodyConstraints; diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraintSolver.h b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraintSolver.h index 489347d874..29f484e1d8 100644 --- a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraintSolver.h +++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyConstraintSolver.h @@ -36,6 +36,7 @@ protected: btMultiBodyConstraintArray m_multiBodyNormalContactConstraints; btMultiBodyConstraintArray m_multiBodyFrictionContactConstraints; + btMultiBodyConstraintArray m_multiBodyTorsionalFrictionContactConstraints; btMultiBodyJacobianData m_data; @@ -45,6 +46,9 @@ protected: btScalar resolveSingleConstraintRowGeneric(const btMultiBodySolverConstraint& c); + //solve 2 friction directions and clamp against the implicit friction cone + btScalar resolveConeFrictionConstraintRows(const btMultiBodySolverConstraint& cA1, const btMultiBodySolverConstraint& cB); + void convertContacts(btPersistentManifold** manifoldPtr,int numManifolds, const btContactSolverInfo& infoGlobal); diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.cpp b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.cpp index 9eacc22647..9c5f3ad8a9 100644 --- a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.cpp +++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.cpp @@ -277,7 +277,11 @@ struct MultiBodyInplaceSolverIslandCallback : public btSimulationIslandManager:: m_multiBodyConstraints.resize(0); } - + void setMultiBodyConstraintSolver(btMultiBodyConstraintSolver* solver) + { + m_solver = solver; + } + virtual void processIsland(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifolds,int numManifolds, int islandId) { if (islandId<0) @@ -348,7 +352,7 @@ struct MultiBodyInplaceSolverIslandCallback : public btSimulationIslandManager:: for (i=0;i<numCurMultiBodyConstraints;i++) m_multiBodyConstraints.push_back(startMultiBodyConstraint[i]); - if ((m_constraints.size()+m_manifolds.size())>m_solverInfo->m_minimumSolverBatchSize) + if ((m_multiBodyConstraints.size()+m_constraints.size()+m_manifolds.size())>m_solverInfo->m_minimumSolverBatchSize) { processConstraints(); } else @@ -394,6 +398,22 @@ btMultiBodyDynamicsWorld::~btMultiBodyDynamicsWorld () delete m_solverMultiBodyIslandCallback; } +void btMultiBodyDynamicsWorld::setMultiBodyConstraintSolver(btMultiBodyConstraintSolver* solver) +{ + m_multiBodyConstraintSolver = solver; + m_solverMultiBodyIslandCallback->setMultiBodyConstraintSolver(solver); + btDiscreteDynamicsWorld::setConstraintSolver(solver); +} + +void btMultiBodyDynamicsWorld::setConstraintSolver(btConstraintSolver* solver) +{ + if (solver->getSolverType()==BT_MULTIBODY_SOLVER) + { + m_multiBodyConstraintSolver = (btMultiBodyConstraintSolver*)solver; + } + btDiscreteDynamicsWorld::setConstraintSolver(solver); +} + void btMultiBodyDynamicsWorld::forwardKinematics() { @@ -411,6 +431,8 @@ void btMultiBodyDynamicsWorld::solveConstraints(btContactSolverInfo& solverInfo) BT_PROFILE("solveConstraints"); + clearMultiBodyConstraintForces(); + m_sortedConstraints.resize( m_constraints.size()); int i; for (i=0;i<getNumConstraints();i++) @@ -433,8 +455,6 @@ void btMultiBodyDynamicsWorld::solveConstraints(btContactSolverInfo& solverInfo) m_solverMultiBodyIslandCallback->setup(&solverInfo,constraintsPtr,m_sortedConstraints.size(),sortedMultiBodyConstraints,m_sortedMultiBodyConstraints.size(), getDebugDrawer()); m_constraintSolver->prepareSolve(getCollisionWorld()->getNumCollisionObjects(), getCollisionWorld()->getDispatcher()->getNumManifolds()); - /// solve all the constraints for this island - m_islandManager->buildAndProcessIslands(getCollisionWorld()->getDispatcher(),getCollisionWorld(),m_solverMultiBodyIslandCallback); #ifndef BT_USE_VIRTUAL_CLEARFORCES_AND_GRAVITY { @@ -669,7 +689,9 @@ void btMultiBodyDynamicsWorld::solveConstraints(btContactSolverInfo& solverInfo) } } - clearMultiBodyConstraintForces(); + /// solve all the constraints for this island + m_islandManager->buildAndProcessIslands(getCollisionWorld()->getDispatcher(), getCollisionWorld(), m_solverMultiBodyIslandCallback); + m_solverMultiBodyIslandCallback->processConstraints(); @@ -824,21 +846,24 @@ void btMultiBodyDynamicsWorld::debugDrawWorld() { btMultiBody* bod = m_multiBodies[b]; bod->forwardKinematics(m_scratch_world_to_local1,m_scratch_local_origin1); - - getDebugDrawer()->drawTransform(bod->getBaseWorldTransform(), 0.1); - + + if (mode & btIDebugDraw::DBG_DrawFrames) + { + getDebugDrawer()->drawTransform(bod->getBaseWorldTransform(), 0.1); + } for (int m = 0; m<bod->getNumLinks(); m++) { const btTransform& tr = bod->getLink(m).m_cachedWorldTransform; - - getDebugDrawer()->drawTransform(tr, 0.1); - + if (mode & btIDebugDraw::DBG_DrawFrames) + { + getDebugDrawer()->drawTransform(tr, 0.1); + } //draw the joint axis if (bod->getLink(m).m_jointType==btMultibodyLink::eRevolute) { - btVector3 vec = quatRotate(tr.getRotation(),bod->getLink(m).m_axes[0].m_topVec); + btVector3 vec = quatRotate(tr.getRotation(),bod->getLink(m).m_axes[0].m_topVec)*0.1; btVector4 color(0,0,0,1);//1,1,1); btVector3 from = vec+tr.getOrigin()-quatRotate(tr.getRotation(),bod->getLink(m).m_dVector); @@ -847,7 +872,7 @@ void btMultiBodyDynamicsWorld::debugDrawWorld() } if (bod->getLink(m).m_jointType==btMultibodyLink::eFixed) { - btVector3 vec = quatRotate(tr.getRotation(),bod->getLink(m).m_axes[0].m_bottomVec); + btVector3 vec = quatRotate(tr.getRotation(),bod->getLink(m).m_axes[0].m_bottomVec)*0.1; btVector4 color(0,0,0,1);//1,1,1); btVector3 from = vec+tr.getOrigin()-quatRotate(tr.getRotation(),bod->getLink(m).m_dVector); @@ -856,7 +881,7 @@ void btMultiBodyDynamicsWorld::debugDrawWorld() } if (bod->getLink(m).m_jointType==btMultibodyLink::ePrismatic) { - btVector3 vec = quatRotate(tr.getRotation(),bod->getLink(m).m_axes[0].m_bottomVec); + btVector3 vec = quatRotate(tr.getRotation(),bod->getLink(m).m_axes[0].m_bottomVec)*0.1; btVector4 color(0,0,0,1);//1,1,1); btVector3 from = vec+tr.getOrigin()-quatRotate(tr.getRotation(),bod->getLink(m).m_dVector); @@ -970,6 +995,8 @@ void btMultiBodyDynamicsWorld::serialize(btSerializer* serializer) serializeCollisionObjects(serializer); + serializeContactManifolds(serializer); + serializer->finishSerialization(); } @@ -988,4 +1015,17 @@ void btMultiBodyDynamicsWorld::serializeMultiBodies(btSerializer* serializer) } } -}
\ No newline at end of file + //serialize all multibody links (collision objects) + for (i=0;i<m_collisionObjects.size();i++) + { + btCollisionObject* colObj = m_collisionObjects[i]; + if (colObj->getInternalType() == btCollisionObject::CO_FEATHERSTONE_LINK) + { + int len = colObj->calculateSerializeBufferSize(); + btChunk* chunk = serializer->allocate(len,1); + const char* structType = colObj->serialize(chunk->m_oldPtr, serializer); + serializer->finalizeChunk(chunk,structType,BT_MB_LINKCOLLIDER_CODE,colObj); + } + } + +} diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.h b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.h index c0c132bbba..2fbf089d81 100644 --- a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.h +++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.h @@ -109,6 +109,8 @@ public: virtual void applyGravity(); virtual void serialize(btSerializer* serializer); + virtual void setMultiBodyConstraintSolver(btMultiBodyConstraintSolver* solver); + virtual void setConstraintSolver(btConstraintSolver* solver); }; #endif //BT_MULTIBODY_DYNAMICS_WORLD_H diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyFixedConstraint.cpp b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyFixedConstraint.cpp index 1f94117aa9..af48e94a83 100644 --- a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyFixedConstraint.cpp +++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyFixedConstraint.cpp @@ -65,13 +65,16 @@ int btMultiBodyFixedConstraint::getIslandIdA() const if (m_bodyA) { - btMultiBodyLinkCollider* col = m_bodyA->getBaseCollider(); - if (col) - return col->getIslandTag(); - for (int i=0;i<m_bodyA->getNumLinks();i++) + if (m_linkA < 0) { - if (m_bodyA->getLink(i).m_collider) - return m_bodyA->getLink(i).m_collider->getIslandTag(); + btMultiBodyLinkCollider* col = m_bodyA->getBaseCollider(); + if (col) + return col->getIslandTag(); + } + else + { + if (m_bodyA->getLink(m_linkA).m_collider) + return m_bodyA->getLink(m_linkA).m_collider->getIslandTag(); } } return -1; @@ -83,16 +86,17 @@ int btMultiBodyFixedConstraint::getIslandIdB() const return m_rigidBodyB->getIslandTag(); if (m_bodyB) { - btMultiBodyLinkCollider* col = m_bodyB->getBaseCollider(); - if (col) - return col->getIslandTag(); - - for (int i=0;i<m_bodyB->getNumLinks();i++) + if (m_linkB < 0) { - col = m_bodyB->getLink(i).m_collider; + btMultiBodyLinkCollider* col = m_bodyB->getBaseCollider(); if (col) return col->getIslandTag(); } + else + { + if (m_bodyB->getLink(m_linkB).m_collider) + return m_bodyB->getLink(m_linkB).m_collider->getIslandTag(); + } } return -1; } diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyGearConstraint.cpp b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyGearConstraint.cpp index 5fdb7007d8..09ddd65cd8 100644 --- a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyGearConstraint.cpp +++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyGearConstraint.cpp @@ -45,16 +45,18 @@ btMultiBodyGearConstraint::~btMultiBodyGearConstraint() int btMultiBodyGearConstraint::getIslandIdA() const { - if (m_bodyA) { - btMultiBodyLinkCollider* col = m_bodyA->getBaseCollider(); - if (col) - return col->getIslandTag(); - for (int i=0;i<m_bodyA->getNumLinks();i++) + if (m_linkA < 0) + { + btMultiBodyLinkCollider* col = m_bodyA->getBaseCollider(); + if (col) + return col->getIslandTag(); + } + else { - if (m_bodyA->getLink(i).m_collider) - return m_bodyA->getLink(i).m_collider->getIslandTag(); + if (m_bodyA->getLink(m_linkA).m_collider) + return m_bodyA->getLink(m_linkA).m_collider->getIslandTag(); } } return -1; @@ -64,16 +66,17 @@ int btMultiBodyGearConstraint::getIslandIdB() const { if (m_bodyB) { - btMultiBodyLinkCollider* col = m_bodyB->getBaseCollider(); - if (col) - return col->getIslandTag(); - - for (int i=0;i<m_bodyB->getNumLinks();i++) + if (m_linkB < 0) { - col = m_bodyB->getLink(i).m_collider; + btMultiBodyLinkCollider* col = m_bodyB->getBaseCollider(); if (col) return col->getIslandTag(); } + else + { + if (m_bodyB->getLink(m_linkB).m_collider) + return m_bodyB->getLink(m_linkB).m_collider->getIslandTag(); + } } return -1; } @@ -134,6 +137,10 @@ void btMultiBodyGearConstraint::createConstraintRows(btMultiBodyConstraintArray& if (m_erp!=0) { btScalar currentPositionA = m_bodyA->getJointPosMultiDof(m_linkA)[dof]; + if (m_gearAuxLink >= 0) + { + currentPositionA -= m_bodyA->getJointPosMultiDof(m_gearAuxLink)[dof]; + } btScalar currentPositionB = m_gearRatio*m_bodyA->getJointPosMultiDof(m_linkB)[dof]; btScalar diff = currentPositionB+currentPositionA; btScalar desiredPositionDiff = this->m_relativePositionTarget; diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyJointLimitConstraint.cpp b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyJointLimitConstraint.cpp index 6d173b66a1..35c929f7ce 100644 --- a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyJointLimitConstraint.cpp +++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyJointLimitConstraint.cpp @@ -53,17 +53,22 @@ btMultiBodyJointLimitConstraint::~btMultiBodyJointLimitConstraint() { } + int btMultiBodyJointLimitConstraint::getIslandIdA() const { - if(m_bodyA) + + if (m_bodyA) { - btMultiBodyLinkCollider* col = m_bodyA->getBaseCollider(); - if (col) - return col->getIslandTag(); - for (int i=0;i<m_bodyA->getNumLinks();i++) + if (m_linkA < 0) { - if (m_bodyA->getLink(i).m_collider) - return m_bodyA->getLink(i).m_collider->getIslandTag(); + btMultiBodyLinkCollider* col = m_bodyA->getBaseCollider(); + if (col) + return col->getIslandTag(); + } + else + { + if (m_bodyA->getLink(m_linkA).m_collider) + return m_bodyA->getLink(m_linkA).m_collider->getIslandTag(); } } return -1; @@ -71,18 +76,19 @@ int btMultiBodyJointLimitConstraint::getIslandIdA() const int btMultiBodyJointLimitConstraint::getIslandIdB() const { - if(m_bodyB) + if (m_bodyB) { - btMultiBodyLinkCollider* col = m_bodyB->getBaseCollider(); - if (col) - return col->getIslandTag(); - - for (int i=0;i<m_bodyB->getNumLinks();i++) + if (m_linkB < 0) { - col = m_bodyB->getLink(i).m_collider; + btMultiBodyLinkCollider* col = m_bodyB->getBaseCollider(); if (col) return col->getIslandTag(); } + else + { + if (m_bodyB->getLink(m_linkB).m_collider) + return m_bodyB->getLink(m_linkB).m_collider->getIslandTag(); + } } return -1; } diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyJointMotor.cpp b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyJointMotor.cpp index e0921178e9..2a70ea97e5 100644 --- a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyJointMotor.cpp +++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyJointMotor.cpp @@ -74,29 +74,37 @@ btMultiBodyJointMotor::~btMultiBodyJointMotor() int btMultiBodyJointMotor::getIslandIdA() const { - btMultiBodyLinkCollider* col = m_bodyA->getBaseCollider(); - if (col) - return col->getIslandTag(); - for (int i=0;i<m_bodyA->getNumLinks();i++) + if (this->m_linkA < 0) { - if (m_bodyA->getLink(i).m_collider) - return m_bodyA->getLink(i).m_collider->getIslandTag(); + btMultiBodyLinkCollider* col = m_bodyA->getBaseCollider(); + if (col) + return col->getIslandTag(); + } + else + { + if (m_bodyA->getLink(m_linkA).m_collider) + { + return m_bodyA->getLink(m_linkA).m_collider->getIslandTag(); + } } return -1; } int btMultiBodyJointMotor::getIslandIdB() const { - btMultiBodyLinkCollider* col = m_bodyB->getBaseCollider(); - if (col) - return col->getIslandTag(); - - for (int i=0;i<m_bodyB->getNumLinks();i++) + if (m_linkB < 0) { - col = m_bodyB->getLink(i).m_collider; + btMultiBodyLinkCollider* col = m_bodyB->getBaseCollider(); if (col) return col->getIslandTag(); } + else + { + if (m_bodyB->getLink(m_linkB).m_collider) + { + return m_bodyB->getLink(m_linkB).m_collider->getIslandTag(); + } + } return -1; } diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyLink.h b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyLink.h index 01828e5843..21c9e7a557 100644 --- a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyLink.h +++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyLink.h @@ -182,6 +182,8 @@ btVector3 m_appliedConstraintForce; // In WORLD frame m_cachedRVector.setValue(0, 0, 0); m_appliedForce.setValue( 0, 0, 0); m_appliedTorque.setValue(0, 0, 0); + m_appliedConstraintForce.setValue(0,0,0); + m_appliedConstraintTorque.setValue(0,0,0); // m_jointPos[0] = m_jointPos[1] = m_jointPos[2] = m_jointPos[4] = m_jointPos[5] = m_jointPos[6] = 0.f; m_jointPos[3] = 1.f; //"quat.w" diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyLinkCollider.h b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyLinkCollider.h index 671e15d314..7092e62b5a 100644 --- a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyLinkCollider.h +++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyLinkCollider.h @@ -19,6 +19,16 @@ subject to the following restrictions: #include "BulletCollision/CollisionDispatch/btCollisionObject.h" #include "btMultiBody.h" +#include "LinearMath/btSerializer.h" + +#ifdef BT_USE_DOUBLE_PRECISION +#define btMultiBodyLinkColliderData btMultiBodyLinkColliderDoubleData +#define btMultiBodyLinkColliderDataName "btMultiBodyLinkColliderDoubleData" +#else +#define btMultiBodyLinkColliderData btMultiBodyLinkColliderFloatData +#define btMultiBodyLinkColliderDataName "btMultiBodyLinkColliderFloatData" +#endif + class btMultiBodyLinkCollider : public btCollisionObject { @@ -119,7 +129,49 @@ public: } return true; } + + virtual int calculateSerializeBufferSize() const; + + ///fills the dataBuffer and returns the struct name (and 0 on failure) + virtual const char* serialize(void* dataBuffer, class btSerializer* serializer) const; + +}; + + +struct btMultiBodyLinkColliderFloatData +{ + btCollisionObjectFloatData m_colObjData; + btMultiBodyFloatData *m_multiBody; + int m_link; + char m_padding[4]; }; +struct btMultiBodyLinkColliderDoubleData +{ + btCollisionObjectDoubleData m_colObjData; + btMultiBodyDoubleData *m_multiBody; + int m_link; + char m_padding[4]; +}; + +SIMD_FORCE_INLINE int btMultiBodyLinkCollider::calculateSerializeBufferSize() const +{ + return sizeof(btMultiBodyLinkColliderData); +} + +SIMD_FORCE_INLINE const char* btMultiBodyLinkCollider::serialize(void* dataBuffer, class btSerializer* serializer) const +{ + btMultiBodyLinkColliderData* dataOut = (btMultiBodyLinkColliderData*)dataBuffer; + btCollisionObject::serialize(&dataOut->m_colObjData,serializer); + + dataOut->m_link = this->m_link; + dataOut->m_multiBody = (btMultiBodyData*)serializer->getUniquePointer(m_multiBody); + + // Fill padding with zeros to appease msan. + memset(dataOut->m_padding, 0, sizeof(dataOut->m_padding)); + + return btMultiBodyLinkColliderDataName; +} + #endif //BT_FEATHERSTONE_LINK_COLLIDER_H diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyMLCPConstraintSolver.cpp b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyMLCPConstraintSolver.cpp new file mode 100644 index 0000000000..338e8af0ab --- /dev/null +++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyMLCPConstraintSolver.cpp @@ -0,0 +1,966 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2018 Google Inc. http://bulletphysics.org + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#include "BulletDynamics/Featherstone/btMultiBodyMLCPConstraintSolver.h" + +#include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h" +#include "BulletDynamics/Featherstone/btMultiBodyLinkCollider.h" +#include "BulletDynamics/Featherstone/btMultiBodyConstraint.h" +#include "BulletDynamics/MLCPSolvers/btMLCPSolverInterface.h" + +#define DIRECTLY_UPDATE_VELOCITY_DURING_SOLVER_ITERATIONS + +static bool interleaveContactAndFriction = false; + +struct btJointNode +{ + int jointIndex; // pointer to enclosing dxJoint object + int otherBodyIndex; // *other* body this joint is connected to + int nextJointNodeIndex; //-1 for null + int constraintRowIndex; +}; + +// Helper function to compute a delta velocity in the constraint space. +static btScalar computeDeltaVelocityInConstraintSpace( + const btVector3& angularDeltaVelocity, + const btVector3& contactNormal, + btScalar invMass, + const btVector3& angularJacobian, + const btVector3& linearJacobian) +{ + return angularDeltaVelocity.dot(angularJacobian) + contactNormal.dot(linearJacobian) * invMass; +} + +// Faster version of computeDeltaVelocityInConstraintSpace that can be used when contactNormal and linearJacobian are +// identical. +static btScalar computeDeltaVelocityInConstraintSpace( + const btVector3& angularDeltaVelocity, + btScalar invMass, + const btVector3& angularJacobian) +{ + return angularDeltaVelocity.dot(angularJacobian) + invMass; +} + +// Helper function to compute a delta velocity in the constraint space. +static btScalar computeDeltaVelocityInConstraintSpace(const btScalar* deltaVelocity, const btScalar* jacobian, int size) +{ + btScalar result = 0; + for (int i = 0; i < size; ++i) + result += deltaVelocity[i] * jacobian[i]; + + return result; +} + +static btScalar computeConstraintMatrixDiagElementMultiBody( + const btAlignedObjectArray<btSolverBody>& solverBodyPool, + const btMultiBodyJacobianData& data, + const btMultiBodySolverConstraint& constraint) +{ + btScalar ret = 0; + + const btMultiBody* multiBodyA = constraint.m_multiBodyA; + const btMultiBody* multiBodyB = constraint.m_multiBodyB; + + if (multiBodyA) + { + const btScalar* jacA = &data.m_jacobians[constraint.m_jacAindex]; + const btScalar* deltaA = &data.m_deltaVelocitiesUnitImpulse[constraint.m_jacAindex]; + const int ndofA = multiBodyA->getNumDofs() + 6; + ret += computeDeltaVelocityInConstraintSpace(deltaA, jacA, ndofA); + } + else + { + const int solverBodyIdA = constraint.m_solverBodyIdA; + btAssert(solverBodyIdA != -1); + const btSolverBody* solverBodyA = &solverBodyPool[solverBodyIdA]; + const btScalar invMassA = solverBodyA->m_originalBody ? solverBodyA->m_originalBody->getInvMass() : 0.0; + ret += computeDeltaVelocityInConstraintSpace( + constraint.m_relpos1CrossNormal, + invMassA, + constraint.m_angularComponentA); + } + + if (multiBodyB) + { + const btScalar* jacB = &data.m_jacobians[constraint.m_jacBindex]; + const btScalar* deltaB = &data.m_deltaVelocitiesUnitImpulse[constraint.m_jacBindex]; + const int ndofB = multiBodyB->getNumDofs() + 6; + ret += computeDeltaVelocityInConstraintSpace(deltaB, jacB, ndofB); + } + else + { + const int solverBodyIdB = constraint.m_solverBodyIdB; + btAssert(solverBodyIdB != -1); + const btSolverBody* solverBodyB = &solverBodyPool[solverBodyIdB]; + const btScalar invMassB = solverBodyB->m_originalBody ? solverBodyB->m_originalBody->getInvMass() : 0.0; + ret += computeDeltaVelocityInConstraintSpace( + constraint.m_relpos2CrossNormal, + invMassB, + constraint.m_angularComponentB); + } + + return ret; +} + +static btScalar computeConstraintMatrixOffDiagElementMultiBody( + const btAlignedObjectArray<btSolverBody>& solverBodyPool, + const btMultiBodyJacobianData& data, + const btMultiBodySolverConstraint& constraint, + const btMultiBodySolverConstraint& offDiagConstraint) +{ + btScalar offDiagA = btScalar(0); + + const btMultiBody* multiBodyA = constraint.m_multiBodyA; + const btMultiBody* multiBodyB = constraint.m_multiBodyB; + const btMultiBody* offDiagMultiBodyA = offDiagConstraint.m_multiBodyA; + const btMultiBody* offDiagMultiBodyB = offDiagConstraint.m_multiBodyB; + + // Assumed at least one system is multibody + btAssert(multiBodyA || multiBodyB); + btAssert(offDiagMultiBodyA || offDiagMultiBodyB); + + if (offDiagMultiBodyA) + { + const btScalar* offDiagJacA = &data.m_jacobians[offDiagConstraint.m_jacAindex]; + + if (offDiagMultiBodyA == multiBodyA) + { + const int ndofA = multiBodyA->getNumDofs() + 6; + const btScalar* deltaA = &data.m_deltaVelocitiesUnitImpulse[constraint.m_jacAindex]; + offDiagA += computeDeltaVelocityInConstraintSpace(deltaA, offDiagJacA, ndofA); + } + else if (offDiagMultiBodyA == multiBodyB) + { + const int ndofB = multiBodyB->getNumDofs() + 6; + const btScalar* deltaB = &data.m_deltaVelocitiesUnitImpulse[constraint.m_jacBindex]; + offDiagA += computeDeltaVelocityInConstraintSpace(deltaB, offDiagJacA, ndofB); + } + } + else + { + const int solverBodyIdA = constraint.m_solverBodyIdA; + const int solverBodyIdB = constraint.m_solverBodyIdB; + + const int offDiagSolverBodyIdA = offDiagConstraint.m_solverBodyIdA; + btAssert(offDiagSolverBodyIdA != -1); + + if (offDiagSolverBodyIdA == solverBodyIdA) + { + btAssert(solverBodyIdA != -1); + const btSolverBody* solverBodyA = &solverBodyPool[solverBodyIdA]; + const btScalar invMassA = solverBodyA->m_originalBody ? solverBodyA->m_originalBody->getInvMass() : 0.0; + offDiagA += computeDeltaVelocityInConstraintSpace( + offDiagConstraint.m_relpos1CrossNormal, + offDiagConstraint.m_contactNormal1, + invMassA, constraint.m_angularComponentA, + constraint.m_contactNormal1); + } + else if (offDiagSolverBodyIdA == solverBodyIdB) + { + btAssert(solverBodyIdB != -1); + const btSolverBody* solverBodyB = &solverBodyPool[solverBodyIdB]; + const btScalar invMassB = solverBodyB->m_originalBody ? solverBodyB->m_originalBody->getInvMass() : 0.0; + offDiagA += computeDeltaVelocityInConstraintSpace( + offDiagConstraint.m_relpos1CrossNormal, + offDiagConstraint.m_contactNormal1, + invMassB, + constraint.m_angularComponentB, + constraint.m_contactNormal2); + } + } + + if (offDiagMultiBodyB) + { + const btScalar* offDiagJacB = &data.m_jacobians[offDiagConstraint.m_jacBindex]; + + if (offDiagMultiBodyB == multiBodyA) + { + const int ndofA = multiBodyA->getNumDofs() + 6; + const btScalar* deltaA = &data.m_deltaVelocitiesUnitImpulse[constraint.m_jacAindex]; + offDiagA += computeDeltaVelocityInConstraintSpace(deltaA, offDiagJacB, ndofA); + } + else if (offDiagMultiBodyB == multiBodyB) + { + const int ndofB = multiBodyB->getNumDofs() + 6; + const btScalar* deltaB = &data.m_deltaVelocitiesUnitImpulse[constraint.m_jacBindex]; + offDiagA += computeDeltaVelocityInConstraintSpace(deltaB, offDiagJacB, ndofB); + } + } + else + { + const int solverBodyIdA = constraint.m_solverBodyIdA; + const int solverBodyIdB = constraint.m_solverBodyIdB; + + const int offDiagSolverBodyIdB = offDiagConstraint.m_solverBodyIdB; + btAssert(offDiagSolverBodyIdB != -1); + + if (offDiagSolverBodyIdB == solverBodyIdA) + { + btAssert(solverBodyIdA != -1); + const btSolverBody* solverBodyA = &solverBodyPool[solverBodyIdA]; + const btScalar invMassA = solverBodyA->m_originalBody ? solverBodyA->m_originalBody->getInvMass() : 0.0; + offDiagA += computeDeltaVelocityInConstraintSpace( + offDiagConstraint.m_relpos2CrossNormal, + offDiagConstraint.m_contactNormal2, + invMassA, constraint.m_angularComponentA, + constraint.m_contactNormal1); + } + else if (offDiagSolverBodyIdB == solverBodyIdB) + { + btAssert(solverBodyIdB != -1); + const btSolverBody* solverBodyB = &solverBodyPool[solverBodyIdB]; + const btScalar invMassB = solverBodyB->m_originalBody ? solverBodyB->m_originalBody->getInvMass() : 0.0; + offDiagA += computeDeltaVelocityInConstraintSpace( + offDiagConstraint.m_relpos2CrossNormal, + offDiagConstraint.m_contactNormal2, + invMassB, constraint.m_angularComponentB, + constraint.m_contactNormal2); + } + } + + return offDiagA; +} + +void btMultiBodyMLCPConstraintSolver::createMLCPFast(const btContactSolverInfo& infoGlobal) +{ + createMLCPFastRigidBody(infoGlobal); + createMLCPFastMultiBody(infoGlobal); +} + +void btMultiBodyMLCPConstraintSolver::createMLCPFastRigidBody(const btContactSolverInfo& infoGlobal) +{ + int numContactRows = interleaveContactAndFriction ? 3 : 1; + + int numConstraintRows = m_allConstraintPtrArray.size(); + + if (numConstraintRows == 0) + return; + + int n = numConstraintRows; + { + BT_PROFILE("init b (rhs)"); + m_b.resize(numConstraintRows); + m_bSplit.resize(numConstraintRows); + m_b.setZero(); + m_bSplit.setZero(); + for (int i = 0; i < numConstraintRows; i++) + { + btScalar jacDiag = m_allConstraintPtrArray[i]->m_jacDiagABInv; + if (!btFuzzyZero(jacDiag)) + { + btScalar rhs = m_allConstraintPtrArray[i]->m_rhs; + btScalar rhsPenetration = m_allConstraintPtrArray[i]->m_rhsPenetration; + m_b[i] = rhs / jacDiag; + m_bSplit[i] = rhsPenetration / jacDiag; + } + } + } + + // btScalar* w = 0; + // int nub = 0; + + m_lo.resize(numConstraintRows); + m_hi.resize(numConstraintRows); + + { + BT_PROFILE("init lo/ho"); + + for (int i = 0; i < numConstraintRows; i++) + { + if (0) //m_limitDependencies[i]>=0) + { + m_lo[i] = -BT_INFINITY; + m_hi[i] = BT_INFINITY; + } + else + { + m_lo[i] = m_allConstraintPtrArray[i]->m_lowerLimit; + m_hi[i] = m_allConstraintPtrArray[i]->m_upperLimit; + } + } + } + + // + int m = m_allConstraintPtrArray.size(); + + int numBodies = m_tmpSolverBodyPool.size(); + btAlignedObjectArray<int> bodyJointNodeArray; + { + BT_PROFILE("bodyJointNodeArray.resize"); + bodyJointNodeArray.resize(numBodies, -1); + } + btAlignedObjectArray<btJointNode> jointNodeArray; + { + BT_PROFILE("jointNodeArray.reserve"); + jointNodeArray.reserve(2 * m_allConstraintPtrArray.size()); + } + + btMatrixXu& J3 = m_scratchJ3; + { + BT_PROFILE("J3.resize"); + J3.resize(2 * m, 8); + } + btMatrixXu& JinvM3 = m_scratchJInvM3; + { + BT_PROFILE("JinvM3.resize/setZero"); + + JinvM3.resize(2 * m, 8); + JinvM3.setZero(); + J3.setZero(); + } + int cur = 0; + int rowOffset = 0; + btAlignedObjectArray<int>& ofs = m_scratchOfs; + { + BT_PROFILE("ofs resize"); + ofs.resize(0); + ofs.resizeNoInitialize(m_allConstraintPtrArray.size()); + } + { + BT_PROFILE("Compute J and JinvM"); + int c = 0; + + int numRows = 0; + + for (int i = 0; i < m_allConstraintPtrArray.size(); i += numRows, c++) + { + ofs[c] = rowOffset; + int sbA = m_allConstraintPtrArray[i]->m_solverBodyIdA; + int sbB = m_allConstraintPtrArray[i]->m_solverBodyIdB; + btRigidBody* orgBodyA = m_tmpSolverBodyPool[sbA].m_originalBody; + btRigidBody* orgBodyB = m_tmpSolverBodyPool[sbB].m_originalBody; + + numRows = i < m_tmpSolverNonContactConstraintPool.size() ? m_tmpConstraintSizesPool[c].m_numConstraintRows : numContactRows; + if (orgBodyA) + { + { + int slotA = -1; + //find free jointNode slot for sbA + slotA = jointNodeArray.size(); + jointNodeArray.expand(); //NonInitializing(); + int prevSlot = bodyJointNodeArray[sbA]; + bodyJointNodeArray[sbA] = slotA; + jointNodeArray[slotA].nextJointNodeIndex = prevSlot; + jointNodeArray[slotA].jointIndex = c; + jointNodeArray[slotA].constraintRowIndex = i; + jointNodeArray[slotA].otherBodyIndex = orgBodyB ? sbB : -1; + } + for (int row = 0; row < numRows; row++, cur++) + { + btVector3 normalInvMass = m_allConstraintPtrArray[i + row]->m_contactNormal1 * orgBodyA->getInvMass(); + btVector3 relPosCrossNormalInvInertia = m_allConstraintPtrArray[i + row]->m_relpos1CrossNormal * orgBodyA->getInvInertiaTensorWorld(); + + for (int r = 0; r < 3; r++) + { + J3.setElem(cur, r, m_allConstraintPtrArray[i + row]->m_contactNormal1[r]); + J3.setElem(cur, r + 4, m_allConstraintPtrArray[i + row]->m_relpos1CrossNormal[r]); + JinvM3.setElem(cur, r, normalInvMass[r]); + JinvM3.setElem(cur, r + 4, relPosCrossNormalInvInertia[r]); + } + J3.setElem(cur, 3, 0); + JinvM3.setElem(cur, 3, 0); + J3.setElem(cur, 7, 0); + JinvM3.setElem(cur, 7, 0); + } + } + else + { + cur += numRows; + } + if (orgBodyB) + { + { + int slotB = -1; + //find free jointNode slot for sbA + slotB = jointNodeArray.size(); + jointNodeArray.expand(); //NonInitializing(); + int prevSlot = bodyJointNodeArray[sbB]; + bodyJointNodeArray[sbB] = slotB; + jointNodeArray[slotB].nextJointNodeIndex = prevSlot; + jointNodeArray[slotB].jointIndex = c; + jointNodeArray[slotB].otherBodyIndex = orgBodyA ? sbA : -1; + jointNodeArray[slotB].constraintRowIndex = i; + } + + for (int row = 0; row < numRows; row++, cur++) + { + btVector3 normalInvMassB = m_allConstraintPtrArray[i + row]->m_contactNormal2 * orgBodyB->getInvMass(); + btVector3 relPosInvInertiaB = m_allConstraintPtrArray[i + row]->m_relpos2CrossNormal * orgBodyB->getInvInertiaTensorWorld(); + + for (int r = 0; r < 3; r++) + { + J3.setElem(cur, r, m_allConstraintPtrArray[i + row]->m_contactNormal2[r]); + J3.setElem(cur, r + 4, m_allConstraintPtrArray[i + row]->m_relpos2CrossNormal[r]); + JinvM3.setElem(cur, r, normalInvMassB[r]); + JinvM3.setElem(cur, r + 4, relPosInvInertiaB[r]); + } + J3.setElem(cur, 3, 0); + JinvM3.setElem(cur, 3, 0); + J3.setElem(cur, 7, 0); + JinvM3.setElem(cur, 7, 0); + } + } + else + { + cur += numRows; + } + rowOffset += numRows; + } + } + + //compute JinvM = J*invM. + const btScalar* JinvM = JinvM3.getBufferPointer(); + + const btScalar* Jptr = J3.getBufferPointer(); + { + BT_PROFILE("m_A.resize"); + m_A.resize(n, n); + } + + { + BT_PROFILE("m_A.setZero"); + m_A.setZero(); + } + int c = 0; + { + int numRows = 0; + BT_PROFILE("Compute A"); + for (int i = 0; i < m_allConstraintPtrArray.size(); i += numRows, c++) + { + int row__ = ofs[c]; + int sbA = m_allConstraintPtrArray[i]->m_solverBodyIdA; + int sbB = m_allConstraintPtrArray[i]->m_solverBodyIdB; + // btRigidBody* orgBodyA = m_tmpSolverBodyPool[sbA].m_originalBody; + // btRigidBody* orgBodyB = m_tmpSolverBodyPool[sbB].m_originalBody; + + numRows = i < m_tmpSolverNonContactConstraintPool.size() ? m_tmpConstraintSizesPool[c].m_numConstraintRows : numContactRows; + + const btScalar* JinvMrow = JinvM + 2 * 8 * (size_t)row__; + + { + int startJointNodeA = bodyJointNodeArray[sbA]; + while (startJointNodeA >= 0) + { + int j0 = jointNodeArray[startJointNodeA].jointIndex; + int cr0 = jointNodeArray[startJointNodeA].constraintRowIndex; + if (j0 < c) + { + int numRowsOther = cr0 < m_tmpSolverNonContactConstraintPool.size() ? m_tmpConstraintSizesPool[j0].m_numConstraintRows : numContactRows; + size_t ofsother = (m_allConstraintPtrArray[cr0]->m_solverBodyIdB == sbA) ? 8 * numRowsOther : 0; + //printf("%d joint i %d and j0: %d: ",count++,i,j0); + m_A.multiplyAdd2_p8r(JinvMrow, + Jptr + 2 * 8 * (size_t)ofs[j0] + ofsother, numRows, numRowsOther, row__, ofs[j0]); + } + startJointNodeA = jointNodeArray[startJointNodeA].nextJointNodeIndex; + } + } + + { + int startJointNodeB = bodyJointNodeArray[sbB]; + while (startJointNodeB >= 0) + { + int j1 = jointNodeArray[startJointNodeB].jointIndex; + int cj1 = jointNodeArray[startJointNodeB].constraintRowIndex; + + if (j1 < c) + { + int numRowsOther = cj1 < m_tmpSolverNonContactConstraintPool.size() ? m_tmpConstraintSizesPool[j1].m_numConstraintRows : numContactRows; + size_t ofsother = (m_allConstraintPtrArray[cj1]->m_solverBodyIdB == sbB) ? 8 * numRowsOther : 0; + m_A.multiplyAdd2_p8r(JinvMrow + 8 * (size_t)numRows, + Jptr + 2 * 8 * (size_t)ofs[j1] + ofsother, numRows, numRowsOther, row__, ofs[j1]); + } + startJointNodeB = jointNodeArray[startJointNodeB].nextJointNodeIndex; + } + } + } + + { + BT_PROFILE("compute diagonal"); + // compute diagonal blocks of m_A + + int row__ = 0; + int numJointRows = m_allConstraintPtrArray.size(); + + int jj = 0; + for (; row__ < numJointRows;) + { + //int sbA = m_allConstraintPtrArray[row__]->m_solverBodyIdA; + int sbB = m_allConstraintPtrArray[row__]->m_solverBodyIdB; + // btRigidBody* orgBodyA = m_tmpSolverBodyPool[sbA].m_originalBody; + btRigidBody* orgBodyB = m_tmpSolverBodyPool[sbB].m_originalBody; + + const unsigned int infom = row__ < m_tmpSolverNonContactConstraintPool.size() ? m_tmpConstraintSizesPool[jj].m_numConstraintRows : numContactRows; + + const btScalar* JinvMrow = JinvM + 2 * 8 * (size_t)row__; + const btScalar* Jrow = Jptr + 2 * 8 * (size_t)row__; + m_A.multiply2_p8r(JinvMrow, Jrow, infom, infom, row__, row__); + if (orgBodyB) + { + m_A.multiplyAdd2_p8r(JinvMrow + 8 * (size_t)infom, Jrow + 8 * (size_t)infom, infom, infom, row__, row__); + } + row__ += infom; + jj++; + } + } + } + + if (1) + { + // add cfm to the diagonal of m_A + for (int i = 0; i < m_A.rows(); ++i) + { + m_A.setElem(i, i, m_A(i, i) + infoGlobal.m_globalCfm / infoGlobal.m_timeStep); + } + } + + ///fill the upper triangle of the matrix, to make it symmetric + { + BT_PROFILE("fill the upper triangle "); + m_A.copyLowerToUpperTriangle(); + } + + { + BT_PROFILE("resize/init x"); + m_x.resize(numConstraintRows); + m_xSplit.resize(numConstraintRows); + + if (infoGlobal.m_solverMode & SOLVER_USE_WARMSTARTING) + { + for (int i = 0; i < m_allConstraintPtrArray.size(); i++) + { + const btSolverConstraint& c = *m_allConstraintPtrArray[i]; + m_x[i] = c.m_appliedImpulse; + m_xSplit[i] = c.m_appliedPushImpulse; + } + } + else + { + m_x.setZero(); + m_xSplit.setZero(); + } + } +} + +void btMultiBodyMLCPConstraintSolver::createMLCPFastMultiBody(const btContactSolverInfo& infoGlobal) +{ + const int multiBodyNumConstraints = m_multiBodyAllConstraintPtrArray.size(); + + if (multiBodyNumConstraints == 0) + return; + + // 1. Compute b + { + BT_PROFILE("init b (rhs)"); + + m_multiBodyB.resize(multiBodyNumConstraints); + m_multiBodyB.setZero(); + + for (int i = 0; i < multiBodyNumConstraints; ++i) + { + const btMultiBodySolverConstraint& constraint = *m_multiBodyAllConstraintPtrArray[i]; + const btScalar jacDiag = constraint.m_jacDiagABInv; + + if (!btFuzzyZero(jacDiag)) + { + // Note that rhsPenetration is currently always zero because the split impulse hasn't been implemented for multibody yet. + const btScalar rhs = constraint.m_rhs; + m_multiBodyB[i] = rhs / jacDiag; + } + } + } + + // 2. Compute lo and hi + { + BT_PROFILE("init lo/ho"); + + m_multiBodyLo.resize(multiBodyNumConstraints); + m_multiBodyHi.resize(multiBodyNumConstraints); + + for (int i = 0; i < multiBodyNumConstraints; ++i) + { + const btMultiBodySolverConstraint& constraint = *m_multiBodyAllConstraintPtrArray[i]; + m_multiBodyLo[i] = constraint.m_lowerLimit; + m_multiBodyHi[i] = constraint.m_upperLimit; + } + } + + // 3. Construct A matrix by using the impulse testing + { + BT_PROFILE("Compute A"); + + { + BT_PROFILE("m_A.resize"); + m_multiBodyA.resize(multiBodyNumConstraints, multiBodyNumConstraints); + } + + for (int i = 0; i < multiBodyNumConstraints; ++i) + { + // Compute the diagonal of A, which is A(i, i) + const btMultiBodySolverConstraint& constraint = *m_multiBodyAllConstraintPtrArray[i]; + const btScalar diagA = computeConstraintMatrixDiagElementMultiBody(m_tmpSolverBodyPool, m_data, constraint); + m_multiBodyA.setElem(i, i, diagA); + + // Computes the off-diagonals of A: + // a. The rest of i-th row of A, from A(i, i+1) to A(i, n) + // b. The rest of i-th column of A, from A(i+1, i) to A(n, i) + for (int j = i + 1; j < multiBodyNumConstraints; ++j) + { + const btMultiBodySolverConstraint& offDiagConstraint = *m_multiBodyAllConstraintPtrArray[j]; + const btScalar offDiagA = computeConstraintMatrixOffDiagElementMultiBody(m_tmpSolverBodyPool, m_data, constraint, offDiagConstraint); + + // Set the off-diagonal values of A. Note that A is symmetric. + m_multiBodyA.setElem(i, j, offDiagA); + m_multiBodyA.setElem(j, i, offDiagA); + } + } + } + + // Add CFM to the diagonal of m_A + for (int i = 0; i < m_multiBodyA.rows(); ++i) + { + m_multiBodyA.setElem(i, i, m_multiBodyA(i, i) + infoGlobal.m_globalCfm / infoGlobal.m_timeStep); + } + + // 4. Initialize x + { + BT_PROFILE("resize/init x"); + + m_multiBodyX.resize(multiBodyNumConstraints); + + if (infoGlobal.m_solverMode & SOLVER_USE_WARMSTARTING) + { + for (int i = 0; i < multiBodyNumConstraints; ++i) + { + const btMultiBodySolverConstraint& constraint = *m_multiBodyAllConstraintPtrArray[i]; + m_multiBodyX[i] = constraint.m_appliedImpulse; + } + } + else + { + m_multiBodyX.setZero(); + } + } +} + +bool btMultiBodyMLCPConstraintSolver::solveMLCP(const btContactSolverInfo& infoGlobal) +{ + bool result = true; + + if (m_A.rows() != 0) + { + // If using split impulse, we solve 2 separate (M)LCPs + if (infoGlobal.m_splitImpulse) + { + const btMatrixXu Acopy = m_A; + const btAlignedObjectArray<int> limitDependenciesCopy = m_limitDependencies; + // TODO(JS): Do we really need these copies when solveMLCP takes them as const? + + result = m_solver->solveMLCP(m_A, m_b, m_x, m_lo, m_hi, m_limitDependencies, infoGlobal.m_numIterations); + if (result) + result = m_solver->solveMLCP(Acopy, m_bSplit, m_xSplit, m_lo, m_hi, limitDependenciesCopy, infoGlobal.m_numIterations); + } + else + { + result = m_solver->solveMLCP(m_A, m_b, m_x, m_lo, m_hi, m_limitDependencies, infoGlobal.m_numIterations); + } + } + + if (!result) + return false; + + if (m_multiBodyA.rows() != 0) + { + result = m_solver->solveMLCP(m_multiBodyA, m_multiBodyB, m_multiBodyX, m_multiBodyLo, m_multiBodyHi, m_multiBodyLimitDependencies, infoGlobal.m_numIterations); + } + + return result; +} + +btScalar btMultiBodyMLCPConstraintSolver::solveGroupCacheFriendlySetup( + btCollisionObject** bodies, + int numBodies, + btPersistentManifold** manifoldPtr, + int numManifolds, + btTypedConstraint** constraints, + int numConstraints, + const btContactSolverInfo& infoGlobal, + btIDebugDraw* debugDrawer) +{ + // 1. Setup for rigid-bodies + btMultiBodyConstraintSolver::solveGroupCacheFriendlySetup( + bodies, numBodies, manifoldPtr, numManifolds, constraints, numConstraints, infoGlobal, debugDrawer); + + // 2. Setup for multi-bodies + // a. Collect all different kinds of constraint as pointers into one array, m_allConstraintPtrArray + // b. Set the index array for frictional contact constraints, m_limitDependencies + { + BT_PROFILE("gather constraint data"); + + int dindex = 0; + + const int numRigidBodyConstraints = m_tmpSolverNonContactConstraintPool.size() + m_tmpSolverContactConstraintPool.size() + m_tmpSolverContactFrictionConstraintPool.size(); + const int numMultiBodyConstraints = m_multiBodyNonContactConstraints.size() + m_multiBodyNormalContactConstraints.size() + m_multiBodyFrictionContactConstraints.size(); + + m_allConstraintPtrArray.resize(0); + m_multiBodyAllConstraintPtrArray.resize(0); + + // i. Setup for rigid bodies + + m_limitDependencies.resize(numRigidBodyConstraints); + + for (int i = 0; i < m_tmpSolverNonContactConstraintPool.size(); ++i) + { + m_allConstraintPtrArray.push_back(&m_tmpSolverNonContactConstraintPool[i]); + m_limitDependencies[dindex++] = -1; + } + + int firstContactConstraintOffset = dindex; + + // The btSequentialImpulseConstraintSolver moves all friction constraints at the very end, we can also interleave them instead + if (interleaveContactAndFriction) + { + for (int i = 0; i < m_tmpSolverContactConstraintPool.size(); i++) + { + const int numFrictionPerContact = m_tmpSolverContactConstraintPool.size() == m_tmpSolverContactFrictionConstraintPool.size() ? 1 : 2; + + m_allConstraintPtrArray.push_back(&m_tmpSolverContactConstraintPool[i]); + m_limitDependencies[dindex++] = -1; + m_allConstraintPtrArray.push_back(&m_tmpSolverContactFrictionConstraintPool[i * numFrictionPerContact]); + int findex = (m_tmpSolverContactFrictionConstraintPool[i * numFrictionPerContact].m_frictionIndex * (1 + numFrictionPerContact)); + m_limitDependencies[dindex++] = findex + firstContactConstraintOffset; + if (numFrictionPerContact == 2) + { + m_allConstraintPtrArray.push_back(&m_tmpSolverContactFrictionConstraintPool[i * numFrictionPerContact + 1]); + m_limitDependencies[dindex++] = findex + firstContactConstraintOffset; + } + } + } + else + { + for (int i = 0; i < m_tmpSolverContactConstraintPool.size(); i++) + { + m_allConstraintPtrArray.push_back(&m_tmpSolverContactConstraintPool[i]); + m_limitDependencies[dindex++] = -1; + } + for (int i = 0; i < m_tmpSolverContactFrictionConstraintPool.size(); i++) + { + m_allConstraintPtrArray.push_back(&m_tmpSolverContactFrictionConstraintPool[i]); + m_limitDependencies[dindex++] = m_tmpSolverContactFrictionConstraintPool[i].m_frictionIndex + firstContactConstraintOffset; + } + } + + if (!m_allConstraintPtrArray.size()) + { + m_A.resize(0, 0); + m_b.resize(0); + m_x.resize(0); + m_lo.resize(0); + m_hi.resize(0); + } + + // ii. Setup for multibodies + + dindex = 0; + + m_multiBodyLimitDependencies.resize(numMultiBodyConstraints); + + for (int i = 0; i < m_multiBodyNonContactConstraints.size(); ++i) + { + m_multiBodyAllConstraintPtrArray.push_back(&m_multiBodyNonContactConstraints[i]); + m_multiBodyLimitDependencies[dindex++] = -1; + } + + firstContactConstraintOffset = dindex; + + // The btSequentialImpulseConstraintSolver moves all friction constraints at the very end, we can also interleave them instead + if (interleaveContactAndFriction) + { + for (int i = 0; i < m_multiBodyNormalContactConstraints.size(); ++i) + { + const int numtiBodyNumFrictionPerContact = m_multiBodyNormalContactConstraints.size() == m_multiBodyFrictionContactConstraints.size() ? 1 : 2; + + m_multiBodyAllConstraintPtrArray.push_back(&m_multiBodyNormalContactConstraints[i]); + m_multiBodyLimitDependencies[dindex++] = -1; + + btMultiBodySolverConstraint& frictionContactConstraint1 = m_multiBodyFrictionContactConstraints[i * numtiBodyNumFrictionPerContact]; + m_multiBodyAllConstraintPtrArray.push_back(&frictionContactConstraint1); + + const int findex = (frictionContactConstraint1.m_frictionIndex * (1 + numtiBodyNumFrictionPerContact)) + firstContactConstraintOffset; + + m_multiBodyLimitDependencies[dindex++] = findex; + + if (numtiBodyNumFrictionPerContact == 2) + { + btMultiBodySolverConstraint& frictionContactConstraint2 = m_multiBodyFrictionContactConstraints[i * numtiBodyNumFrictionPerContact + 1]; + m_multiBodyAllConstraintPtrArray.push_back(&frictionContactConstraint2); + + m_multiBodyLimitDependencies[dindex++] = findex; + } + } + } + else + { + for (int i = 0; i < m_multiBodyNormalContactConstraints.size(); ++i) + { + m_multiBodyAllConstraintPtrArray.push_back(&m_multiBodyNormalContactConstraints[i]); + m_multiBodyLimitDependencies[dindex++] = -1; + } + for (int i = 0; i < m_multiBodyFrictionContactConstraints.size(); ++i) + { + m_multiBodyAllConstraintPtrArray.push_back(&m_multiBodyFrictionContactConstraints[i]); + m_multiBodyLimitDependencies[dindex++] = m_multiBodyFrictionContactConstraints[i].m_frictionIndex + firstContactConstraintOffset; + } + } + + if (!m_multiBodyAllConstraintPtrArray.size()) + { + m_multiBodyA.resize(0, 0); + m_multiBodyB.resize(0); + m_multiBodyX.resize(0); + m_multiBodyLo.resize(0); + m_multiBodyHi.resize(0); + } + } + + // Construct MLCP terms + { + BT_PROFILE("createMLCPFast"); + createMLCPFast(infoGlobal); + } + + return btScalar(0); +} + +btScalar btMultiBodyMLCPConstraintSolver::solveGroupCacheFriendlyIterations(btCollisionObject** bodies, int numBodies, btPersistentManifold** manifoldPtr, int numManifolds, btTypedConstraint** constraints, int numConstraints, const btContactSolverInfo& infoGlobal, btIDebugDraw* debugDrawer) +{ + bool result = true; + { + BT_PROFILE("solveMLCP"); + result = solveMLCP(infoGlobal); + } + + // Fallback to btSequentialImpulseConstraintSolver::solveGroupCacheFriendlyIterations if the solution isn't valid. + if (!result) + { + m_fallback++; + return btMultiBodyConstraintSolver::solveGroupCacheFriendlyIterations(bodies, numBodies, manifoldPtr, numManifolds, constraints, numConstraints, infoGlobal, debugDrawer); + } + + { + BT_PROFILE("process MLCP results"); + + for (int i = 0; i < m_allConstraintPtrArray.size(); ++i) + { + const btSolverConstraint& c = *m_allConstraintPtrArray[i]; + + const btScalar deltaImpulse = m_x[i] - c.m_appliedImpulse; + c.m_appliedImpulse = m_x[i]; + + int sbA = c.m_solverBodyIdA; + int sbB = c.m_solverBodyIdB; + + btSolverBody& solverBodyA = m_tmpSolverBodyPool[sbA]; + btSolverBody& solverBodyB = m_tmpSolverBodyPool[sbB]; + + solverBodyA.internalApplyImpulse(c.m_contactNormal1 * solverBodyA.internalGetInvMass(), c.m_angularComponentA, deltaImpulse); + solverBodyB.internalApplyImpulse(c.m_contactNormal2 * solverBodyB.internalGetInvMass(), c.m_angularComponentB, deltaImpulse); + + if (infoGlobal.m_splitImpulse) + { + const btScalar deltaPushImpulse = m_xSplit[i] - c.m_appliedPushImpulse; + solverBodyA.internalApplyPushImpulse(c.m_contactNormal1 * solverBodyA.internalGetInvMass(), c.m_angularComponentA, deltaPushImpulse); + solverBodyB.internalApplyPushImpulse(c.m_contactNormal2 * solverBodyB.internalGetInvMass(), c.m_angularComponentB, deltaPushImpulse); + c.m_appliedPushImpulse = m_xSplit[i]; + } + } + + for (int i = 0; i < m_multiBodyAllConstraintPtrArray.size(); ++i) + { + btMultiBodySolverConstraint& c = *m_multiBodyAllConstraintPtrArray[i]; + + const btScalar deltaImpulse = m_multiBodyX[i] - c.m_appliedImpulse; + c.m_appliedImpulse = m_multiBodyX[i]; + + btMultiBody* multiBodyA = c.m_multiBodyA; + if (multiBodyA) + { + const int ndofA = multiBodyA->getNumDofs() + 6; + applyDeltaVee(&m_data.m_deltaVelocitiesUnitImpulse[c.m_jacAindex], deltaImpulse, c.m_deltaVelAindex, ndofA); +#ifdef DIRECTLY_UPDATE_VELOCITY_DURING_SOLVER_ITERATIONS + //note: update of the actual velocities (below) in the multibody does not have to happen now since m_deltaVelocities can be applied after all iterations + //it would make the multibody solver more like the regular one with m_deltaVelocities being equivalent to btSolverBody::m_deltaLinearVelocity/m_deltaAngularVelocity + multiBodyA->applyDeltaVeeMultiDof2(&m_data.m_deltaVelocitiesUnitImpulse[c.m_jacAindex], deltaImpulse); +#endif // DIRECTLY_UPDATE_VELOCITY_DURING_SOLVER_ITERATIONS + } + else + { + const int sbA = c.m_solverBodyIdA; + btSolverBody& solverBodyA = m_tmpSolverBodyPool[sbA]; + solverBodyA.internalApplyImpulse(c.m_contactNormal1 * solverBodyA.internalGetInvMass(), c.m_angularComponentA, deltaImpulse); + } + + btMultiBody* multiBodyB = c.m_multiBodyB; + if (multiBodyB) + { + const int ndofB = multiBodyB->getNumDofs() + 6; + applyDeltaVee(&m_data.m_deltaVelocitiesUnitImpulse[c.m_jacBindex], deltaImpulse, c.m_deltaVelBindex, ndofB); +#ifdef DIRECTLY_UPDATE_VELOCITY_DURING_SOLVER_ITERATIONS + //note: update of the actual velocities (below) in the multibody does not have to happen now since m_deltaVelocities can be applied after all iterations + //it would make the multibody solver more like the regular one with m_deltaVelocities being equivalent to btSolverBody::m_deltaLinearVelocity/m_deltaAngularVelocity + multiBodyB->applyDeltaVeeMultiDof2(&m_data.m_deltaVelocitiesUnitImpulse[c.m_jacBindex], deltaImpulse); +#endif // DIRECTLY_UPDATE_VELOCITY_DURING_SOLVER_ITERATIONS + } + else + { + const int sbB = c.m_solverBodyIdB; + btSolverBody& solverBodyB = m_tmpSolverBodyPool[sbB]; + solverBodyB.internalApplyImpulse(c.m_contactNormal2 * solverBodyB.internalGetInvMass(), c.m_angularComponentB, deltaImpulse); + } + } + } + + return btScalar(0); +} + +btMultiBodyMLCPConstraintSolver::btMultiBodyMLCPConstraintSolver(btMLCPSolverInterface* solver) + : m_solver(solver), m_fallback(0) +{ + // Do nothing +} + +btMultiBodyMLCPConstraintSolver::~btMultiBodyMLCPConstraintSolver() +{ + // Do nothing +} + +void btMultiBodyMLCPConstraintSolver::setMLCPSolver(btMLCPSolverInterface* solver) +{ + m_solver = solver; +} + +int btMultiBodyMLCPConstraintSolver::getNumFallbacks() const +{ + return m_fallback; +} + +void btMultiBodyMLCPConstraintSolver::setNumFallbacks(int num) +{ + m_fallback = num; +} + +btConstraintSolverType btMultiBodyMLCPConstraintSolver::getSolverType() const +{ + return BT_MLCP_SOLVER; +} diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyMLCPConstraintSolver.h b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyMLCPConstraintSolver.h new file mode 100644 index 0000000000..6be36ba142 --- /dev/null +++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyMLCPConstraintSolver.h @@ -0,0 +1,187 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2018 Google Inc. http://bulletphysics.org + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef BT_MULTIBODY_MLCP_CONSTRAINT_SOLVER_H +#define BT_MULTIBODY_MLCP_CONSTRAINT_SOLVER_H + +#include "LinearMath/btMatrixX.h" +#include "LinearMath/btThreads.h" +#include "BulletDynamics/Featherstone/btMultiBodyConstraintSolver.h" + +class btMLCPSolverInterface; +class btMultiBody; + +class btMultiBodyMLCPConstraintSolver : public btMultiBodyConstraintSolver +{ +protected: + /// \name MLCP Formulation for Rigid Bodies + /// \{ + + /// A matrix in the MLCP formulation + btMatrixXu m_A; + + /// b vector in the MLCP formulation. + btVectorXu m_b; + + /// Constraint impulse, which is an output of MLCP solving. + btVectorXu m_x; + + /// Lower bound of constraint impulse, \c m_x. + btVectorXu m_lo; + + /// Upper bound of constraint impulse, \c m_x. + btVectorXu m_hi; + + /// \} + + /// \name Cache Variables for Split Impulse for Rigid Bodies + /// When using 'split impulse' we solve two separate (M)LCPs + /// \{ + + /// Split impulse Cache vector corresponding to \c m_b. + btVectorXu m_bSplit; + + /// Split impulse cache vector corresponding to \c m_x. + btVectorXu m_xSplit; + + /// \} + + /// \name MLCP Formulation for Multibodies + /// \{ + + /// A matrix in the MLCP formulation + btMatrixXu m_multiBodyA; + + /// b vector in the MLCP formulation. + btVectorXu m_multiBodyB; + + /// Constraint impulse, which is an output of MLCP solving. + btVectorXu m_multiBodyX; + + /// Lower bound of constraint impulse, \c m_x. + btVectorXu m_multiBodyLo; + + /// Upper bound of constraint impulse, \c m_x. + btVectorXu m_multiBodyHi; + + /// \} + + /// Indices of normal contact constraint associated with frictional contact constraint for rigid bodies. + /// + /// This is used by the MLCP solver to update the upper bounds of frictional contact impulse given intermediate + /// normal contact impulse. For example, i-th element represents the index of a normal constraint that is + /// accosiated with i-th frictional contact constraint if i-th constraint is a frictional contact constraint. + /// Otherwise, -1. + btAlignedObjectArray<int> m_limitDependencies; + + /// Indices of normal contact constraint associated with frictional contact constraint for multibodies. + /// + /// This is used by the MLCP solver to update the upper bounds of frictional contact impulse given intermediate + /// normal contact impulse. For example, i-th element represents the index of a normal constraint that is + /// accosiated with i-th frictional contact constraint if i-th constraint is a frictional contact constraint. + /// Otherwise, -1. + btAlignedObjectArray<int> m_multiBodyLimitDependencies; + + /// Array of all the rigid body constraints + btAlignedObjectArray<btSolverConstraint*> m_allConstraintPtrArray; + + /// Array of all the multibody constraints + btAlignedObjectArray<btMultiBodySolverConstraint*> m_multiBodyAllConstraintPtrArray; + + /// MLCP solver + btMLCPSolverInterface* m_solver; + + /// Count of fallbacks of using btSequentialImpulseConstraintSolver, which happens when the MLCP solver fails. + int m_fallback; + + /// \name MLCP Scratch Variables + /// The following scratch variables are not stateful -- contents are cleared prior to each use. + /// They are only cached here to avoid extra memory allocations and deallocations and to ensure + /// that multiple instances of the solver can be run in parallel. + /// + /// \{ + + /// Cache variable for constraint Jacobian matrix. + btMatrixXu m_scratchJ3; + + /// Cache variable for constraint Jacobian times inverse mass matrix. + btMatrixXu m_scratchJInvM3; + + /// Cache variable for offsets. + btAlignedObjectArray<int> m_scratchOfs; + + /// \} + + /// Constructs MLCP terms, which are \c m_A, \c m_b, \c m_lo, and \c m_hi. + virtual void createMLCPFast(const btContactSolverInfo& infoGlobal); + + /// Constructs MLCP terms for constraints of two rigid bodies + void createMLCPFastRigidBody(const btContactSolverInfo& infoGlobal); + + /// Constructs MLCP terms for constraints of two multi-bodies or one rigid body and one multibody + void createMLCPFastMultiBody(const btContactSolverInfo& infoGlobal); + + /// Solves MLCP and returns the success + virtual bool solveMLCP(const btContactSolverInfo& infoGlobal); + + // Documentation inherited + btScalar solveGroupCacheFriendlySetup( + btCollisionObject** bodies, + int numBodies, + btPersistentManifold** manifoldPtr, + int numManifolds, + btTypedConstraint** constraints, + int numConstraints, + const btContactSolverInfo& infoGlobal, + btIDebugDraw* debugDrawer) BT_OVERRIDE; + + // Documentation inherited + btScalar solveGroupCacheFriendlyIterations( + btCollisionObject** bodies, + int numBodies, + btPersistentManifold** manifoldPtr, + int numManifolds, + btTypedConstraint** constraints, + int numConstraints, + const btContactSolverInfo& infoGlobal, + btIDebugDraw* debugDrawer) BT_OVERRIDE; + +public: + BT_DECLARE_ALIGNED_ALLOCATOR() + + /// Constructor + /// + /// \param[in] solver MLCP solver. Assumed it's not null. + /// \param[in] maxLCPSize Maximum size of LCP to solve using MLCP solver. If the MLCP size exceeds this number, sequaltial impulse method will be used. + explicit btMultiBodyMLCPConstraintSolver(btMLCPSolverInterface* solver); + + /// Destructor + virtual ~btMultiBodyMLCPConstraintSolver(); + + /// Sets MLCP solver. Assumed it's not null. + void setMLCPSolver(btMLCPSolverInterface* solver); + + /// Returns the number of fallbacks of using btSequentialImpulseConstraintSolver, which happens when the MLCP + /// solver fails. + int getNumFallbacks() const; + + /// Sets the number of fallbacks. This function may be used to reset the number to zero. + void setNumFallbacks(int num); + + /// Returns the constraint solver type. + virtual btConstraintSolverType getSolverType() const; +}; + +#endif // BT_MULTIBODY_MLCP_CONSTRAINT_SOLVER_H diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyPoint2Point.cpp b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyPoint2Point.cpp index 125d52ad0b..2b59f0b7a6 100644 --- a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyPoint2Point.cpp +++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodyPoint2Point.cpp @@ -64,13 +64,16 @@ int btMultiBodyPoint2Point::getIslandIdA() const if (m_bodyA) { - btMultiBodyLinkCollider* col = m_bodyA->getBaseCollider(); - if (col) - return col->getIslandTag(); - for (int i=0;i<m_bodyA->getNumLinks();i++) + if (m_linkA < 0) { - if (m_bodyA->getLink(i).m_collider) - return m_bodyA->getLink(i).m_collider->getIslandTag(); + btMultiBodyLinkCollider* col = m_bodyA->getBaseCollider(); + if (col) + return col->getIslandTag(); + } + else + { + if (m_bodyA->getLink(m_linkA).m_collider) + return m_bodyA->getLink(m_linkA).m_collider->getIslandTag(); } } return -1; @@ -82,16 +85,17 @@ int btMultiBodyPoint2Point::getIslandIdB() const return m_rigidBodyB->getIslandTag(); if (m_bodyB) { - btMultiBodyLinkCollider* col = m_bodyB->getBaseCollider(); - if (col) - return col->getIslandTag(); - - for (int i=0;i<m_bodyB->getNumLinks();i++) + if (m_linkB < 0) { - col = m_bodyB->getLink(i).m_collider; + btMultiBodyLinkCollider* col = m_bodyB->getBaseCollider(); if (col) return col->getIslandTag(); } + else + { + if (m_bodyB->getLink(m_linkB).m_collider) + return m_bodyB->getLink(m_linkB).m_collider->getIslandTag(); + } } return -1; } diff --git a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodySliderConstraint.cpp b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodySliderConstraint.cpp index 3b64b8183f..43f26f9833 100644 --- a/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodySliderConstraint.cpp +++ b/thirdparty/bullet/BulletDynamics/Featherstone/btMultiBodySliderConstraint.cpp @@ -68,13 +68,16 @@ int btMultiBodySliderConstraint::getIslandIdA() const if (m_bodyA) { - btMultiBodyLinkCollider* col = m_bodyA->getBaseCollider(); - if (col) - return col->getIslandTag(); - for (int i=0;i<m_bodyA->getNumLinks();i++) + if (m_linkA < 0) { - if (m_bodyA->getLink(i).m_collider) - return m_bodyA->getLink(i).m_collider->getIslandTag(); + btMultiBodyLinkCollider* col = m_bodyA->getBaseCollider(); + if (col) + return col->getIslandTag(); + } + else + { + if (m_bodyA->getLink(m_linkA).m_collider) + return m_bodyA->getLink(m_linkA).m_collider->getIslandTag(); } } return -1; @@ -86,20 +89,20 @@ int btMultiBodySliderConstraint::getIslandIdB() const return m_rigidBodyB->getIslandTag(); if (m_bodyB) { - btMultiBodyLinkCollider* col = m_bodyB->getBaseCollider(); - if (col) - return col->getIslandTag(); - - for (int i=0;i<m_bodyB->getNumLinks();i++) + if (m_linkB < 0) { - col = m_bodyB->getLink(i).m_collider; + btMultiBodyLinkCollider* col = m_bodyB->getBaseCollider(); if (col) return col->getIslandTag(); } + else + { + if (m_bodyB->getLink(m_linkB).m_collider) + return m_bodyB->getLink(m_linkB).m_collider->getIslandTag(); + } } return -1; } - void btMultiBodySliderConstraint::createConstraintRows(btMultiBodyConstraintArray& constraintRows, btMultiBodyJacobianData& data, const btContactSolverInfo& infoGlobal) { // Convert local points back to world diff --git a/thirdparty/bullet/BulletDynamics/Vehicle/btRaycastVehicle.cpp b/thirdparty/bullet/BulletDynamics/Vehicle/btRaycastVehicle.cpp index a7b1688469..f299aa34e8 100644 --- a/thirdparty/bullet/BulletDynamics/Vehicle/btRaycastVehicle.cpp +++ b/thirdparty/bullet/BulletDynamics/Vehicle/btRaycastVehicle.cpp @@ -121,12 +121,19 @@ void btRaycastVehicle::updateWheelTransform( int wheelIndex , bool interpolatedT btQuaternion rotatingOrn(right,-wheel.m_rotation); btMatrix3x3 rotatingMat(rotatingOrn); - btMatrix3x3 basis2( - right[0],fwd[0],up[0], - right[1],fwd[1],up[1], - right[2],fwd[2],up[2] - ); - + btMatrix3x3 basis2; + basis2[0][m_indexRightAxis] = -right[0]; + basis2[1][m_indexRightAxis] = -right[1]; + basis2[2][m_indexRightAxis] = -right[2]; + + basis2[0][m_indexUpAxis] = up[0]; + basis2[1][m_indexUpAxis] = up[1]; + basis2[2][m_indexUpAxis] = up[2]; + + basis2[0][m_indexForwardAxis] = fwd[0]; + basis2[1][m_indexForwardAxis] = fwd[1]; + basis2[2][m_indexForwardAxis] = fwd[2]; + wheel.m_worldTransform.setBasis(steeringMat * rotatingMat * basis2); wheel.m_worldTransform.setOrigin( wheel.m_raycastInfo.m_hardPointWS + wheel.m_raycastInfo.m_wheelDirectionWS * wheel.m_raycastInfo.m_suspensionLength @@ -493,8 +500,8 @@ struct btWheelContactPoint }; -btScalar calcRollingFriction(btWheelContactPoint& contactPoint); -btScalar calcRollingFriction(btWheelContactPoint& contactPoint) +btScalar calcRollingFriction(btWheelContactPoint& contactPoint, int numWheelsOnGround); +btScalar calcRollingFriction(btWheelContactPoint& contactPoint, int numWheelsOnGround) { btScalar j1=0.f; @@ -513,7 +520,7 @@ btScalar calcRollingFriction(btWheelContactPoint& contactPoint) btScalar vrel = contactPoint.m_frictionDirectionWorld.dot(vel); // calculate j that moves us to zero relative velocity - j1 = -vrel * contactPoint.m_jacDiagABInv; + j1 = -vrel * contactPoint.m_jacDiagABInv/btScalar(numWheelsOnGround); btSetMin(j1, maxImpulse); btSetMax(j1, -maxImpulse); @@ -567,7 +574,7 @@ void btRaycastVehicle::updateFriction(btScalar timeStep) const btTransform& wheelTrans = getWheelTransformWS( i ); btMatrix3x3 wheelBasis0 = wheelTrans.getBasis(); - m_axle[i] = btVector3( + m_axle[i] = -btVector3( wheelBasis0[0][m_indexRightAxis], wheelBasis0[1][m_indexRightAxis], wheelBasis0[2][m_indexRightAxis]); @@ -615,7 +622,8 @@ void btRaycastVehicle::updateFriction(btScalar timeStep) btScalar defaultRollingFrictionImpulse = 0.f; btScalar maxImpulse = wheelInfo.m_brake ? wheelInfo.m_brake : defaultRollingFrictionImpulse; btWheelContactPoint contactPt(m_chassisBody,groundObject,wheelInfo.m_raycastInfo.m_contactPointWS,m_forwardWS[wheel],maxImpulse); - rollingFriction = calcRollingFriction(contactPt); + btAssert(numWheelsOnGround > 0); + rollingFriction = calcRollingFriction(contactPt, numWheelsOnGround); } } diff --git a/thirdparty/bullet/BulletInverseDynamics/IDErrorMessages.hpp b/thirdparty/bullet/BulletInverseDynamics/IDErrorMessages.hpp index 1dc22f860a..1b3fd268a0 100644 --- a/thirdparty/bullet/BulletInverseDynamics/IDErrorMessages.hpp +++ b/thirdparty/bullet/BulletInverseDynamics/IDErrorMessages.hpp @@ -7,19 +7,19 @@ #if !defined(BT_ID_WO_BULLET) && !defined(BT_USE_INVERSE_DYNAMICS_WITH_BULLET2) #include "Bullet3Common/b3Logging.h" -#define error_message(...) b3Error(__VA_ARGS__) -#define warning_message(...) b3Warning(__VA_ARGS__) +#define bt_id_error_message(...) b3Error(__VA_ARGS__) +#define bt_id_warning_message(...) b3Warning(__VA_ARGS__) #define id_printf(...) b3Printf(__VA_ARGS__) #else // BT_ID_WO_BULLET #include <cstdio> /// print error message with file/line information -#define error_message(...) \ +#define bt_id_error_message(...) \ do { \ fprintf(stderr, "[Error:%s:%d] ", __INVDYN_FILE_WO_DIR__, __LINE__); \ fprintf(stderr, __VA_ARGS__); \ } while (0) /// print warning message with file/line information -#define warning_message(...) \ +#define bt_id_warning_message(...) \ do { \ fprintf(stderr, "[Warning:%s:%d] ", __INVDYN_FILE_WO_DIR__, __LINE__); \ fprintf(stderr, __VA_ARGS__); \ diff --git a/thirdparty/bullet/BulletInverseDynamics/IDMath.cpp b/thirdparty/bullet/BulletInverseDynamics/IDMath.cpp index 99fe20e492..d279d3435c 100644 --- a/thirdparty/bullet/BulletInverseDynamics/IDMath.cpp +++ b/thirdparty/bullet/BulletInverseDynamics/IDMath.cpp @@ -81,7 +81,7 @@ idScalar maxAbsMat3x(const mat3x &m) { void mul(const mat33 &a, const mat3x &b, mat3x *result) { if (b.cols() != result->cols()) { - error_message("size missmatch. b.cols()= %d, result->cols()= %d\n", + bt_id_error_message("size missmatch. b.cols()= %d, result->cols()= %d\n", static_cast<int>(b.cols()), static_cast<int>(result->cols())); abort(); } @@ -97,7 +97,7 @@ void mul(const mat33 &a, const mat3x &b, mat3x *result) { } void add(const mat3x &a, const mat3x &b, mat3x *result) { if (a.cols() != b.cols()) { - error_message("size missmatch. a.cols()= %d, b.cols()= %d\n", + bt_id_error_message("size missmatch. a.cols()= %d, b.cols()= %d\n", static_cast<int>(a.cols()), static_cast<int>(b.cols())); abort(); } @@ -109,7 +109,7 @@ void add(const mat3x &a, const mat3x &b, mat3x *result) { } void sub(const mat3x &a, const mat3x &b, mat3x *result) { if (a.cols() != b.cols()) { - error_message("size missmatch. a.cols()= %d, b.cols()= %d\n", + bt_id_error_message("size missmatch. a.cols()= %d, b.cols()= %d\n", static_cast<int>(a.cols()), static_cast<int>(b.cols())); abort(); } @@ -305,10 +305,10 @@ bool isValidInertiaMatrix(const mat33 &I, const int index, bool has_fixed_joint) // the determinant of the inertia tensor about the joint axis is almost // zero and can have a very small negative value. if (!isPositiveSemiDefiniteFuzzy(I)) { - error_message("invalid inertia matrix for body %d, not positive definite " + bt_id_error_message("invalid inertia matrix for body %d, not positive definite " "(fixed joint)\n", index); - error_message("matrix is:\n" + bt_id_error_message("matrix is:\n" "[%.20e %.20e %.20e;\n" "%.20e %.20e %.20e;\n" "%.20e %.20e %.20e]\n", @@ -321,8 +321,8 @@ bool isValidInertiaMatrix(const mat33 &I, const int index, bool has_fixed_joint) // check triangle inequality, must have I(i,i)+I(j,j)>=I(k,k) if (!has_fixed_joint) { if (I(0, 0) + I(1, 1) < I(2, 2)) { - error_message("invalid inertia tensor for body %d, I(0,0) + I(1,1) < I(2,2)\n", index); - error_message("matrix is:\n" + bt_id_error_message("invalid inertia tensor for body %d, I(0,0) + I(1,1) < I(2,2)\n", index); + bt_id_error_message("matrix is:\n" "[%.20e %.20e %.20e;\n" "%.20e %.20e %.20e;\n" "%.20e %.20e %.20e]\n", @@ -331,8 +331,8 @@ bool isValidInertiaMatrix(const mat33 &I, const int index, bool has_fixed_joint) return false; } if (I(0, 0) + I(1, 1) < I(2, 2)) { - error_message("invalid inertia tensor for body %d, I(0,0) + I(1,1) < I(2,2)\n", index); - error_message("matrix is:\n" + bt_id_error_message("invalid inertia tensor for body %d, I(0,0) + I(1,1) < I(2,2)\n", index); + bt_id_error_message("matrix is:\n" "[%.20e %.20e %.20e;\n" "%.20e %.20e %.20e;\n" "%.20e %.20e %.20e]\n", @@ -341,8 +341,8 @@ bool isValidInertiaMatrix(const mat33 &I, const int index, bool has_fixed_joint) return false; } if (I(1, 1) + I(2, 2) < I(0, 0)) { - error_message("invalid inertia tensor for body %d, I(1,1) + I(2,2) < I(0,0)\n", index); - error_message("matrix is:\n" + bt_id_error_message("invalid inertia tensor for body %d, I(1,1) + I(2,2) < I(0,0)\n", index); + bt_id_error_message("matrix is:\n" "[%.20e %.20e %.20e;\n" "%.20e %.20e %.20e;\n" "%.20e %.20e %.20e]\n", @@ -354,25 +354,25 @@ bool isValidInertiaMatrix(const mat33 &I, const int index, bool has_fixed_joint) // check positive/zero diagonal elements for (int i = 0; i < 3; i++) { if (I(i, i) < 0) { // accept zero - error_message("invalid inertia tensor, I(%d,%d)= %e <0\n", i, i, I(i, i)); + bt_id_error_message("invalid inertia tensor, I(%d,%d)= %e <0\n", i, i, I(i, i)); return false; } } // check symmetry if (BT_ID_FABS(I(1, 0) - I(0, 1)) > kIsZero) { - error_message("invalid inertia tensor for body %d I(1,0)!=I(0,1). I(1,0)-I(0,1)= " + bt_id_error_message("invalid inertia tensor for body %d I(1,0)!=I(0,1). I(1,0)-I(0,1)= " "%e\n", index, I(1, 0) - I(0, 1)); return false; } if (BT_ID_FABS(I(2, 0) - I(0, 2)) > kIsZero) { - error_message("invalid inertia tensor for body %d I(2,0)!=I(0,2). I(2,0)-I(0,2)= " + bt_id_error_message("invalid inertia tensor for body %d I(2,0)!=I(0,2). I(2,0)-I(0,2)= " "%e\n", index, I(2, 0) - I(0, 2)); return false; } if (BT_ID_FABS(I(1, 2) - I(2, 1)) > kIsZero) { - error_message("invalid inertia tensor body %d I(1,2)!=I(2,1). I(1,2)-I(2,1)= %e\n", index, + bt_id_error_message("invalid inertia tensor body %d I(1,2)!=I(2,1). I(1,2)-I(2,1)= %e\n", index, I(1, 2) - I(2, 1)); return false; } @@ -381,7 +381,7 @@ bool isValidInertiaMatrix(const mat33 &I, const int index, bool has_fixed_joint) bool isValidTransformMatrix(const mat33 &m) { #define print_mat(x) \ - error_message("matrix is [%e, %e, %e; %e, %e, %e; %e, %e, %e]\n", x(0, 0), x(0, 1), x(0, 2), \ + bt_id_error_message("matrix is [%e, %e, %e; %e, %e, %e; %e, %e, %e]\n", x(0, 0), x(0, 1), x(0, 2), \ x(1, 0), x(1, 1), x(1, 2), x(2, 0), x(2, 1), x(2, 2)) // check for unit length column vectors @@ -389,7 +389,7 @@ bool isValidTransformMatrix(const mat33 &m) { const idScalar length_minus_1 = BT_ID_FABS(m(0, i) * m(0, i) + m(1, i) * m(1, i) + m(2, i) * m(2, i) - 1.0); if (length_minus_1 > kAxisLengthEpsilon) { - error_message("Not a valid rotation matrix (column %d not unit length)\n" + bt_id_error_message("Not a valid rotation matrix (column %d not unit length)\n" "column = [%.18e %.18e %.18e]\n" "length-1.0= %.18e\n", i, m(0, i), m(1, i), m(2, i), length_minus_1); @@ -399,23 +399,23 @@ bool isValidTransformMatrix(const mat33 &m) { } // check for orthogonal column vectors if (BT_ID_FABS(m(0, 0) * m(0, 1) + m(1, 0) * m(1, 1) + m(2, 0) * m(2, 1)) > kAxisLengthEpsilon) { - error_message("Not a valid rotation matrix (columns 0 and 1 not orthogonal)\n"); + bt_id_error_message("Not a valid rotation matrix (columns 0 and 1 not orthogonal)\n"); print_mat(m); return false; } if (BT_ID_FABS(m(0, 0) * m(0, 2) + m(1, 0) * m(1, 2) + m(2, 0) * m(2, 2)) > kAxisLengthEpsilon) { - error_message("Not a valid rotation matrix (columns 0 and 2 not orthogonal)\n"); + bt_id_error_message("Not a valid rotation matrix (columns 0 and 2 not orthogonal)\n"); print_mat(m); return false; } if (BT_ID_FABS(m(0, 1) * m(0, 2) + m(1, 1) * m(1, 2) + m(2, 1) * m(2, 2)) > kAxisLengthEpsilon) { - error_message("Not a valid rotation matrix (columns 0 and 2 not orthogonal)\n"); + bt_id_error_message("Not a valid rotation matrix (columns 0 and 2 not orthogonal)\n"); print_mat(m); return false; } // check determinant (rotation not reflection) if (determinant(m) <= 0) { - error_message("Not a valid rotation matrix (determinant <=0)\n"); + bt_id_error_message("Not a valid rotation matrix (determinant <=0)\n"); print_mat(m); return false; } diff --git a/thirdparty/bullet/BulletInverseDynamics/MultiBodyTree.cpp b/thirdparty/bullet/BulletInverseDynamics/MultiBodyTree.cpp index c67588d49f..becfe0f4a2 100644 --- a/thirdparty/bullet/BulletInverseDynamics/MultiBodyTree.cpp +++ b/thirdparty/bullet/BulletInverseDynamics/MultiBodyTree.cpp @@ -83,11 +83,11 @@ int MultiBodyTree::numDoFs() const { return m_impl->m_num_dofs; } int MultiBodyTree::calculateInverseDynamics(const vecx &q, const vecx &u, const vecx &dot_u, vecx *joint_forces) { if (false == m_is_finalized) { - error_message("system has not been initialized\n"); + bt_id_error_message("system has not been initialized\n"); return -1; } if (-1 == m_impl->calculateInverseDynamics(q, u, dot_u, joint_forces)) { - error_message("error in inverse dynamics calculation\n"); + bt_id_error_message("error in inverse dynamics calculation\n"); return -1; } return 0; @@ -97,13 +97,13 @@ int MultiBodyTree::calculateMassMatrix(const vecx &q, const bool update_kinemati const bool initialize_matrix, const bool set_lower_triangular_matrix, matxx *mass_matrix) { if (false == m_is_finalized) { - error_message("system has not been initialized\n"); + bt_id_error_message("system has not been initialized\n"); return -1; } if (-1 == m_impl->calculateMassMatrix(q, update_kinematics, initialize_matrix, set_lower_triangular_matrix, mass_matrix)) { - error_message("error in mass matrix calculation\n"); + bt_id_error_message("error in mass matrix calculation\n"); return -1; } return 0; @@ -121,12 +121,12 @@ int MultiBodyTree::calculateKinematics(const vecx& q, const vecx& u, const vecx& setZero(m_impl->m_world_gravity); if (false == m_is_finalized) { - error_message("system has not been initialized\n"); + bt_id_error_message("system has not been initialized\n"); return -1; } if (-1 == m_impl->calculateKinematics(q, u, dot_u, MultiBodyTree::MultiBodyImpl::POSITION_VELOCITY_ACCELERATION)) { - error_message("error in kinematics calculation\n"); + bt_id_error_message("error in kinematics calculation\n"); return -1; } @@ -137,12 +137,12 @@ int MultiBodyTree::calculateKinematics(const vecx& q, const vecx& u, const vecx& int MultiBodyTree::calculatePositionKinematics(const vecx& q) { if (false == m_is_finalized) { - error_message("system has not been initialized\n"); + bt_id_error_message("system has not been initialized\n"); return -1; } if (-1 == m_impl->calculateKinematics(q, q, q, MultiBodyTree::MultiBodyImpl::POSITION_VELOCITY)) { - error_message("error in kinematics calculation\n"); + bt_id_error_message("error in kinematics calculation\n"); return -1; } return 0; @@ -150,12 +150,12 @@ int MultiBodyTree::calculatePositionKinematics(const vecx& q) { int MultiBodyTree::calculatePositionAndVelocityKinematics(const vecx& q, const vecx& u) { if (false == m_is_finalized) { - error_message("system has not been initialized\n"); + bt_id_error_message("system has not been initialized\n"); return -1; } if (-1 == m_impl->calculateKinematics(q, u, u, MultiBodyTree::MultiBodyImpl::POSITION_VELOCITY)) { - error_message("error in kinematics calculation\n"); + bt_id_error_message("error in kinematics calculation\n"); return -1; } return 0; @@ -165,12 +165,12 @@ int MultiBodyTree::calculatePositionAndVelocityKinematics(const vecx& q, const v #if (defined BT_ID_HAVE_MAT3X) && (defined BT_ID_WITH_JACOBIANS) int MultiBodyTree::calculateJacobians(const vecx& q, const vecx& u) { if (false == m_is_finalized) { - error_message("system has not been initialized\n"); + bt_id_error_message("system has not been initialized\n"); return -1; } if (-1 == m_impl->calculateJacobians(q, u, MultiBodyTree::MultiBodyImpl::POSITION_VELOCITY)) { - error_message("error in jacobian calculation\n"); + bt_id_error_message("error in jacobian calculation\n"); return -1; } return 0; @@ -178,12 +178,12 @@ int MultiBodyTree::calculateJacobians(const vecx& q, const vecx& u) { int MultiBodyTree::calculateJacobians(const vecx& q){ if (false == m_is_finalized) { - error_message("system has not been initialized\n"); + bt_id_error_message("system has not been initialized\n"); return -1; } if (-1 == m_impl->calculateJacobians(q, q, MultiBodyTree::MultiBodyImpl::POSITION_ONLY)) { - error_message("error in jacobian calculation\n"); + bt_id_error_message("error in jacobian calculation\n"); return -1; } return 0; @@ -214,7 +214,7 @@ int MultiBodyTree::addBody(int body_index, int parent_index, JointType joint_typ const vec3 &body_r_body_com, const mat33 &body_I_body, const int user_int, void *user_ptr) { if (body_index < 0) { - error_message("body index must be positive (got %d)\n", body_index); + bt_id_error_message("body index must be positive (got %d)\n", body_index); return -1; } vec3 body_axis_of_motion(body_axis_of_motion_); @@ -223,14 +223,14 @@ int MultiBodyTree::addBody(int body_index, int parent_index, JointType joint_typ case PRISMATIC: // check if axis is unit vector if (!isUnitVector(body_axis_of_motion)) { - warning_message( + bt_id_warning_message( "axis of motion not a unit axis ([%f %f %f]), will use normalized vector\n", body_axis_of_motion(0), body_axis_of_motion(1), body_axis_of_motion(2)); idScalar length = BT_ID_SQRT(BT_ID_POW(body_axis_of_motion(0), 2) + BT_ID_POW(body_axis_of_motion(1), 2) + BT_ID_POW(body_axis_of_motion(2), 2)); if (length < BT_ID_SQRT(std::numeric_limits<idScalar>::min())) { - error_message("axis of motion vector too short (%e)\n", length); + bt_id_error_message("axis of motion vector too short (%e)\n", length); return -1; } body_axis_of_motion = (1.0 / length) * body_axis_of_motion; @@ -241,14 +241,14 @@ int MultiBodyTree::addBody(int body_index, int parent_index, JointType joint_typ case FLOATING: break; default: - error_message("unknown joint type %d\n", joint_type); + bt_id_error_message("unknown joint type %d\n", joint_type); return -1; } // sanity check for mass properties. Zero mass is OK. if (mass < 0) { m_mass_parameters_are_valid = false; - error_message("Body %d has invalid mass %e\n", body_index, mass); + bt_id_error_message("Body %d has invalid mass %e\n", body_index, mass); if (!m_accept_invalid_mass_parameters) { return -1; } @@ -296,7 +296,7 @@ int MultiBodyTree::finalize() { const int &num_dofs = m_init_cache->numDoFs(); if(num_dofs<=0) { - error_message("Need num_dofs>=1, but num_dofs= %d\n", num_dofs); + bt_id_error_message("Need num_dofs>=1, but num_dofs= %d\n", num_dofs); //return -1; } @@ -331,6 +331,22 @@ int MultiBodyTree::finalize() { rigid_body.m_parent_pos_parent_body_ref = joint.m_parent_pos_parent_child_ref; rigid_body.m_joint_type = joint.m_type; + int user_int; + if (-1 == m_init_cache->getUserInt(index, &user_int)) { + return -1; + } + if (-1 == m_impl->setUserInt(index, user_int)) { + return -1; + } + + void* user_ptr; + if (-1 == m_init_cache->getUserPtr(index, &user_ptr)) { + return -1; + } + if (-1 == m_impl->setUserPtr(index, user_ptr)) { + return -1; + } + // Set joint Jacobians. Note that the dimension is always 3x1 here to avoid variable sized // matrices. switch (rigid_body.m_joint_type) { @@ -370,14 +386,14 @@ int MultiBodyTree::finalize() { rigid_body.m_Jac_JT(2) = 0.0; break; default: - error_message("unsupported joint type %d\n", rigid_body.m_joint_type); + bt_id_error_message("unsupported joint type %d\n", rigid_body.m_joint_type); return -1; } } // 4 assign degree of freedom indices & build per-joint-type index arrays if (-1 == m_impl->generateIndexSets()) { - error_message("generating index sets\n"); + bt_id_error_message("generating index sets\n"); return -1; } diff --git a/thirdparty/bullet/BulletInverseDynamics/details/IDLinearMathInterface.hpp b/thirdparty/bullet/BulletInverseDynamics/details/IDLinearMathInterface.hpp index 5bb4a33bdd..c179daeec6 100644 --- a/thirdparty/bullet/BulletInverseDynamics/details/IDLinearMathInterface.hpp +++ b/thirdparty/bullet/BulletInverseDynamics/details/IDLinearMathInterface.hpp @@ -49,9 +49,9 @@ inline mat33 operator*(const idScalar& s, const mat33& a) { return a * s; } class vecx : public btVectorX<idScalar> { public: - vecx(int size) : btVectorX(size) {} + vecx(int size) : btVectorX<idScalar>(size) {} const vecx& operator=(const btVectorX<idScalar>& rhs) { - *static_cast<btVectorX*>(this) = rhs; + *static_cast<btVectorX<idScalar>*>(this) = rhs; return *this; } @@ -78,7 +78,7 @@ inline vecx operator+(const vecx& a, const vecx& b) { vecx result(a.size()); // TODO: error handling for a.size() != b.size()?? if (a.size() != b.size()) { - error_message("size missmatch. a.size()= %d, b.size()= %d\n", a.size(), b.size()); + bt_id_error_message("size missmatch. a.size()= %d, b.size()= %d\n", a.size(), b.size()); abort(); } for (int i = 0; i < a.size(); i++) { @@ -92,7 +92,7 @@ inline vecx operator-(const vecx& a, const vecx& b) { vecx result(a.size()); // TODO: error handling for a.size() != b.size()?? if (a.size() != b.size()) { - error_message("size missmatch. a.size()= %d, b.size()= %d\n", a.size(), b.size()); + bt_id_error_message("size missmatch. a.size()= %d, b.size()= %d\n", a.size(), b.size()); abort(); } for (int i = 0; i < a.size(); i++) { @@ -121,7 +121,7 @@ public: } void operator=(const mat3x& rhs) { if (m_cols != rhs.m_cols) { - error_message("size missmatch, cols= %d but rhs.cols= %d\n", cols(), rhs.cols()); + bt_id_error_message("size missmatch, cols= %d but rhs.cols= %d\n", cols(), rhs.cols()); abort(); } for(int i=0;i<rows();i++) { @@ -139,7 +139,7 @@ public: inline vec3 operator*(const mat3x& a, const vecx& b) { vec3 result; if (a.cols() != b.size()) { - error_message("size missmatch. a.cols()= %d, b.size()= %d\n", a.cols(), b.size()); + bt_id_error_message("size missmatch. a.cols()= %d, b.size()= %d\n", a.cols(), b.size()); abort(); } result(0)=0.0; diff --git a/thirdparty/bullet/BulletInverseDynamics/details/IDMatVec.hpp b/thirdparty/bullet/BulletInverseDynamics/details/IDMatVec.hpp index 4d3f6c87e9..c89db5e123 100644 --- a/thirdparty/bullet/BulletInverseDynamics/details/IDMatVec.hpp +++ b/thirdparty/bullet/BulletInverseDynamics/details/IDMatVec.hpp @@ -123,7 +123,7 @@ public: }; void operator=(const mat3x& rhs) { if (m_cols != rhs.m_cols) { - error_message("size missmatch, cols= %d but rhs.cols= %d\n", cols(), rhs.cols()); + bt_id_error_message("size missmatch, cols= %d but rhs.cols= %d\n", cols(), rhs.cols()); abort(); } for(int i=0;i<3*m_cols;i++) { @@ -336,7 +336,7 @@ inline vec3 operator/(const vec3& a, const idScalar& s) { inline const vecx& vecx::operator=(const vecx& rhs) { if (size() != rhs.size()) { - error_message("size missmatch, size()= %d but rhs.size()= %d\n", size(), rhs.size()); + bt_id_error_message("size missmatch, size()= %d but rhs.size()= %d\n", size(), rhs.size()); abort(); } if (&rhs != this) { @@ -356,7 +356,7 @@ inline vecx operator+(const vecx& a, const vecx& b) { vecx result(a.size()); // TODO: error handling for a.size() != b.size()?? if (a.size() != b.size()) { - error_message("size missmatch. a.size()= %d, b.size()= %d\n", a.size(), b.size()); + bt_id_error_message("size missmatch. a.size()= %d, b.size()= %d\n", a.size(), b.size()); abort(); } for (int i = 0; i < a.size(); i++) { @@ -369,7 +369,7 @@ inline vecx operator-(const vecx& a, const vecx& b) { vecx result(a.size()); // TODO: error handling for a.size() != b.size()?? if (a.size() != b.size()) { - error_message("size missmatch. a.size()= %d, b.size()= %d\n", a.size(), b.size()); + bt_id_error_message("size missmatch. a.size()= %d, b.size()= %d\n", a.size(), b.size()); abort(); } for (int i = 0; i < a.size(); i++) { @@ -389,7 +389,7 @@ inline vecx operator/(const vecx& a, const idScalar& s) { inline vec3 operator*(const mat3x& a, const vecx& b) { vec3 result; if (a.cols() != b.size()) { - error_message("size missmatch. a.cols()= %d, b.size()= %d\n", a.cols(), b.size()); + bt_id_error_message("size missmatch. a.cols()= %d, b.size()= %d\n", a.cols(), b.size()); abort(); } result(0)=0.0; diff --git a/thirdparty/bullet/BulletInverseDynamics/details/MultiBodyTreeImpl.cpp b/thirdparty/bullet/BulletInverseDynamics/details/MultiBodyTreeImpl.cpp index b35c55df61..e8563238c3 100644 --- a/thirdparty/bullet/BulletInverseDynamics/details/MultiBodyTreeImpl.cpp +++ b/thirdparty/bullet/BulletInverseDynamics/details/MultiBodyTreeImpl.cpp @@ -80,7 +80,7 @@ int MultiBodyTree::MultiBodyImpl::bodyNumDoFs(const JointType &type) const { case FLOATING: return 6; } - error_message("unknown joint type %d\n", type); + bt_id_error_message("unknown joint type %d\n", type); return 0; } @@ -136,13 +136,13 @@ int MultiBodyTree::MultiBodyImpl::generateIndexSets() { q_index += 6; break; default: - error_message("unsupported joint type %d\n", body.m_joint_type); + bt_id_error_message("unsupported joint type %d\n", body.m_joint_type); return -1; } } // sanity check if (q_index != m_num_dofs) { - error_message("internal error, q_index= %d but num_dofs %d\n", q_index, m_num_dofs); + bt_id_error_message("internal error, q_index= %d but num_dofs %d\n", q_index, m_num_dofs); return -1; } @@ -155,10 +155,10 @@ int MultiBodyTree::MultiBodyImpl::generateIndexSets() { } else { if (-1 == parent) { // multiple bodies are directly linked to the environment, ie, not a single root - error_message("building index sets parent(%zu)= -1 (multiple roots)\n", child); + bt_id_error_message("building index sets parent(%zu)= -1 (multiple roots)\n", child); } else { // should never happen - error_message( + bt_id_error_message( "building index sets. parent_index[%zu]= %d, but m_parent_index.size()= %d\n", child, parent, static_cast<int>(m_parent_index.size())); } @@ -234,7 +234,7 @@ int MultiBodyTree::MultiBodyImpl::calculateInverseDynamics(const vecx &q, const const vecx &dot_u, vecx *joint_forces) { if (q.size() != m_num_dofs || u.size() != m_num_dofs || dot_u.size() != m_num_dofs || joint_forces->size() != m_num_dofs) { - error_message("wrong vector dimension. system has %d DOFs,\n" + bt_id_error_message("wrong vector dimension. system has %d DOFs,\n" "but dim(q)= %d, dim(u)= %d, dim(dot_u)= %d, dim(joint_forces)= %d\n", m_num_dofs, static_cast<int>(q.size()), static_cast<int>(u.size()), static_cast<int>(dot_u.size()), static_cast<int>(joint_forces->size())); @@ -242,7 +242,7 @@ int MultiBodyTree::MultiBodyImpl::calculateInverseDynamics(const vecx &q, const } // 1. relative kinematics if(-1 == calculateKinematics(q,u,dot_u, POSITION_VELOCITY_ACCELERATION)) { - error_message("error in calculateKinematics\n"); + bt_id_error_message("error in calculateKinematics\n"); return -1; } // 2. update contributions to equations of motion for every body. @@ -322,14 +322,14 @@ int MultiBodyTree::MultiBodyImpl::calculateInverseDynamics(const vecx &q, const int MultiBodyTree::MultiBodyImpl::calculateKinematics(const vecx &q, const vecx &u, const vecx& dot_u, const KinUpdateType type) { if (q.size() != m_num_dofs || u.size() != m_num_dofs || dot_u.size() != m_num_dofs ) { - error_message("wrong vector dimension. system has %d DOFs,\n" + bt_id_error_message("wrong vector dimension. system has %d DOFs,\n" "but dim(q)= %d, dim(u)= %d, dim(dot_u)= %d\n", m_num_dofs, static_cast<int>(q.size()), static_cast<int>(u.size()), static_cast<int>(dot_u.size())); return -1; } if(type != POSITION_ONLY && type != POSITION_VELOCITY && type != POSITION_VELOCITY_ACCELERATION) { - error_message("invalid type %d\n", type); + bt_id_error_message("invalid type %d\n", type); return -1; } @@ -516,13 +516,13 @@ void MultiBodyTree::MultiBodyImpl::addRelativeJacobianComponent(RigidBody&body) int MultiBodyTree::MultiBodyImpl::calculateJacobians(const vecx& q, const vecx& u, const KinUpdateType type) { if (q.size() != m_num_dofs || u.size() != m_num_dofs) { - error_message("wrong vector dimension. system has %d DOFs,\n" + bt_id_error_message("wrong vector dimension. system has %d DOFs,\n" "but dim(q)= %d, dim(u)= %d\n", m_num_dofs, static_cast<int>(q.size()), static_cast<int>(u.size())); return -1; } if(type != POSITION_ONLY && type != POSITION_VELOCITY) { - error_message("invalid type %d\n", type); + bt_id_error_message("invalid type %d\n", type); return -1; } @@ -606,7 +606,7 @@ static inline int jointNumDoFs(const JointType &type) { return 6; } // this should never happen - error_message("invalid joint type\n"); + bt_id_error_message("invalid joint type\n"); // TODO add configurable abort/crash function abort(); return 0; @@ -626,7 +626,7 @@ int MultiBodyTree::MultiBodyImpl::calculateMassMatrix(const vecx &q, const bool if (q.size() != m_num_dofs || mass_matrix->rows() != m_num_dofs || mass_matrix->cols() != m_num_dofs) { - error_message("Dimension error. System has %d DOFs,\n" + bt_id_error_message("Dimension error. System has %d DOFs,\n" "but dim(q)= %d, dim(mass_matrix)= %d x %d\n", m_num_dofs, static_cast<int>(q.size()), static_cast<int>(mass_matrix->rows()), static_cast<int>(mass_matrix->cols())); @@ -734,7 +734,7 @@ int MultiBodyTree::MultiBodyImpl::calculateMassMatrix(const vecx &q, const bool // 1. for multi-dof joints, rest of the dofs of this body for (int row = col - 1; row >= q_index_min; row--) { if (FLOATING != body.m_joint_type) { - error_message("??\n"); + bt_id_error_message("??\n"); return -1; } setSixDoFJacobians(row - q_index_min, Jac_JR, Jac_JT); @@ -788,7 +788,7 @@ int MultiBodyTree::MultiBodyImpl::calculateMassMatrix(const vecx &q, const bool #define CHECK_IF_BODY_INDEX_IS_VALID(index) \ do { \ if (index < 0 || index >= m_num_bodies) { \ - error_message("invalid index %d (num_bodies= %d)\n", index, m_num_bodies); \ + bt_id_error_message("invalid index %d (num_bodies= %d)\n", index, m_num_bodies); \ return -1; \ } \ } while (0) diff --git a/thirdparty/bullet/BulletInverseDynamics/details/MultiBodyTreeInitCache.cpp b/thirdparty/bullet/BulletInverseDynamics/details/MultiBodyTreeInitCache.cpp index 47b4ab3890..e9511b7076 100644 --- a/thirdparty/bullet/BulletInverseDynamics/details/MultiBodyTreeInitCache.cpp +++ b/thirdparty/bullet/BulletInverseDynamics/details/MultiBodyTreeInitCache.cpp @@ -29,13 +29,13 @@ int MultiBodyTree::InitCache::addBody(const int body_index, const int parent_ind m_num_dofs += 6; break; default: - error_message("unknown joint type %d\n", joint_type); + bt_id_error_message("unknown joint type %d\n", joint_type); return -1; } if(-1 == parent_index) { if(m_root_index>=0) { - error_message("trying to add body %d as root, but already added %d as root body\n", + bt_id_error_message("trying to add body %d as root, but already added %d as root body\n", body_index, m_root_index); return -1; } @@ -63,7 +63,7 @@ int MultiBodyTree::InitCache::addBody(const int body_index, const int parent_ind } int MultiBodyTree::InitCache::getInertiaData(const int index, InertiaData* inertia) const { if (index < 0 || index > static_cast<int>(m_inertias.size())) { - error_message("index out of range\n"); + bt_id_error_message("index out of range\n"); return -1; } @@ -73,7 +73,7 @@ int MultiBodyTree::InitCache::getInertiaData(const int index, InertiaData* inert int MultiBodyTree::InitCache::getUserInt(const int index, int* user_int) const { if (index < 0 || index > static_cast<int>(m_user_int.size())) { - error_message("index out of range\n"); + bt_id_error_message("index out of range\n"); return -1; } *user_int = m_user_int[index]; @@ -82,7 +82,7 @@ int MultiBodyTree::InitCache::getUserInt(const int index, int* user_int) const { int MultiBodyTree::InitCache::getUserPtr(const int index, void** user_ptr) const { if (index < 0 || index > static_cast<int>(m_user_ptr.size())) { - error_message("index out of range\n"); + bt_id_error_message("index out of range\n"); return -1; } *user_ptr = m_user_ptr[index]; @@ -91,7 +91,7 @@ int MultiBodyTree::InitCache::getUserPtr(const int index, void** user_ptr) const int MultiBodyTree::InitCache::getJointData(const int index, JointData* joint) const { if (index < 0 || index > static_cast<int>(m_joints.size())) { - error_message("index out of range\n"); + bt_id_error_message("index out of range\n"); return -1; } *joint = m_joints[index]; diff --git a/thirdparty/bullet/BulletSoftBody/btSoftMultiBodyDynamicsWorld.cpp b/thirdparty/bullet/BulletSoftBody/btSoftMultiBodyDynamicsWorld.cpp index 4e76dca9db..6facce4e86 100644 --- a/thirdparty/bullet/BulletSoftBody/btSoftMultiBodyDynamicsWorld.cpp +++ b/thirdparty/bullet/BulletSoftBody/btSoftMultiBodyDynamicsWorld.cpp @@ -157,7 +157,7 @@ void btSoftMultiBodyDynamicsWorld::removeCollisionObject(btCollisionObject* coll void btSoftMultiBodyDynamicsWorld::debugDrawWorld() { - btDiscreteDynamicsWorld::debugDrawWorld(); + btMultiBodyDynamicsWorld::debugDrawWorld(); if (getDebugDrawer()) { @@ -357,10 +357,14 @@ void btSoftMultiBodyDynamicsWorld::serialize(btSerializer* serializer) serializeSoftBodies(serializer); + serializeMultiBodies(serializer); + serializeRigidBodies(serializer); serializeCollisionObjects(serializer); + serializeContactManifolds(serializer); + serializer->finishSerialization(); } diff --git a/thirdparty/bullet/LinearMath/TaskScheduler/btTaskScheduler.cpp b/thirdparty/bullet/LinearMath/TaskScheduler/btTaskScheduler.cpp new file mode 100644 index 0000000000..49510d1660 --- /dev/null +++ b/thirdparty/bullet/LinearMath/TaskScheduler/btTaskScheduler.cpp @@ -0,0 +1,802 @@ + +#include "LinearMath/btMinMax.h" +#include "LinearMath/btAlignedObjectArray.h" +#include "LinearMath/btThreads.h" +#include "LinearMath/btQuickprof.h" +#include <stdio.h> +#include <algorithm> + + + +#if BT_THREADSAFE + +#include "btThreadSupportInterface.h" + +#if defined( _WIN32 ) + +#define WIN32_LEAN_AND_MEAN + +#include <windows.h> + +#endif + + +typedef unsigned long long btU64; +static const int kCacheLineSize = 64; + +void btSpinPause() +{ +#if defined( _WIN32 ) + YieldProcessor(); +#endif +} + + +struct WorkerThreadStatus +{ + enum Type + { + kInvalid, + kWaitingForWork, + kWorking, + kSleeping, + }; +}; + + +ATTRIBUTE_ALIGNED64(class) WorkerThreadDirectives +{ + static const int kMaxThreadCount = BT_MAX_THREAD_COUNT; + // directives for all worker threads packed into a single cacheline + char m_threadDirs[kMaxThreadCount]; + +public: + enum Type + { + kInvalid, + kGoToSleep, // go to sleep + kStayAwakeButIdle, // wait for not checking job queue + kScanForJobs, // actively scan job queue for jobs + }; + WorkerThreadDirectives() + { + for ( int i = 0; i < kMaxThreadCount; ++i ) + { + m_threadDirs[ i ] = 0; + } + } + + Type getDirective(int threadId) + { + btAssert(threadId < kMaxThreadCount); + return static_cast<Type>(m_threadDirs[threadId]); + } + + void setDirectiveByRange(int threadBegin, int threadEnd, Type dir) + { + btAssert( threadBegin < threadEnd ); + btAssert( threadEnd <= kMaxThreadCount ); + char dirChar = static_cast<char>(dir); + for ( int i = threadBegin; i < threadEnd; ++i ) + { + m_threadDirs[ i ] = dirChar; + } + } +}; + +class JobQueue; + +ATTRIBUTE_ALIGNED64(struct) ThreadLocalStorage +{ + int m_threadId; + WorkerThreadStatus::Type m_status; + int m_numJobsFinished; + btSpinMutex m_mutex; + btScalar m_sumResult; + WorkerThreadDirectives * m_directive; + JobQueue* m_queue; + btClock* m_clock; + unsigned int m_cooldownTime; +}; + + +struct IJob +{ + virtual void executeJob(int threadId) = 0; +}; + +class ParallelForJob : public IJob +{ + const btIParallelForBody* m_body; + int m_begin; + int m_end; + +public: + ParallelForJob( int iBegin, int iEnd, const btIParallelForBody& body ) + { + m_body = &body; + m_begin = iBegin; + m_end = iEnd; + } + virtual void executeJob(int threadId) BT_OVERRIDE + { + BT_PROFILE( "executeJob" ); + + // call the functor body to do the work + m_body->forLoop( m_begin, m_end ); + } +}; + + +class ParallelSumJob : public IJob +{ + const btIParallelSumBody* m_body; + ThreadLocalStorage* m_threadLocalStoreArray; + int m_begin; + int m_end; + +public: + ParallelSumJob( int iBegin, int iEnd, const btIParallelSumBody& body, ThreadLocalStorage* tls ) + { + m_body = &body; + m_threadLocalStoreArray = tls; + m_begin = iBegin; + m_end = iEnd; + } + virtual void executeJob( int threadId ) BT_OVERRIDE + { + BT_PROFILE( "executeJob" ); + + // call the functor body to do the work + btScalar val = m_body->sumLoop( m_begin, m_end ); +#if BT_PARALLEL_SUM_DETERMINISTISM + // by truncating bits of the result, we can make the parallelSum deterministic (at the expense of precision) + const float TRUNC_SCALE = float(1<<19); + val = floor(val*TRUNC_SCALE+0.5f)/TRUNC_SCALE; // truncate some bits +#endif + m_threadLocalStoreArray[threadId].m_sumResult += val; + } +}; + + +ATTRIBUTE_ALIGNED64(class) JobQueue +{ + btThreadSupportInterface* m_threadSupport; + btCriticalSection* m_queueLock; + btSpinMutex m_mutex; + + btAlignedObjectArray<IJob*> m_jobQueue; + char* m_jobMem; + int m_jobMemSize; + bool m_queueIsEmpty; + int m_tailIndex; + int m_headIndex; + int m_allocSize; + bool m_useSpinMutex; + btAlignedObjectArray<JobQueue*> m_neighborContexts; + char m_cachePadding[kCacheLineSize]; // prevent false sharing + + void freeJobMem() + { + if ( m_jobMem ) + { + // free old + btAlignedFree(m_jobMem); + m_jobMem = NULL; + } + } + void resizeJobMem(int newSize) + { + if (newSize > m_jobMemSize) + { + freeJobMem(); + m_jobMem = static_cast<char*>(btAlignedAlloc(newSize, kCacheLineSize)); + m_jobMemSize = newSize; + } + } + +public: + + JobQueue() + { + m_jobMem = NULL; + m_jobMemSize = 0; + m_threadSupport = NULL; + m_queueLock = NULL; + m_headIndex = 0; + m_tailIndex = 0; + m_useSpinMutex = false; + } + ~JobQueue() + { + exit(); + } + void exit() + { + freeJobMem(); + if (m_queueLock && m_threadSupport) + { + m_threadSupport->deleteCriticalSection(m_queueLock); + m_queueLock = NULL; + m_threadSupport = 0; + } + } + + void init(btThreadSupportInterface* threadSup, btAlignedObjectArray<JobQueue>* contextArray) + { + m_threadSupport = threadSup; + if (threadSup) + { + m_queueLock = m_threadSupport->createCriticalSection(); + } + setupJobStealing(contextArray, contextArray->size()); + } + void setupJobStealing(btAlignedObjectArray<JobQueue>* contextArray, int numActiveContexts) + { + btAlignedObjectArray<JobQueue>& contexts = *contextArray; + int selfIndex = 0; + for (int i = 0; i < contexts.size(); ++i) + { + if ( this == &contexts[ i ] ) + { + selfIndex = i; + break; + } + } + int numNeighbors = btMin(2, contexts.size() - 1); + int neighborOffsets[ ] = {-1, 1, -2, 2, -3, 3}; + int numOffsets = sizeof(neighborOffsets)/sizeof(neighborOffsets[0]); + m_neighborContexts.reserve( numNeighbors ); + m_neighborContexts.resizeNoInitialize(0); + for (int i = 0; i < numOffsets && m_neighborContexts.size() < numNeighbors; i++) + { + int neighborIndex = selfIndex + neighborOffsets[i]; + if ( neighborIndex >= 0 && neighborIndex < numActiveContexts) + { + m_neighborContexts.push_back( &contexts[ neighborIndex ] ); + } + } + } + + bool isQueueEmpty() const {return m_queueIsEmpty;} + void lockQueue() + { + if ( m_useSpinMutex ) + { + m_mutex.lock(); + } + else + { + m_queueLock->lock(); + } + } + void unlockQueue() + { + if ( m_useSpinMutex ) + { + m_mutex.unlock(); + } + else + { + m_queueLock->unlock(); + } + } + void clearQueue(int jobCount, int jobSize) + { + lockQueue(); + m_headIndex = 0; + m_tailIndex = 0; + m_allocSize = 0; + m_queueIsEmpty = true; + int jobBufSize = jobSize * jobCount; + // make sure we have enough memory allocated to store jobs + if ( jobBufSize > m_jobMemSize ) + { + resizeJobMem( jobBufSize ); + } + // make sure job queue is big enough + if ( jobCount > m_jobQueue.capacity() ) + { + m_jobQueue.reserve( jobCount ); + } + unlockQueue(); + m_jobQueue.resizeNoInitialize( 0 ); + } + void* allocJobMem(int jobSize) + { + btAssert(m_jobMemSize >= (m_allocSize + jobSize)); + void* jobMem = &m_jobMem[m_allocSize]; + m_allocSize += jobSize; + return jobMem; + } + void submitJob( IJob* job ) + { + btAssert( reinterpret_cast<char*>( job ) >= &m_jobMem[ 0 ] && reinterpret_cast<char*>( job ) < &m_jobMem[ 0 ] + m_allocSize ); + m_jobQueue.push_back( job ); + lockQueue(); + m_tailIndex++; + m_queueIsEmpty = false; + unlockQueue(); + } + IJob* consumeJobFromOwnQueue() + { + if ( m_queueIsEmpty ) + { + // lock free path. even if this is taken erroneously it isn't harmful + return NULL; + } + IJob* job = NULL; + lockQueue(); + if ( !m_queueIsEmpty ) + { + job = m_jobQueue[ m_headIndex++ ]; + btAssert( reinterpret_cast<char*>( job ) >= &m_jobMem[ 0 ] && reinterpret_cast<char*>( job ) < &m_jobMem[ 0 ] + m_allocSize ); + if ( m_headIndex == m_tailIndex ) + { + m_queueIsEmpty = true; + } + } + unlockQueue(); + return job; + } + IJob* consumeJob() + { + if (IJob* job = consumeJobFromOwnQueue()) + { + return job; + } + // own queue is empty, try to steal from neighbor + for (int i = 0; i < m_neighborContexts.size(); ++i) + { + JobQueue* otherContext = m_neighborContexts[ i ]; + if ( IJob* job = otherContext->consumeJobFromOwnQueue() ) + { + return job; + } + } + return NULL; + } +}; + + +static void WorkerThreadFunc( void* userPtr ) +{ + BT_PROFILE( "WorkerThreadFunc" ); + ThreadLocalStorage* localStorage = (ThreadLocalStorage*) userPtr; + JobQueue* jobQueue = localStorage->m_queue; + + bool shouldSleep = false; + int threadId = localStorage->m_threadId; + while (! shouldSleep) + { + // do work + localStorage->m_mutex.lock(); + while ( IJob* job = jobQueue->consumeJob() ) + { + localStorage->m_status = WorkerThreadStatus::kWorking; + job->executeJob( threadId ); + localStorage->m_numJobsFinished++; + } + localStorage->m_status = WorkerThreadStatus::kWaitingForWork; + localStorage->m_mutex.unlock(); + btU64 clockStart = localStorage->m_clock->getTimeMicroseconds(); + // while queue is empty, + while (jobQueue->isQueueEmpty()) + { + // todo: spin wait a bit to avoid hammering the empty queue + btSpinPause(); + if ( localStorage->m_directive->getDirective(threadId) == WorkerThreadDirectives::kGoToSleep ) + { + shouldSleep = true; + break; + } + // if jobs are incoming, + if ( localStorage->m_directive->getDirective( threadId ) == WorkerThreadDirectives::kScanForJobs ) + { + clockStart = localStorage->m_clock->getTimeMicroseconds(); // reset clock + } + else + { + for ( int i = 0; i < 50; ++i ) + { + btSpinPause(); + btSpinPause(); + btSpinPause(); + btSpinPause(); + if (localStorage->m_directive->getDirective( threadId ) == WorkerThreadDirectives::kScanForJobs || !jobQueue->isQueueEmpty()) + { + break; + } + } + // if no jobs incoming and queue has been empty for the cooldown time, sleep + btU64 timeElapsed = localStorage->m_clock->getTimeMicroseconds() - clockStart; + if (timeElapsed > localStorage->m_cooldownTime) + { + shouldSleep = true; + break; + } + } + } + } + { + BT_PROFILE("sleep"); + // go sleep + localStorage->m_mutex.lock(); + localStorage->m_status = WorkerThreadStatus::kSleeping; + localStorage->m_mutex.unlock(); + } +} + + +class btTaskSchedulerDefault : public btITaskScheduler +{ + btThreadSupportInterface* m_threadSupport; + WorkerThreadDirectives* m_workerDirective; + btAlignedObjectArray<JobQueue> m_jobQueues; + btAlignedObjectArray<JobQueue*> m_perThreadJobQueues; + btAlignedObjectArray<ThreadLocalStorage> m_threadLocalStorage; + btSpinMutex m_antiNestingLock; // prevent nested parallel-for + btClock m_clock; + int m_numThreads; + int m_numWorkerThreads; + int m_numActiveJobQueues; + int m_maxNumThreads; + int m_numJobs; + static const int kFirstWorkerThreadId = 1; +public: + + btTaskSchedulerDefault() : btITaskScheduler("ThreadSupport") + { + m_threadSupport = NULL; + m_workerDirective = NULL; + } + + virtual ~btTaskSchedulerDefault() + { + waitForWorkersToSleep(); + + for ( int i = 0; i < m_jobQueues.size(); ++i ) + { + m_jobQueues[i].exit(); + } + + if (m_threadSupport) + { + delete m_threadSupport; + m_threadSupport = NULL; + } + if (m_workerDirective) + { + btAlignedFree(m_workerDirective); + m_workerDirective = NULL; + } + } + + void init() + { + btThreadSupportInterface::ConstructionInfo constructionInfo( "TaskScheduler", WorkerThreadFunc ); + m_threadSupport = btThreadSupportInterface::create( constructionInfo ); + m_workerDirective = static_cast<WorkerThreadDirectives*>(btAlignedAlloc(sizeof(*m_workerDirective), 64)); + + m_numWorkerThreads = m_threadSupport->getNumWorkerThreads(); + m_maxNumThreads = m_threadSupport->getNumWorkerThreads() + 1; + m_numThreads = m_maxNumThreads; + // ideal to have one job queue for each physical processor (except for the main thread which needs no queue) + int numThreadsPerQueue = m_threadSupport->getLogicalToPhysicalCoreRatio(); + int numJobQueues = (numThreadsPerQueue == 1) ? (m_maxNumThreads-1) : (m_maxNumThreads / numThreadsPerQueue); + m_jobQueues.resize(numJobQueues); + m_numActiveJobQueues = numJobQueues; + for ( int i = 0; i < m_jobQueues.size(); ++i ) + { + m_jobQueues[i].init( m_threadSupport, &m_jobQueues ); + } + m_perThreadJobQueues.resize(m_numThreads); + for ( int i = 0; i < m_numThreads; i++ ) + { + JobQueue* jq = NULL; + // only worker threads get a job queue + if (i > 0) + { + if (numThreadsPerQueue == 1) + { + // one queue per worker thread + jq = &m_jobQueues[ i - kFirstWorkerThreadId ]; + } + else + { + // 2 threads share each queue + jq = &m_jobQueues[ i / numThreadsPerQueue ]; + } + } + m_perThreadJobQueues[i] = jq; + } + m_threadLocalStorage.resize(m_numThreads); + for ( int i = 0; i < m_numThreads; i++ ) + { + ThreadLocalStorage& storage = m_threadLocalStorage[i]; + storage.m_threadId = i; + storage.m_directive = m_workerDirective; + storage.m_status = WorkerThreadStatus::kSleeping; + storage.m_cooldownTime = 100; // 100 microseconds, threads go to sleep after this long if they have nothing to do + storage.m_clock = &m_clock; + storage.m_queue = m_perThreadJobQueues[i]; + } + setWorkerDirectives( WorkerThreadDirectives::kGoToSleep ); // no work for them yet + setNumThreads( m_threadSupport->getCacheFriendlyNumThreads() ); + } + + void setWorkerDirectives(WorkerThreadDirectives::Type dir) + { + m_workerDirective->setDirectiveByRange(kFirstWorkerThreadId, m_numThreads, dir); + } + + virtual int getMaxNumThreads() const BT_OVERRIDE + { + return m_maxNumThreads; + } + + virtual int getNumThreads() const BT_OVERRIDE + { + return m_numThreads; + } + + virtual void setNumThreads( int numThreads ) BT_OVERRIDE + { + m_numThreads = btMax( btMin(numThreads, int(m_maxNumThreads)), 1 ); + m_numWorkerThreads = m_numThreads - 1; + m_numActiveJobQueues = 0; + // if there is at least 1 worker, + if ( m_numWorkerThreads > 0 ) + { + // re-setup job stealing between queues to avoid attempting to steal from an inactive job queue + JobQueue* lastActiveContext = m_perThreadJobQueues[ m_numThreads - 1 ]; + int iLastActiveContext = lastActiveContext - &m_jobQueues[0]; + m_numActiveJobQueues = iLastActiveContext + 1; + for ( int i = 0; i < m_jobQueues.size(); ++i ) + { + m_jobQueues[ i ].setupJobStealing( &m_jobQueues, m_numActiveJobQueues ); + } + } + m_workerDirective->setDirectiveByRange(m_numThreads, BT_MAX_THREAD_COUNT, WorkerThreadDirectives::kGoToSleep); + } + + void waitJobs() + { + BT_PROFILE( "waitJobs" ); + // have the main thread work until the job queues are empty + int numMainThreadJobsFinished = 0; + for ( int i = 0; i < m_numActiveJobQueues; ++i ) + { + while ( IJob* job = m_jobQueues[i].consumeJob() ) + { + job->executeJob( 0 ); + numMainThreadJobsFinished++; + } + } + + // done with jobs for now, tell workers to rest (but not sleep) + setWorkerDirectives( WorkerThreadDirectives::kStayAwakeButIdle ); + + btU64 clockStart = m_clock.getTimeMicroseconds(); + // wait for workers to finish any jobs in progress + while ( true ) + { + int numWorkerJobsFinished = 0; + for ( int iThread = kFirstWorkerThreadId; iThread < m_numThreads; ++iThread ) + { + ThreadLocalStorage* storage = &m_threadLocalStorage[iThread]; + storage->m_mutex.lock(); + numWorkerJobsFinished += storage->m_numJobsFinished; + storage->m_mutex.unlock(); + } + if (numWorkerJobsFinished + numMainThreadJobsFinished == m_numJobs) + { + break; + } + btU64 timeElapsed = m_clock.getTimeMicroseconds() - clockStart; + btAssert(timeElapsed < 1000); + if (timeElapsed > 100000) + { + break; + } + btSpinPause(); + } + } + + void wakeWorkers(int numWorkersToWake) + { + BT_PROFILE( "wakeWorkers" ); + btAssert( m_workerDirective->getDirective(1) == WorkerThreadDirectives::kScanForJobs ); + int numDesiredWorkers = btMin(numWorkersToWake, m_numWorkerThreads); + int numActiveWorkers = 0; + for ( int iWorker = 0; iWorker < m_numWorkerThreads; ++iWorker ) + { + // note this count of active workers is not necessarily totally reliable, because a worker thread could be + // just about to put itself to sleep. So we may on occasion fail to wake up all the workers. It should be rare. + ThreadLocalStorage& storage = m_threadLocalStorage[ kFirstWorkerThreadId + iWorker ]; + if (storage.m_status != WorkerThreadStatus::kSleeping) + { + numActiveWorkers++; + } + } + for ( int iWorker = 0; iWorker < m_numWorkerThreads && numActiveWorkers < numDesiredWorkers; ++iWorker ) + { + ThreadLocalStorage& storage = m_threadLocalStorage[ kFirstWorkerThreadId + iWorker ]; + if (storage.m_status == WorkerThreadStatus::kSleeping) + { + m_threadSupport->runTask( iWorker, &storage ); + numActiveWorkers++; + } + } + } + + void waitForWorkersToSleep() + { + BT_PROFILE( "waitForWorkersToSleep" ); + setWorkerDirectives( WorkerThreadDirectives::kGoToSleep ); + m_threadSupport->waitForAllTasks(); + for ( int i = kFirstWorkerThreadId; i < m_numThreads; i++ ) + { + ThreadLocalStorage& storage = m_threadLocalStorage[i]; + btAssert( storage.m_status == WorkerThreadStatus::kSleeping ); + } + } + + virtual void sleepWorkerThreadsHint() BT_OVERRIDE + { + BT_PROFILE( "sleepWorkerThreadsHint" ); + // hint the task scheduler that we may not be using these threads for a little while + setWorkerDirectives( WorkerThreadDirectives::kGoToSleep ); + } + + void prepareWorkerThreads() + { + for ( int i = kFirstWorkerThreadId; i < m_numThreads; ++i ) + { + ThreadLocalStorage& storage = m_threadLocalStorage[i]; + storage.m_mutex.lock(); + storage.m_numJobsFinished = 0; + storage.m_mutex.unlock(); + } + setWorkerDirectives( WorkerThreadDirectives::kScanForJobs ); + } + + virtual void parallelFor( int iBegin, int iEnd, int grainSize, const btIParallelForBody& body ) BT_OVERRIDE + { + BT_PROFILE( "parallelFor_ThreadSupport" ); + btAssert( iEnd >= iBegin ); + btAssert( grainSize >= 1 ); + int iterationCount = iEnd - iBegin; + if ( iterationCount > grainSize && m_numWorkerThreads > 0 && m_antiNestingLock.tryLock() ) + { + typedef ParallelForJob JobType; + int jobCount = ( iterationCount + grainSize - 1 ) / grainSize; + m_numJobs = jobCount; + btAssert( jobCount >= 2 ); // need more than one job for multithreading + int jobSize = sizeof( JobType ); + + for (int i = 0; i < m_numActiveJobQueues; ++i) + { + m_jobQueues[i].clearQueue( jobCount, jobSize ); + } + // prepare worker threads for incoming work + prepareWorkerThreads(); + // submit all of the jobs + int iJob = 0; + int iThread = kFirstWorkerThreadId; // first worker thread + for ( int i = iBegin; i < iEnd; i += grainSize ) + { + btAssert( iJob < jobCount ); + int iE = btMin( i + grainSize, iEnd ); + JobQueue* jq = m_perThreadJobQueues[ iThread ]; + btAssert(jq); + btAssert((jq - &m_jobQueues[0]) < m_numActiveJobQueues); + void* jobMem = jq->allocJobMem(jobSize); + JobType* job = new ( jobMem ) ParallelForJob( i, iE, body ); // placement new + jq->submitJob( job ); + iJob++; + iThread++; + if ( iThread >= m_numThreads ) + { + iThread = kFirstWorkerThreadId; // first worker thread + } + } + wakeWorkers( jobCount - 1 ); + + // put the main thread to work on emptying the job queue and then wait for all workers to finish + waitJobs(); + m_antiNestingLock.unlock(); + } + else + { + BT_PROFILE( "parallelFor_mainThread" ); + // just run on main thread + body.forLoop( iBegin, iEnd ); + } + } + virtual btScalar parallelSum( int iBegin, int iEnd, int grainSize, const btIParallelSumBody& body ) BT_OVERRIDE + { + BT_PROFILE( "parallelSum_ThreadSupport" ); + btAssert( iEnd >= iBegin ); + btAssert( grainSize >= 1 ); + int iterationCount = iEnd - iBegin; + if ( iterationCount > grainSize && m_numWorkerThreads > 0 && m_antiNestingLock.tryLock() ) + { + typedef ParallelSumJob JobType; + int jobCount = ( iterationCount + grainSize - 1 ) / grainSize; + m_numJobs = jobCount; + btAssert( jobCount >= 2 ); // need more than one job for multithreading + int jobSize = sizeof( JobType ); + for (int i = 0; i < m_numActiveJobQueues; ++i) + { + m_jobQueues[i].clearQueue( jobCount, jobSize ); + } + + // initialize summation + for ( int iThread = 0; iThread < m_numThreads; ++iThread ) + { + m_threadLocalStorage[iThread].m_sumResult = btScalar(0); + } + + // prepare worker threads for incoming work + prepareWorkerThreads(); + // submit all of the jobs + int iJob = 0; + int iThread = kFirstWorkerThreadId; // first worker thread + for ( int i = iBegin; i < iEnd; i += grainSize ) + { + btAssert( iJob < jobCount ); + int iE = btMin( i + grainSize, iEnd ); + JobQueue* jq = m_perThreadJobQueues[ iThread ]; + btAssert(jq); + btAssert((jq - &m_jobQueues[0]) < m_numActiveJobQueues); + void* jobMem = jq->allocJobMem(jobSize); + JobType* job = new ( jobMem ) ParallelSumJob( i, iE, body, &m_threadLocalStorage[0] ); // placement new + jq->submitJob( job ); + iJob++; + iThread++; + if ( iThread >= m_numThreads ) + { + iThread = kFirstWorkerThreadId; // first worker thread + } + } + wakeWorkers( jobCount - 1 ); + + // put the main thread to work on emptying the job queue and then wait for all workers to finish + waitJobs(); + + // add up all the thread sums + btScalar sum = btScalar(0); + for ( int iThread = 0; iThread < m_numThreads; ++iThread ) + { + sum += m_threadLocalStorage[ iThread ].m_sumResult; + } + m_antiNestingLock.unlock(); + return sum; + } + else + { + BT_PROFILE( "parallelSum_mainThread" ); + // just run on main thread + return body.sumLoop( iBegin, iEnd ); + } + } +}; + + + +btITaskScheduler* btCreateDefaultTaskScheduler() +{ + btTaskSchedulerDefault* ts = new btTaskSchedulerDefault(); + ts->init(); + return ts; +} + +#else // #if BT_THREADSAFE + +btITaskScheduler* btCreateDefaultTaskScheduler() +{ + return NULL; +} + +#endif // #else // #if BT_THREADSAFE diff --git a/thirdparty/bullet/LinearMath/TaskScheduler/btThreadSupportInterface.h b/thirdparty/bullet/LinearMath/TaskScheduler/btThreadSupportInterface.h new file mode 100644 index 0000000000..a0ad802b1e --- /dev/null +++ b/thirdparty/bullet/LinearMath/TaskScheduler/btThreadSupportInterface.h @@ -0,0 +1,70 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2018 Erwin Coumans http://bulletphysics.com + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef BT_THREAD_SUPPORT_INTERFACE_H +#define BT_THREAD_SUPPORT_INTERFACE_H + + + +class btCriticalSection +{ +public: + btCriticalSection() {} + virtual ~btCriticalSection() {} + + virtual void lock() = 0; + virtual void unlock() = 0; +}; + + +class btThreadSupportInterface +{ +public: + + virtual ~btThreadSupportInterface() {} + + virtual int getNumWorkerThreads() const = 0; // number of worker threads (total number of logical processors - 1) + virtual int getCacheFriendlyNumThreads() const = 0; // the number of logical processors sharing a single L3 cache + virtual int getLogicalToPhysicalCoreRatio() const = 0; // the number of logical processors per physical processor (usually 1 or 2) + virtual void runTask( int threadIndex, void* userData ) = 0; + virtual void waitForAllTasks() = 0; + + virtual btCriticalSection* createCriticalSection() = 0; + virtual void deleteCriticalSection( btCriticalSection* criticalSection ) = 0; + + typedef void( *ThreadFunc )( void* userPtr ); + + struct ConstructionInfo + { + ConstructionInfo( const char* uniqueName, + ThreadFunc userThreadFunc, + int threadStackSize = 65535 + ) + :m_uniqueName( uniqueName ), + m_userThreadFunc( userThreadFunc ), + m_threadStackSize( threadStackSize ) + { + } + + const char* m_uniqueName; + ThreadFunc m_userThreadFunc; + int m_threadStackSize; + }; + + static btThreadSupportInterface* create( const ConstructionInfo& info ); +}; + +#endif //BT_THREAD_SUPPORT_INTERFACE_H + diff --git a/thirdparty/bullet/LinearMath/TaskScheduler/btThreadSupportPosix.cpp b/thirdparty/bullet/LinearMath/TaskScheduler/btThreadSupportPosix.cpp new file mode 100644 index 0000000000..50ca060dfe --- /dev/null +++ b/thirdparty/bullet/LinearMath/TaskScheduler/btThreadSupportPosix.cpp @@ -0,0 +1,365 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2018 Erwin Coumans http://bulletphysics.com + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + + +#if BT_THREADSAFE && !defined( _WIN32 ) + + +#include "LinearMath/btScalar.h" +#include "LinearMath/btAlignedObjectArray.h" +#include "LinearMath/btThreads.h" +#include "LinearMath/btMinMax.h" +#include "btThreadSupportInterface.h" + +#include <stdio.h> +#include <errno.h> +#include <unistd.h> + + +#ifndef _XOPEN_SOURCE +#define _XOPEN_SOURCE 600 //for definition of pthread_barrier_t, see http://pages.cs.wisc.edu/~travitch/pthreads_primer.html +#endif //_XOPEN_SOURCE +#include <pthread.h> +#include <semaphore.h> +#include <unistd.h> //for sysconf + + +/// +/// getNumHardwareThreads() +/// +/// +/// https://stackoverflow.com/questions/150355/programmatically-find-the-number-of-cores-on-a-machine +/// +#if __cplusplus >= 201103L + +#include <thread> + +int btGetNumHardwareThreads() +{ + return btMin<int>(BT_MAX_THREAD_COUNT, std::thread::hardware_concurrency()); +} + +#else + +int btGetNumHardwareThreads() +{ + return btMin<int>(BT_MAX_THREAD_COUNT, sysconf( _SC_NPROCESSORS_ONLN )); +} + +#endif + + +// btThreadSupportPosix helps to initialize/shutdown libspe2, start/stop SPU tasks and communication +class btThreadSupportPosix : public btThreadSupportInterface +{ +public: + struct btThreadStatus + { + int m_taskId; + int m_commandId; + int m_status; + + ThreadFunc m_userThreadFunc; + void* m_userPtr; //for taskDesc etc + + pthread_t thread; + //each tread will wait until this signal to start its work + sem_t* startSemaphore; + + // this is a copy of m_mainSemaphore, + //each tread will signal once it is finished with its work + sem_t* m_mainSemaphore; + unsigned long threadUsed; + }; +private: + typedef unsigned long long UINT64; + + btAlignedObjectArray<btThreadStatus> m_activeThreadStatus; + // m_mainSemaphoresemaphore will signal, if and how many threads are finished with their work + sem_t* m_mainSemaphore; + int m_numThreads; + UINT64 m_startedThreadsMask; + void startThreads( const ConstructionInfo& threadInfo ); + void stopThreads(); + int waitForResponse(); + +public: + btThreadSupportPosix( const ConstructionInfo& threadConstructionInfo ); + virtual ~btThreadSupportPosix(); + + virtual int getNumWorkerThreads() const BT_OVERRIDE { return m_numThreads; } + // TODO: return the number of logical processors sharing the first L3 cache + virtual int getCacheFriendlyNumThreads() const BT_OVERRIDE { return m_numThreads + 1; } + // TODO: detect if CPU has hyperthreading enabled + virtual int getLogicalToPhysicalCoreRatio() const BT_OVERRIDE { return 1; } + + virtual void runTask( int threadIndex, void* userData ) BT_OVERRIDE; + virtual void waitForAllTasks() BT_OVERRIDE; + + virtual btCriticalSection* createCriticalSection() BT_OVERRIDE; + virtual void deleteCriticalSection( btCriticalSection* criticalSection ) BT_OVERRIDE; +}; + + +#define checkPThreadFunction(returnValue) \ + if(0 != returnValue) { \ + printf("PThread problem at line %i in file %s: %i %d\n", __LINE__, __FILE__, returnValue, errno); \ + } + +// The number of threads should be equal to the number of available cores +// Todo: each worker should be linked to a single core, using SetThreadIdealProcessor. + + +btThreadSupportPosix::btThreadSupportPosix( const ConstructionInfo& threadConstructionInfo ) +{ + startThreads( threadConstructionInfo ); +} + +// cleanup/shutdown Libspe2 +btThreadSupportPosix::~btThreadSupportPosix() +{ + stopThreads(); +} + +#if (defined (__APPLE__)) +#define NAMED_SEMAPHORES +#endif + + +static sem_t* createSem( const char* baseName ) +{ + static int semCount = 0; +#ifdef NAMED_SEMAPHORES + /// Named semaphore begin + char name[ 32 ]; + snprintf( name, 32, "/%8.s-%4.d-%4.4d", baseName, getpid(), semCount++ ); + sem_t* tempSem = sem_open( name, O_CREAT, 0600, 0 ); + + if ( tempSem != reinterpret_cast<sem_t *>( SEM_FAILED ) ) + { + // printf("Created \"%s\" Semaphore %p\n", name, tempSem); + } + else + { + //printf("Error creating Semaphore %d\n", errno); + exit( -1 ); + } + /// Named semaphore end +#else + sem_t* tempSem = new sem_t; + checkPThreadFunction( sem_init( tempSem, 0, 0 ) ); +#endif + return tempSem; +} + +static void destroySem( sem_t* semaphore ) +{ +#ifdef NAMED_SEMAPHORES + checkPThreadFunction( sem_close( semaphore ) ); +#else + checkPThreadFunction( sem_destroy( semaphore ) ); + delete semaphore; +#endif +} + +static void *threadFunction( void *argument ) +{ + btThreadSupportPosix::btThreadStatus* status = ( btThreadSupportPosix::btThreadStatus* )argument; + + while ( 1 ) + { + checkPThreadFunction( sem_wait( status->startSemaphore ) ); + void* userPtr = status->m_userPtr; + + if ( userPtr ) + { + btAssert( status->m_status ); + status->m_userThreadFunc( userPtr ); + status->m_status = 2; + checkPThreadFunction( sem_post( status->m_mainSemaphore ) ); + status->threadUsed++; + } + else + { + //exit Thread + status->m_status = 3; + checkPThreadFunction( sem_post( status->m_mainSemaphore ) ); + printf( "Thread with taskId %i exiting\n", status->m_taskId ); + break; + } + } + + printf( "Thread TERMINATED\n" ); + return 0; +} + +///send messages to SPUs +void btThreadSupportPosix::runTask( int threadIndex, void* userData ) +{ + ///we should spawn an SPU task here, and in 'waitForResponse' it should wait for response of the (one of) the first tasks that finished + btThreadStatus& threadStatus = m_activeThreadStatus[ threadIndex ]; + btAssert( threadIndex >= 0 ); + btAssert( threadIndex < m_activeThreadStatus.size() ); + + threadStatus.m_commandId = 1; + threadStatus.m_status = 1; + threadStatus.m_userPtr = userData; + m_startedThreadsMask |= UINT64( 1 ) << threadIndex; + + // fire event to start new task + checkPThreadFunction( sem_post( threadStatus.startSemaphore ) ); +} + + +///check for messages from SPUs +int btThreadSupportPosix::waitForResponse() +{ + ///We should wait for (one of) the first tasks to finish (or other SPU messages), and report its response + ///A possible response can be 'yes, SPU handled it', or 'no, please do a PPU fallback' + + btAssert( m_activeThreadStatus.size() ); + + // wait for any of the threads to finish + checkPThreadFunction( sem_wait( m_mainSemaphore ) ); + // get at least one thread which has finished + size_t last = -1; + + for ( size_t t = 0; t < size_t( m_activeThreadStatus.size() ); ++t ) + { + if ( 2 == m_activeThreadStatus[ t ].m_status ) + { + last = t; + break; + } + } + + btThreadStatus& threadStatus = m_activeThreadStatus[ last ]; + + btAssert( threadStatus.m_status > 1 ); + threadStatus.m_status = 0; + + // need to find an active spu + btAssert( last >= 0 ); + m_startedThreadsMask &= ~( UINT64( 1 ) << last ); + + return last; +} + + +void btThreadSupportPosix::waitForAllTasks() +{ + while ( m_startedThreadsMask ) + { + waitForResponse(); + } +} + + +void btThreadSupportPosix::startThreads( const ConstructionInfo& threadConstructionInfo ) +{ + m_numThreads = btGetNumHardwareThreads() - 1; // main thread exists already + printf( "%s creating %i threads.\n", __FUNCTION__, m_numThreads ); + m_activeThreadStatus.resize( m_numThreads ); + m_startedThreadsMask = 0; + + m_mainSemaphore = createSem( "main" ); + //checkPThreadFunction(sem_wait(mainSemaphore)); + + for ( int i = 0; i < m_numThreads; i++ ) + { + printf( "starting thread %d\n", i ); + btThreadStatus& threadStatus = m_activeThreadStatus[ i ]; + threadStatus.startSemaphore = createSem( "threadLocal" ); + checkPThreadFunction( pthread_create( &threadStatus.thread, NULL, &threadFunction, (void*) &threadStatus ) ); + + threadStatus.m_userPtr = 0; + threadStatus.m_taskId = i; + threadStatus.m_commandId = 0; + threadStatus.m_status = 0; + threadStatus.m_mainSemaphore = m_mainSemaphore; + threadStatus.m_userThreadFunc = threadConstructionInfo.m_userThreadFunc; + threadStatus.threadUsed = 0; + + printf( "started thread %d \n", i ); + } +} + +///tell the task scheduler we are done with the SPU tasks +void btThreadSupportPosix::stopThreads() +{ + for ( size_t t = 0; t < size_t( m_activeThreadStatus.size() ); ++t ) + { + btThreadStatus& threadStatus = m_activeThreadStatus[ t ]; + printf( "%s: Thread %i used: %ld\n", __FUNCTION__, int( t ), threadStatus.threadUsed ); + + threadStatus.m_userPtr = 0; + checkPThreadFunction( sem_post( threadStatus.startSemaphore ) ); + checkPThreadFunction( sem_wait( m_mainSemaphore ) ); + + printf( "destroy semaphore\n" ); + destroySem( threadStatus.startSemaphore ); + printf( "semaphore destroyed\n" ); + checkPThreadFunction( pthread_join( threadStatus.thread, 0 ) ); + + } + printf( "destroy main semaphore\n" ); + destroySem( m_mainSemaphore ); + printf( "main semaphore destroyed\n" ); + m_activeThreadStatus.clear(); +} + +class btCriticalSectionPosix : public btCriticalSection +{ + pthread_mutex_t m_mutex; + +public: + btCriticalSectionPosix() + { + pthread_mutex_init( &m_mutex, NULL ); + } + virtual ~btCriticalSectionPosix() + { + pthread_mutex_destroy( &m_mutex ); + } + + virtual void lock() + { + pthread_mutex_lock( &m_mutex ); + } + virtual void unlock() + { + pthread_mutex_unlock( &m_mutex ); + } +}; + + +btCriticalSection* btThreadSupportPosix::createCriticalSection() +{ + return new btCriticalSectionPosix(); +} + +void btThreadSupportPosix::deleteCriticalSection( btCriticalSection* cs ) +{ + delete cs; +} + + +btThreadSupportInterface* btThreadSupportInterface::create( const ConstructionInfo& info ) +{ + return new btThreadSupportPosix( info ); +} + +#endif // BT_THREADSAFE && !defined( _WIN32 ) + diff --git a/thirdparty/bullet/LinearMath/TaskScheduler/btThreadSupportWin32.cpp b/thirdparty/bullet/LinearMath/TaskScheduler/btThreadSupportWin32.cpp new file mode 100644 index 0000000000..00edac650b --- /dev/null +++ b/thirdparty/bullet/LinearMath/TaskScheduler/btThreadSupportWin32.cpp @@ -0,0 +1,472 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2018 Erwin Coumans http://bulletphysics.com + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#if defined( _WIN32 ) && BT_THREADSAFE + +#include "LinearMath/btScalar.h" +#include "LinearMath/btMinMax.h" +#include "LinearMath/btAlignedObjectArray.h" +#include "LinearMath/btThreads.h" +#include "btThreadSupportInterface.h" +#include <windows.h> +#include <stdio.h> + + +struct btProcessorInfo +{ + int numLogicalProcessors; + int numCores; + int numNumaNodes; + int numL1Cache; + int numL2Cache; + int numL3Cache; + int numPhysicalPackages; + static const int maxNumTeamMasks = 32; + int numTeamMasks; + UINT64 processorTeamMasks[ maxNumTeamMasks ]; +}; + +UINT64 getProcessorTeamMask( const btProcessorInfo& procInfo, int procId ) +{ + UINT64 procMask = UINT64( 1 ) << procId; + for ( int i = 0; i < procInfo.numTeamMasks; ++i ) + { + if ( procMask & procInfo.processorTeamMasks[ i ] ) + { + return procInfo.processorTeamMasks[ i ]; + } + } + return 0; +} + +int getProcessorTeamIndex( const btProcessorInfo& procInfo, int procId ) +{ + UINT64 procMask = UINT64( 1 ) << procId; + for ( int i = 0; i < procInfo.numTeamMasks; ++i ) + { + if ( procMask & procInfo.processorTeamMasks[ i ] ) + { + return i; + } + } + return -1; +} + +int countSetBits( ULONG64 bits ) +{ + int count = 0; + while ( bits ) + { + if ( bits & 1 ) + { + count++; + } + bits >>= 1; + } + return count; +} + + +typedef BOOL( WINAPI *Pfn_GetLogicalProcessorInformation )( PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, PDWORD ); + + +void getProcessorInformation( btProcessorInfo* procInfo ) +{ + memset( procInfo, 0, sizeof( *procInfo ) ); + Pfn_GetLogicalProcessorInformation getLogicalProcInfo = + (Pfn_GetLogicalProcessorInformation) GetProcAddress( GetModuleHandle( TEXT( "kernel32" ) ), "GetLogicalProcessorInformation" ); + if ( getLogicalProcInfo == NULL ) + { + // no info + return; + } + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buf = NULL; + DWORD bufSize = 0; + while ( true ) + { + if ( getLogicalProcInfo( buf, &bufSize ) ) + { + break; + } + else + { + if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER ) + { + if ( buf ) + { + free( buf ); + } + buf = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION) malloc( bufSize ); + } + } + } + + int len = bufSize / sizeof( *buf ); + for ( int i = 0; i < len; ++i ) + { + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION info = buf + i; + switch ( info->Relationship ) + { + case RelationNumaNode: + procInfo->numNumaNodes++; + break; + + case RelationProcessorCore: + procInfo->numCores++; + procInfo->numLogicalProcessors += countSetBits( info->ProcessorMask ); + break; + + case RelationCache: + if ( info->Cache.Level == 1 ) + { + procInfo->numL1Cache++; + } + else if ( info->Cache.Level == 2 ) + { + procInfo->numL2Cache++; + } + else if ( info->Cache.Level == 3 ) + { + procInfo->numL3Cache++; + // processors that share L3 cache are considered to be on the same team + // because they can more easily work together on the same data. + // Large performance penalties will occur if 2 or more threads from different + // teams attempt to frequently read and modify the same cache lines. + // + // On the AMD Ryzen 7 CPU for example, the 8 cores on the CPU are split into + // 2 CCX units of 4 cores each. Each CCX has a separate L3 cache, so if both + // CCXs are operating on the same data, many cycles will be spent keeping the + // two caches coherent. + if ( procInfo->numTeamMasks < btProcessorInfo::maxNumTeamMasks ) + { + procInfo->processorTeamMasks[ procInfo->numTeamMasks ] = info->ProcessorMask; + procInfo->numTeamMasks++; + } + } + break; + + case RelationProcessorPackage: + procInfo->numPhysicalPackages++; + break; + } + } + free( buf ); +} + + + +///btThreadSupportWin32 helps to initialize/shutdown libspe2, start/stop SPU tasks and communication +class btThreadSupportWin32 : public btThreadSupportInterface +{ +public: + struct btThreadStatus + { + int m_taskId; + int m_commandId; + int m_status; + + ThreadFunc m_userThreadFunc; + void* m_userPtr; //for taskDesc etc + + void* m_threadHandle; //this one is calling 'Win32ThreadFunc' + + void* m_eventStartHandle; + char m_eventStartHandleName[ 32 ]; + + void* m_eventCompleteHandle; + char m_eventCompleteHandleName[ 32 ]; + }; + +private: + btAlignedObjectArray<btThreadStatus> m_activeThreadStatus; + btAlignedObjectArray<void*> m_completeHandles; + int m_numThreads; + DWORD_PTR m_startedThreadMask; + btProcessorInfo m_processorInfo; + + void startThreads( const ConstructionInfo& threadInfo ); + void stopThreads(); + int waitForResponse(); + +public: + + btThreadSupportWin32( const ConstructionInfo& threadConstructionInfo ); + virtual ~btThreadSupportWin32(); + + virtual int getNumWorkerThreads() const BT_OVERRIDE { return m_numThreads; } + virtual int getCacheFriendlyNumThreads() const BT_OVERRIDE { return countSetBits(m_processorInfo.processorTeamMasks[0]); } + virtual int getLogicalToPhysicalCoreRatio() const BT_OVERRIDE { return m_processorInfo.numLogicalProcessors / m_processorInfo.numCores; } + + virtual void runTask( int threadIndex, void* userData ) BT_OVERRIDE; + virtual void waitForAllTasks() BT_OVERRIDE; + + virtual btCriticalSection* createCriticalSection() BT_OVERRIDE; + virtual void deleteCriticalSection( btCriticalSection* criticalSection ) BT_OVERRIDE; +}; + + +btThreadSupportWin32::btThreadSupportWin32( const ConstructionInfo & threadConstructionInfo ) +{ + startThreads( threadConstructionInfo ); +} + + +btThreadSupportWin32::~btThreadSupportWin32() +{ + stopThreads(); +} + + +DWORD WINAPI win32threadStartFunc( LPVOID lpParam ) +{ + btThreadSupportWin32::btThreadStatus* status = ( btThreadSupportWin32::btThreadStatus* )lpParam; + + while ( 1 ) + { + WaitForSingleObject( status->m_eventStartHandle, INFINITE ); + void* userPtr = status->m_userPtr; + + if ( userPtr ) + { + btAssert( status->m_status ); + status->m_userThreadFunc( userPtr ); + status->m_status = 2; + SetEvent( status->m_eventCompleteHandle ); + } + else + { + //exit Thread + status->m_status = 3; + printf( "Thread with taskId %i with handle %p exiting\n", status->m_taskId, status->m_threadHandle ); + SetEvent( status->m_eventCompleteHandle ); + break; + } + } + printf( "Thread TERMINATED\n" ); + return 0; +} + + +void btThreadSupportWin32::runTask( int threadIndex, void* userData ) +{ + btThreadStatus& threadStatus = m_activeThreadStatus[ threadIndex ]; + btAssert( threadIndex >= 0 ); + btAssert( int( threadIndex ) < m_activeThreadStatus.size() ); + + threadStatus.m_commandId = 1; + threadStatus.m_status = 1; + threadStatus.m_userPtr = userData; + m_startedThreadMask |= DWORD_PTR( 1 ) << threadIndex; + + ///fire event to start new task + SetEvent( threadStatus.m_eventStartHandle ); +} + + +int btThreadSupportWin32::waitForResponse() +{ + btAssert( m_activeThreadStatus.size() ); + + int last = -1; + DWORD res = WaitForMultipleObjects( m_completeHandles.size(), &m_completeHandles[ 0 ], FALSE, INFINITE ); + btAssert( res != WAIT_FAILED ); + last = res - WAIT_OBJECT_0; + + btThreadStatus& threadStatus = m_activeThreadStatus[ last ]; + btAssert( threadStatus.m_threadHandle ); + btAssert( threadStatus.m_eventCompleteHandle ); + + //WaitForSingleObject(threadStatus.m_eventCompleteHandle, INFINITE); + btAssert( threadStatus.m_status > 1 ); + threadStatus.m_status = 0; + + ///need to find an active spu + btAssert( last >= 0 ); + m_startedThreadMask &= ~( DWORD_PTR( 1 ) << last ); + + return last; +} + + +void btThreadSupportWin32::waitForAllTasks() +{ + while ( m_startedThreadMask ) + { + waitForResponse(); + } +} + + +void btThreadSupportWin32::startThreads( const ConstructionInfo& threadConstructionInfo ) +{ + static int uniqueId = 0; + uniqueId++; + btProcessorInfo& procInfo = m_processorInfo; + getProcessorInformation( &procInfo ); + DWORD_PTR dwProcessAffinityMask = 0; + DWORD_PTR dwSystemAffinityMask = 0; + if ( !GetProcessAffinityMask( GetCurrentProcess(), &dwProcessAffinityMask, &dwSystemAffinityMask ) ) + { + dwProcessAffinityMask = 0; + } + ///The number of threads should be equal to the number of available cores - 1 + m_numThreads = btMin(procInfo.numLogicalProcessors, int(BT_MAX_THREAD_COUNT)) - 1; // cap to max thread count (-1 because main thread already exists) + + m_activeThreadStatus.resize( m_numThreads ); + m_completeHandles.resize( m_numThreads ); + m_startedThreadMask = 0; + + // set main thread affinity + if ( DWORD_PTR mask = dwProcessAffinityMask & getProcessorTeamMask( procInfo, 0 )) + { + SetThreadAffinityMask( GetCurrentThread(), mask ); + SetThreadIdealProcessor( GetCurrentThread(), 0 ); + } + + for ( int i = 0; i < m_numThreads; i++ ) + { + printf( "starting thread %d\n", i ); + + btThreadStatus& threadStatus = m_activeThreadStatus[ i ]; + + LPSECURITY_ATTRIBUTES lpThreadAttributes = NULL; + SIZE_T dwStackSize = threadConstructionInfo.m_threadStackSize; + LPTHREAD_START_ROUTINE lpStartAddress = &win32threadStartFunc; + LPVOID lpParameter = &threadStatus; + DWORD dwCreationFlags = 0; + LPDWORD lpThreadId = 0; + + threadStatus.m_userPtr = 0; + + sprintf( threadStatus.m_eventStartHandleName, "es%.8s%d%d", threadConstructionInfo.m_uniqueName, uniqueId, i ); + threadStatus.m_eventStartHandle = CreateEventA( 0, false, false, threadStatus.m_eventStartHandleName ); + + sprintf( threadStatus.m_eventCompleteHandleName, "ec%.8s%d%d", threadConstructionInfo.m_uniqueName, uniqueId, i ); + threadStatus.m_eventCompleteHandle = CreateEventA( 0, false, false, threadStatus.m_eventCompleteHandleName ); + + m_completeHandles[ i ] = threadStatus.m_eventCompleteHandle; + + HANDLE handle = CreateThread( lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, dwCreationFlags, lpThreadId ); + //SetThreadPriority( handle, THREAD_PRIORITY_HIGHEST ); + // highest priority -- can cause erratic performance when numThreads > numCores + // we don't want worker threads to be higher priority than the main thread or the main thread could get + // totally shut out and unable to tell the workers to stop + //SetThreadPriority( handle, THREAD_PRIORITY_BELOW_NORMAL ); + + { + int processorId = i + 1; // leave processor 0 for main thread + DWORD_PTR teamMask = getProcessorTeamMask( procInfo, processorId ); + if ( teamMask ) + { + // bind each thread to only execute on processors of it's assigned team + // - for single-socket Intel x86 CPUs this has no effect (only a single, shared L3 cache so there is only 1 team) + // - for multi-socket Intel this will keep threads from migrating from one socket to another + // - for AMD Ryzen this will keep threads from migrating from one CCX to another + DWORD_PTR mask = teamMask & dwProcessAffinityMask; + if ( mask ) + { + SetThreadAffinityMask( handle, mask ); + } + } + SetThreadIdealProcessor( handle, processorId ); + } + + threadStatus.m_taskId = i; + threadStatus.m_commandId = 0; + threadStatus.m_status = 0; + threadStatus.m_threadHandle = handle; + threadStatus.m_userThreadFunc = threadConstructionInfo.m_userThreadFunc; + + printf( "started %s thread %d with threadHandle %p\n", threadConstructionInfo.m_uniqueName, i, handle ); + } +} + +///tell the task scheduler we are done with the SPU tasks +void btThreadSupportWin32::stopThreads() +{ + for ( int i = 0; i < m_activeThreadStatus.size(); i++ ) + { + btThreadStatus& threadStatus = m_activeThreadStatus[ i ]; + if ( threadStatus.m_status > 0 ) + { + WaitForSingleObject( threadStatus.m_eventCompleteHandle, INFINITE ); + } + + threadStatus.m_userPtr = NULL; + SetEvent( threadStatus.m_eventStartHandle ); + WaitForSingleObject( threadStatus.m_eventCompleteHandle, INFINITE ); + + CloseHandle( threadStatus.m_eventCompleteHandle ); + CloseHandle( threadStatus.m_eventStartHandle ); + CloseHandle( threadStatus.m_threadHandle ); + + } + + m_activeThreadStatus.clear(); + m_completeHandles.clear(); +} + + +class btWin32CriticalSection : public btCriticalSection +{ +private: + CRITICAL_SECTION mCriticalSection; + +public: + btWin32CriticalSection() + { + InitializeCriticalSection( &mCriticalSection ); + } + + ~btWin32CriticalSection() + { + DeleteCriticalSection( &mCriticalSection ); + } + + void lock() + { + EnterCriticalSection( &mCriticalSection ); + } + + void unlock() + { + LeaveCriticalSection( &mCriticalSection ); + } +}; + + +btCriticalSection* btThreadSupportWin32::createCriticalSection() +{ + unsigned char* mem = (unsigned char*) btAlignedAlloc( sizeof( btWin32CriticalSection ), 16 ); + btWin32CriticalSection* cs = new( mem ) btWin32CriticalSection(); + return cs; +} + +void btThreadSupportWin32::deleteCriticalSection( btCriticalSection* criticalSection ) +{ + criticalSection->~btCriticalSection(); + btAlignedFree( criticalSection ); +} + + +btThreadSupportInterface* btThreadSupportInterface::create( const ConstructionInfo& info ) +{ + return new btThreadSupportWin32( info ); +} + + + +#endif //defined(_WIN32) && BT_THREADSAFE + diff --git a/thirdparty/bullet/LinearMath/btAlignedAllocator.cpp b/thirdparty/bullet/LinearMath/btAlignedAllocator.cpp index e5f6040c43..0526a42283 100644 --- a/thirdparty/bullet/LinearMath/btAlignedAllocator.cpp +++ b/thirdparty/bullet/LinearMath/btAlignedAllocator.cpp @@ -15,9 +15,11 @@ subject to the following restrictions: #include "btAlignedAllocator.h" +#ifdef BT_DEBUG_MEMORY_ALLOCATIONS int gNumAlignedAllocs = 0; int gNumAlignedFree = 0; int gTotalBytesAlignedAllocs = 0;//detect memory leaks +#endif //BT_DEBUG_MEMORY_ALLOCATIONST_DEBUG_ALLOCATIONS static void *btAllocDefault(size_t size) { @@ -246,7 +248,6 @@ void btAlignedFreeInternal (void* ptr,int line,char* filename) void* btAlignedAllocInternal (size_t size, int alignment) { - gNumAlignedAllocs++; void* ptr; ptr = sAlignedAllocFunc(size, alignment); // printf("btAlignedAllocInternal %d, %x\n",size,ptr); @@ -260,7 +261,6 @@ void btAlignedFreeInternal (void* ptr) return; } - gNumAlignedFree++; // printf("btAlignedFreeInternal %x\n",ptr); sAlignedFreeFunc(ptr); } diff --git a/thirdparty/bullet/LinearMath/btHashMap.h b/thirdparty/bullet/LinearMath/btHashMap.h index 5e9cdb6054..180e7b44af 100644 --- a/thirdparty/bullet/LinearMath/btHashMap.h +++ b/thirdparty/bullet/LinearMath/btHashMap.h @@ -17,12 +17,13 @@ subject to the following restrictions: #ifndef BT_HASH_MAP_H #define BT_HASH_MAP_H +#include <string> #include "btAlignedObjectArray.h" ///very basic hashable string implementation, compatible with btHashMap struct btHashString { - const char* m_string; + std::string m_string1; unsigned int m_hash; SIMD_FORCE_INLINE unsigned int getHash()const @@ -30,8 +31,13 @@ struct btHashString return m_hash; } + btHashString() + { + m_string1=""; + m_hash=0; + } btHashString(const char* name) - :m_string(name) + :m_string1(name) { /* magic numbers from http://www.isthe.com/chongo/tech/comp/fnv/ */ static const unsigned int InitialFNV = 2166136261u; @@ -40,36 +46,18 @@ struct btHashString /* Fowler / Noll / Vo (FNV) Hash */ unsigned int hash = InitialFNV; - for(int i = 0; m_string[i]; i++) + for(int i = 0; m_string1.c_str()[i]; i++) { - hash = hash ^ (m_string[i]); /* xor the low 8 bits */ + hash = hash ^ (m_string1.c_str()[i]); /* xor the low 8 bits */ hash = hash * FNVMultiple; /* multiply by the magic number */ } m_hash = hash; } - int portableStringCompare(const char* src, const char* dst) const - { - int ret = 0 ; - - while( ! (ret = *(const unsigned char *)src - *(const unsigned char *)dst) && *dst) - ++src, ++dst; - - if ( ret < 0 ) - ret = -1 ; - else if ( ret > 0 ) - ret = 1 ; - - return( ret ); - } - bool equals(const btHashString& other) const { - return (m_string == other.m_string) || - (0==portableStringCompare(m_string,other.m_string)); - + return (m_string1 == other.m_string1); } - }; const int BT_HASH_NULL=0xffffffff; diff --git a/thirdparty/bullet/LinearMath/btIDebugDraw.h b/thirdparty/bullet/LinearMath/btIDebugDraw.h index 936aaa896b..b57282717d 100644 --- a/thirdparty/bullet/LinearMath/btIDebugDraw.h +++ b/thirdparty/bullet/LinearMath/btIDebugDraw.h @@ -166,9 +166,9 @@ class btIDebugDraw virtual void drawTransform(const btTransform& transform, btScalar orthoLen) { btVector3 start = transform.getOrigin(); - drawLine(start, start+transform.getBasis() * btVector3(orthoLen, 0, 0), btVector3(1.f,0.3,0.3)); - drawLine(start, start+transform.getBasis() * btVector3(0, orthoLen, 0), btVector3(0.3,1.f, 0.3)); - drawLine(start, start+transform.getBasis() * btVector3(0, 0, orthoLen), btVector3(0.3, 0.3,1.f)); + drawLine(start, start+transform.getBasis() * btVector3(orthoLen, 0, 0), btVector3(btScalar(1.), btScalar(0.3), btScalar(0.3))); + drawLine(start, start+transform.getBasis() * btVector3(0, orthoLen, 0), btVector3(btScalar(0.3), btScalar(1.), btScalar(0.3))); + drawLine(start, start+transform.getBasis() * btVector3(0, 0, orthoLen), btVector3(btScalar(0.3), btScalar(0.3), btScalar(1.))); } virtual void drawArc(const btVector3& center, const btVector3& normal, const btVector3& axis, btScalar radiusA, btScalar radiusB, btScalar minAngle, btScalar maxAngle, diff --git a/thirdparty/bullet/LinearMath/btMatrix3x3.h b/thirdparty/bullet/LinearMath/btMatrix3x3.h index 9f642a1779..6cc4993da5 100644 --- a/thirdparty/bullet/LinearMath/btMatrix3x3.h +++ b/thirdparty/bullet/LinearMath/btMatrix3x3.h @@ -289,7 +289,7 @@ public: /** @brief Set the matrix from euler angles YPR around ZYX axes * @param eulerX Roll about X axis * @param eulerY Pitch around Y axis - * @param eulerZ Yaw aboud Z axis + * @param eulerZ Yaw about Z axis * * These angles are used to produce a rotation matrix. The euler * angles are applied in ZYX order. I.e a vector is first rotated @@ -514,7 +514,7 @@ public: /**@brief Get the matrix represented as euler angles around ZYX - * @param yaw Yaw around X axis + * @param yaw Yaw around Z axis * @param pitch Pitch around Y axis * @param roll around X axis * @param solution_number Which solution of two possible solutions ( 1 or 2) are possible values*/ @@ -649,6 +649,10 @@ public: ///extractRotation is from "A robust method to extract the rotational part of deformations" ///See http://dl.acm.org/citation.cfm?doid=2994258.2994269 + ///decomposes a matrix A in a orthogonal matrix R and a + ///symmetric matrix S: + ///A = R*S. + ///note that R can include both rotation and scaling. SIMD_FORCE_INLINE void extractRotation(btQuaternion &q,btScalar tolerance = 1.0e-9, int maxIter=100) { int iter =0; @@ -673,25 +677,93 @@ public: - /**@brief diagonalizes this matrix + + /**@brief diagonalizes this matrix by the Jacobi method. * @param rot stores the rotation from the coordinate system in which the matrix is diagonal to the original - * coordinate system, i.e., old_this = rot * new_this * rot^T. + * coordinate system, i.e., old_this = rot * new_this * rot^T. * @param threshold See iteration - * @param maxIter The iteration stops when we hit the given tolerance or when maxIter have been executed. + * @param iteration The iteration stops when all off-diagonal elements are less than the threshold multiplied + * by the sum of the absolute values of the diagonal, or when maxSteps have been executed. + * + * Note that this matrix is assumed to be symmetric. */ - void diagonalize(btMatrix3x3& rot, btScalar tolerance = 1.0e-9, int maxIter=100) + void diagonalize(btMatrix3x3& rot, btScalar threshold, int maxSteps) { - btQuaternion r; - r = btQuaternion::getIdentity(); - extractRotation(r,tolerance,maxIter); - rot.setRotation(r); - btMatrix3x3 rotInv = btMatrix3x3(r.inverse()); - btMatrix3x3 old = *this; - setValue(old.tdotx( rotInv[0]), old.tdoty( rotInv[0]), old.tdotz( rotInv[0]), - old.tdotx( rotInv[1]), old.tdoty( rotInv[1]), old.tdotz( rotInv[1]), - old.tdotx( rotInv[2]), old.tdoty( rotInv[2]), old.tdotz( rotInv[2])); - } + rot.setIdentity(); + for (int step = maxSteps; step > 0; step--) + { + // find off-diagonal element [p][q] with largest magnitude + int p = 0; + int q = 1; + int r = 2; + btScalar max = btFabs(m_el[0][1]); + btScalar v = btFabs(m_el[0][2]); + if (v > max) + { + q = 2; + r = 1; + max = v; + } + v = btFabs(m_el[1][2]); + if (v > max) + { + p = 1; + q = 2; + r = 0; + max = v; + } + + btScalar t = threshold * (btFabs(m_el[0][0]) + btFabs(m_el[1][1]) + btFabs(m_el[2][2])); + if (max <= t) + { + if (max <= SIMD_EPSILON * t) + { + return; + } + step = 1; + } + // compute Jacobi rotation J which leads to a zero for element [p][q] + btScalar mpq = m_el[p][q]; + btScalar theta = (m_el[q][q] - m_el[p][p]) / (2 * mpq); + btScalar theta2 = theta * theta; + btScalar cos; + btScalar sin; + if (theta2 * theta2 < btScalar(10 / SIMD_EPSILON)) + { + t = (theta >= 0) ? 1 / (theta + btSqrt(1 + theta2)) + : 1 / (theta - btSqrt(1 + theta2)); + cos = 1 / btSqrt(1 + t * t); + sin = cos * t; + } + else + { + // approximation for large theta-value, i.e., a nearly diagonal matrix + t = 1 / (theta * (2 + btScalar(0.5) / theta2)); + cos = 1 - btScalar(0.5) * t * t; + sin = cos * t; + } + + // apply rotation to matrix (this = J^T * this * J) + m_el[p][q] = m_el[q][p] = 0; + m_el[p][p] -= t * mpq; + m_el[q][q] += t * mpq; + btScalar mrp = m_el[r][p]; + btScalar mrq = m_el[r][q]; + m_el[r][p] = m_el[p][r] = cos * mrp - sin * mrq; + m_el[r][q] = m_el[q][r] = cos * mrq + sin * mrp; + + // apply rotation to rot (rot = rot * J) + for (int i = 0; i < 3; i++) + { + btVector3& row = rot[i]; + mrp = row[p]; + mrq = row[q]; + row[p] = cos * mrp - sin * mrq; + row[q] = cos * mrq + sin * mrp; + } + } + } diff --git a/thirdparty/bullet/LinearMath/btQuaternion.h b/thirdparty/bullet/LinearMath/btQuaternion.h index 7bd39e6a33..a98fec7bc4 100644 --- a/thirdparty/bullet/LinearMath/btQuaternion.h +++ b/thirdparty/bullet/LinearMath/btQuaternion.h @@ -173,10 +173,28 @@ public: sqy = m_floats[1] * m_floats[1]; sqz = m_floats[2] * m_floats[2]; squ = m_floats[3] * m_floats[3]; - rollX = btAtan2(2 * (m_floats[1] * m_floats[2] + m_floats[3] * m_floats[0]), squ - sqx - sqy + sqz); - sarg = btScalar(-2.) * (m_floats[0] * m_floats[2] - m_floats[3] * m_floats[1]); - pitchY = sarg <= btScalar(-1.0) ? btScalar(-0.5) * SIMD_PI: (sarg >= btScalar(1.0) ? btScalar(0.5) * SIMD_PI : btAsin(sarg)); - yawZ = btAtan2(2 * (m_floats[0] * m_floats[1] + m_floats[3] * m_floats[2]), squ + sqx - sqy - sqz); + sarg = btScalar(-2.) * (m_floats[0] * m_floats[2] - m_floats[3] * m_floats[1]); + + // If the pitch angle is PI/2 or -PI/2, we can only compute + // the sum roll + yaw. However, any combination that gives + // the right sum will produce the correct orientation, so we + // set rollX = 0 and compute yawZ. + if (sarg <= -btScalar(0.99999)) + { + pitchY = btScalar(-0.5)*SIMD_PI; + rollX = 0; + yawZ = btScalar(2) * btAtan2(m_floats[0],-m_floats[1]); + } else if (sarg >= btScalar(0.99999)) + { + pitchY = btScalar(0.5)*SIMD_PI; + rollX = 0; + yawZ = btScalar(2) * btAtan2(-m_floats[0], m_floats[1]); + } else + { + pitchY = btAsin(sarg); + rollX = btAtan2(2 * (m_floats[1] * m_floats[2] + m_floats[3] * m_floats[0]), squ - sqx - sqy + sqz); + yawZ = btAtan2(2 * (m_floats[0] * m_floats[1] + m_floats[3] * m_floats[2]), squ + sqx - sqy - sqz); + } } /**@brief Add two quaternions @@ -602,7 +620,9 @@ public: SIMD_FORCE_INLINE void serialize(struct btQuaternionData& dataOut) const; - SIMD_FORCE_INLINE void deSerialize(const struct btQuaternionData& dataIn); + SIMD_FORCE_INLINE void deSerialize(const struct btQuaternionFloatData& dataIn); + + SIMD_FORCE_INLINE void deSerialize(const struct btQuaternionDoubleData& dataIn); SIMD_FORCE_INLINE void serializeFloat(struct btQuaternionFloatData& dataOut) const; @@ -1003,10 +1023,16 @@ SIMD_FORCE_INLINE void btQuaternion::serialize(struct btQuaternionData& dataOut) dataOut.m_floats[i] = m_floats[i]; } -SIMD_FORCE_INLINE void btQuaternion::deSerialize(const struct btQuaternionData& dataIn) +SIMD_FORCE_INLINE void btQuaternion::deSerialize(const struct btQuaternionFloatData& dataIn) +{ + for (int i = 0; i<4; i++) + m_floats[i] = (btScalar)dataIn.m_floats[i]; +} + +SIMD_FORCE_INLINE void btQuaternion::deSerialize(const struct btQuaternionDoubleData& dataIn) { for (int i=0;i<4;i++) - m_floats[i] = dataIn.m_floats[i]; + m_floats[i] = (btScalar)dataIn.m_floats[i]; } diff --git a/thirdparty/bullet/LinearMath/btQuickprof.cpp b/thirdparty/bullet/LinearMath/btQuickprof.cpp index aed3104a6e..1572b96262 100644 --- a/thirdparty/bullet/LinearMath/btQuickprof.cpp +++ b/thirdparty/bullet/LinearMath/btQuickprof.cpp @@ -680,56 +680,58 @@ void CProfileManager::dumpAll() CProfileManager::Release_Iterator(profileIterator); } +// clang-format off +#if defined(_WIN32) && (defined(__MINGW32__) || defined(__MINGW64__)) + #define BT_HAVE_TLS 1 +#elif __APPLE__ && !TARGET_OS_IPHONE + // TODO: Modern versions of iOS support TLS now with updated version checking. + #define BT_HAVE_TLS 1 +#elif __linux__ + #define BT_HAVE_TLS 1 +#endif +// __thread is broken on Andorid clang until r12b. See +// https://github.com/android-ndk/ndk/issues/8 +#if defined(__ANDROID__) && defined(__clang__) + #if __has_include(<android/ndk-version.h>) + #include <android/ndk-version.h> + #endif // __has_include(<android/ndk-version.h>) + #if defined(__NDK_MAJOR__) && \ + ((__NDK_MAJOR__ < 12) || ((__NDK_MAJOR__ == 12) && (__NDK_MINOR__ < 1))) + #undef BT_HAVE_TLS + #endif +#endif // defined(__ANDROID__) && defined(__clang__) +// clang-format on + +unsigned int btQuickprofGetCurrentThreadIndex2() { + const unsigned int kNullIndex = ~0U; - -unsigned int btQuickprofGetCurrentThreadIndex2() -{ #if BT_THREADSAFE - return btGetCurrentThreadIndex(); -#else // #if BT_THREADSAFE - const unsigned int kNullIndex = ~0U; -#ifdef _WIN32 - #if defined(__MINGW32__) || defined(__MINGW64__) - static __thread unsigned int sThreadIndex = kNullIndex; - #else - __declspec( thread ) static unsigned int sThreadIndex = kNullIndex; - #endif + return btGetCurrentThreadIndex(); #else -#ifdef __APPLE__ - #if TARGET_OS_IPHONE - unsigned int sThreadIndex = 0; - return -1; - #else - static __thread unsigned int sThreadIndex = kNullIndex; - #endif -#else//__APPLE__ -#if __linux__ - static __thread unsigned int sThreadIndex = kNullIndex; +#if defined(BT_HAVE_TLS) + static __thread unsigned int sThreadIndex = kNullIndex; +#elif defined(_WIN32) + __declspec(thread) static unsigned int sThreadIndex = kNullIndex; #else - unsigned int sThreadIndex = 0; - return -1; + unsigned int sThreadIndex = 0; + return -1; #endif -#endif//__APPLE__ - -#endif - static int gThreadCounter=0; - if ( sThreadIndex == kNullIndex ) - { - sThreadIndex = gThreadCounter++; - } - return sThreadIndex; -#endif // #else // #if BT_THREADSAFE + static int gThreadCounter = 0; + + if (sThreadIndex == kNullIndex) { + sThreadIndex = gThreadCounter++; + } + return sThreadIndex; +#endif //BT_THREADSAFE } void btEnterProfileZoneDefault(const char* name) { - CProfileManager::Start_Profile( name ); } void btLeaveProfileZoneDefault() { - CProfileManager::Stop_Profile(); } diff --git a/thirdparty/bullet/LinearMath/btQuickprof.h b/thirdparty/bullet/LinearMath/btQuickprof.h index 7b38d71b90..98a2675771 100644 --- a/thirdparty/bullet/LinearMath/btQuickprof.h +++ b/thirdparty/bullet/LinearMath/btQuickprof.h @@ -70,11 +70,12 @@ void btSetCustomLeaveProfileZoneFunc(btLeaveProfileZoneFunc* leaveFunc); //#define BT_NO_PROFILE 1 #endif //BT_NO_PROFILE +const unsigned int BT_QUICKPROF_MAX_THREAD_COUNT = 64; + #ifndef BT_NO_PROFILE //btQuickprofGetCurrentThreadIndex will return -1 if thread index cannot be determined, //otherwise returns thread index in range [0..maxThreads] unsigned int btQuickprofGetCurrentThreadIndex2(); -const unsigned int BT_QUICKPROF_MAX_THREAD_COUNT = 64; #include <stdio.h>//@todo remove this, backwards compatibility diff --git a/thirdparty/bullet/LinearMath/btScalar.h b/thirdparty/bullet/LinearMath/btScalar.h index bffb2ce274..24e8454c1f 100644 --- a/thirdparty/bullet/LinearMath/btScalar.h +++ b/thirdparty/bullet/LinearMath/btScalar.h @@ -25,7 +25,7 @@ subject to the following restrictions: #include <float.h> /* SVN $Revision$ on $Date$ from http://bullet.googlecode.com*/ -#define BT_BULLET_VERSION 287 +#define BT_BULLET_VERSION 288 inline int btGetVersion() { diff --git a/thirdparty/bullet/LinearMath/btSerializer.cpp b/thirdparty/bullet/LinearMath/btSerializer.cpp index fcd2255ad5..4faa8f536b 100644 --- a/thirdparty/bullet/LinearMath/btSerializer.cpp +++ b/thirdparty/bullet/LinearMath/btSerializer.cpp @@ -1,5 +1,5 @@ char sBulletDNAstr[]= { -char(83),char(68),char(78),char(65),char(78),char(65),char(77),char(69),char(-124),char(1),char(0),char(0),char(109),char(95),char(115),char(105),char(122),char(101),char(0),char(109), +char(83),char(68),char(78),char(65),char(78),char(65),char(77),char(69),char(-76),char(1),char(0),char(0),char(109),char(95),char(115),char(105),char(122),char(101),char(0),char(109), char(95),char(99),char(97),char(112),char(97),char(99),char(105),char(116),char(121),char(0),char(42),char(109),char(95),char(100),char(97),char(116),char(97),char(0),char(109),char(95), char(99),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(83),char(104),char(97),char(112),char(101),char(115),char(0),char(109),char(95),char(99),char(111), char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(79),char(98),char(106),char(101),char(99),char(116),char(115),char(0),char(109),char(95),char(99),char(111),char(110), @@ -72,528 +72,618 @@ char(105),char(115),char(116),char(97),char(110),char(99),char(101),char(84),cha char(101),char(114),char(111),char(65),char(114),char(101),char(97),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(110), char(101),char(120),char(116),char(83),char(105),char(122),char(101),char(0),char(109),char(95),char(104),char(97),char(115),char(104),char(84),char(97),char(98),char(108),char(101),char(83), char(105),char(122),char(101),char(0),char(109),char(95),char(110),char(117),char(109),char(86),char(97),char(108),char(117),char(101),char(115),char(0),char(109),char(95),char(110),char(117), -char(109),char(75),char(101),char(121),char(115),char(0),char(109),char(95),char(103),char(105),char(109),char(112),char(97),char(99),char(116),char(83),char(117),char(98),char(84),char(121), -char(112),char(101),char(0),char(42),char(109),char(95),char(117),char(110),char(115),char(99),char(97),char(108),char(101),char(100),char(80),char(111),char(105),char(110),char(116),char(115), -char(70),char(108),char(111),char(97),char(116),char(80),char(116),char(114),char(0),char(42),char(109),char(95),char(117),char(110),char(115),char(99),char(97),char(108),char(101),char(100), -char(80),char(111),char(105),char(110),char(116),char(115),char(68),char(111),char(117),char(98),char(108),char(101),char(80),char(116),char(114),char(0),char(109),char(95),char(110),char(117), -char(109),char(85),char(110),char(115),char(99),char(97),char(108),char(101),char(100),char(80),char(111),char(105),char(110),char(116),char(115),char(0),char(109),char(95),char(112),char(97), -char(100),char(100),char(105),char(110),char(103),char(51),char(91),char(52),char(93),char(0),char(42),char(109),char(95),char(98),char(114),char(111),char(97),char(100),char(112),char(104), -char(97),char(115),char(101),char(72),char(97),char(110),char(100),char(108),char(101),char(0),char(42),char(109),char(95),char(99),char(111),char(108),char(108),char(105),char(115),char(105), -char(111),char(110),char(83),char(104),char(97),char(112),char(101),char(0),char(42),char(109),char(95),char(114),char(111),char(111),char(116),char(67),char(111),char(108),char(108),char(105), -char(115),char(105),char(111),char(110),char(83),char(104),char(97),char(112),char(101),char(0),char(109),char(95),char(119),char(111),char(114),char(108),char(100),char(84),char(114),char(97), -char(110),char(115),char(102),char(111),char(114),char(109),char(0),char(109),char(95),char(105),char(110),char(116),char(101),char(114),char(112),char(111),char(108),char(97),char(116),char(105), -char(111),char(110),char(87),char(111),char(114),char(108),char(100),char(84),char(114),char(97),char(110),char(115),char(102),char(111),char(114),char(109),char(0),char(109),char(95),char(105), -char(110),char(116),char(101),char(114),char(112),char(111),char(108),char(97),char(116),char(105),char(111),char(110),char(76),char(105),char(110),char(101),char(97),char(114),char(86),char(101), -char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(105),char(110),char(116),char(101),char(114),char(112),char(111),char(108),char(97),char(116),char(105), -char(111),char(110),char(65),char(110),char(103),char(117),char(108),char(97),char(114),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95), -char(97),char(110),char(105),char(115),char(111),char(116),char(114),char(111),char(112),char(105),char(99),char(70),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(0), -char(109),char(95),char(99),char(111),char(110),char(116),char(97),char(99),char(116),char(80),char(114),char(111),char(99),char(101),char(115),char(115),char(105),char(110),char(103),char(84), -char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(100),char(101),char(97),char(99),char(116),char(105),char(118),char(97),char(116), -char(105),char(111),char(110),char(84),char(105),char(109),char(101),char(0),char(109),char(95),char(102),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(0),char(109), -char(95),char(114),char(111),char(108),char(108),char(105),char(110),char(103),char(70),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(99), -char(111),char(110),char(116),char(97),char(99),char(116),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(99),char(111),char(110),char(116), -char(97),char(99),char(116),char(83),char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(114),char(101),char(115),char(116),char(105), -char(116),char(117),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(104),char(105),char(116),char(70),char(114),char(97),char(99),char(116),char(105),char(111),char(110), -char(0),char(109),char(95),char(99),char(99),char(100),char(83),char(119),char(101),char(112),char(116),char(83),char(112),char(104),char(101),char(114),char(101),char(82),char(97),char(100), -char(105),char(117),char(115),char(0),char(109),char(95),char(99),char(99),char(100),char(77),char(111),char(116),char(105),char(111),char(110),char(84),char(104),char(114),char(101),char(115), -char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(104),char(97),char(115),char(65),char(110),char(105),char(115),char(111),char(116),char(114),char(111),char(112),char(105), -char(99),char(70),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(99),char(111),char(108),char(108),char(105),char(115),char(105),char(111), -char(110),char(70),char(108),char(97),char(103),char(115),char(0),char(109),char(95),char(105),char(115),char(108),char(97),char(110),char(100),char(84),char(97),char(103),char(49),char(0), -char(109),char(95),char(99),char(111),char(109),char(112),char(97),char(110),char(105),char(111),char(110),char(73),char(100),char(0),char(109),char(95),char(97),char(99),char(116),char(105), -char(118),char(97),char(116),char(105),char(111),char(110),char(83),char(116),char(97),char(116),char(101),char(49),char(0),char(109),char(95),char(105),char(110),char(116),char(101),char(114), -char(110),char(97),char(108),char(84),char(121),char(112),char(101),char(0),char(109),char(95),char(99),char(104),char(101),char(99),char(107),char(67),char(111),char(108),char(108),char(105), -char(100),char(101),char(87),char(105),char(116),char(104),char(0),char(109),char(95),char(116),char(97),char(117),char(0),char(109),char(95),char(100),char(97),char(109),char(112),char(105), -char(110),char(103),char(0),char(109),char(95),char(116),char(105),char(109),char(101),char(83),char(116),char(101),char(112),char(0),char(109),char(95),char(109),char(97),char(120),char(69), -char(114),char(114),char(111),char(114),char(82),char(101),char(100),char(117),char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(115),char(111),char(114),char(0), -char(109),char(95),char(101),char(114),char(112),char(0),char(109),char(95),char(101),char(114),char(112),char(50),char(0),char(109),char(95),char(103),char(108),char(111),char(98),char(97), -char(108),char(67),char(102),char(109),char(0),char(109),char(95),char(115),char(112),char(108),char(105),char(116),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(80), -char(101),char(110),char(101),char(116),char(114),char(97),char(116),char(105),char(111),char(110),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0), -char(109),char(95),char(115),char(112),char(108),char(105),char(116),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(84),char(117),char(114),char(110),char(69),char(114), -char(112),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(108),char(111),char(112),char(0),char(109),char(95),char(119),char(97),char(114), -char(109),char(115),char(116),char(97),char(114),char(116),char(105),char(110),char(103),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(109),char(97), -char(120),char(71),char(121),char(114),char(111),char(115),char(99),char(111),char(112),char(105),char(99),char(70),char(111),char(114),char(99),char(101),char(0),char(109),char(95),char(115), -char(105),char(110),char(103),char(108),char(101),char(65),char(120),char(105),char(115),char(82),char(111),char(108),char(108),char(105),char(110),char(103),char(70),char(114),char(105),char(99), -char(116),char(105),char(111),char(110),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(110),char(117),char(109),char(73), -char(116),char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95),char(115),char(111),char(108),char(118),char(101),char(114),char(77),char(111), -char(100),char(101),char(0),char(109),char(95),char(114),char(101),char(115),char(116),char(105),char(110),char(103),char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(82), -char(101),char(115),char(116),char(105),char(116),char(117),char(116),char(105),char(111),char(110),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0), -char(109),char(95),char(109),char(105),char(110),char(105),char(109),char(117),char(109),char(83),char(111),char(108),char(118),char(101),char(114),char(66),char(97),char(116),char(99),char(104), -char(83),char(105),char(122),char(101),char(0),char(109),char(95),char(115),char(112),char(108),char(105),char(116),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(0), -char(109),char(95),char(115),char(111),char(108),char(118),char(101),char(114),char(73),char(110),char(102),char(111),char(0),char(109),char(95),char(103),char(114),char(97),char(118),char(105), -char(116),char(121),char(0),char(109),char(95),char(99),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(79),char(98),char(106),char(101),char(99),char(116), -char(68),char(97),char(116),char(97),char(0),char(109),char(95),char(105),char(110),char(118),char(73),char(110),char(101),char(114),char(116),char(105),char(97),char(84),char(101),char(110), -char(115),char(111),char(114),char(87),char(111),char(114),char(108),char(100),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(86),char(101),char(108), -char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(86),char(101),char(108),char(111),char(99), -char(105),char(116),char(121),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(70),char(97),char(99),char(116),char(111),char(114),char(0), -char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(103),char(114),char(97), -char(118),char(105),char(116),char(121),char(95),char(97),char(99),char(99),char(101),char(108),char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(0),char(109),char(95), -char(105),char(110),char(118),char(73),char(110),char(101),char(114),char(116),char(105),char(97),char(76),char(111),char(99),char(97),char(108),char(0),char(109),char(95),char(116),char(111), -char(116),char(97),char(108),char(70),char(111),char(114),char(99),char(101),char(0),char(109),char(95),char(116),char(111),char(116),char(97),char(108),char(84),char(111),char(114),char(113), -char(117),char(101),char(0),char(109),char(95),char(105),char(110),char(118),char(101),char(114),char(115),char(101),char(77),char(97),char(115),char(115),char(0),char(109),char(95),char(108), -char(105),char(110),char(101),char(97),char(114),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108), -char(97),char(114),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(97),char(100),char(100),char(105),char(116),char(105),char(111),char(110), -char(97),char(108),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(97),char(100), -char(100),char(105),char(116),char(105),char(111),char(110),char(97),char(108),char(76),char(105),char(110),char(101),char(97),char(114),char(68),char(97),char(109),char(112),char(105),char(110), -char(103),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(83),char(113),char(114),char(0),char(109),char(95),char(97),char(100),char(100),char(105), -char(116),char(105),char(111),char(110),char(97),char(108),char(65),char(110),char(103),char(117),char(108),char(97),char(114),char(68),char(97),char(109),char(112),char(105),char(110),char(103), -char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(83),char(113),char(114),char(0),char(109),char(95),char(97),char(100),char(100),char(105),char(116), -char(105),char(111),char(110),char(97),char(108),char(65),char(110),char(103),char(117),char(108),char(97),char(114),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(70), -char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(108),char(101),char(101),char(112),char(105), -char(110),char(103),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97), -char(114),char(83),char(108),char(101),char(101),char(112),char(105),char(110),char(103),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109), -char(95),char(97),char(100),char(100),char(105),char(116),char(105),char(111),char(110),char(97),char(108),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109), -char(95),char(110),char(117),char(109),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(82),char(111),char(119),char(115),char(0),char(110), -char(117),char(98),char(0),char(42),char(109),char(95),char(114),char(98),char(65),char(0),char(42),char(109),char(95),char(114),char(98),char(66),char(0),char(109),char(95),char(111), -char(98),char(106),char(101),char(99),char(116),char(84),char(121),char(112),char(101),char(0),char(109),char(95),char(117),char(115),char(101),char(114),char(67),char(111),char(110),char(115), -char(116),char(114),char(97),char(105),char(110),char(116),char(84),char(121),char(112),char(101),char(0),char(109),char(95),char(117),char(115),char(101),char(114),char(67),char(111),char(110), -char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(73),char(100),char(0),char(109),char(95),char(110),char(101),char(101),char(100),char(115),char(70),char(101),char(101), -char(100),char(98),char(97),char(99),char(107),char(0),char(109),char(95),char(97),char(112),char(112),char(108),char(105),char(101),char(100),char(73),char(109),char(112),char(117),char(108), -char(115),char(101),char(0),char(109),char(95),char(100),char(98),char(103),char(68),char(114),char(97),char(119),char(83),char(105),char(122),char(101),char(0),char(109),char(95),char(100), -char(105),char(115),char(97),char(98),char(108),char(101),char(67),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(115),char(66),char(101),char(116),char(119), -char(101),char(101),char(110),char(76),char(105),char(110),char(107),char(101),char(100),char(66),char(111),char(100),char(105),char(101),char(115),char(0),char(109),char(95),char(111),char(118), -char(101),char(114),char(114),char(105),char(100),char(101),char(78),char(117),char(109),char(83),char(111),char(108),char(118),char(101),char(114),char(73),char(116),char(101),char(114),char(97), -char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95),char(98),char(114),char(101),char(97),char(107),char(105),char(110),char(103),char(73),char(109),char(112),char(117), -char(108),char(115),char(101),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(105),char(115),char(69),char(110),char(97), -char(98),char(108),char(101),char(100),char(0),char(112),char(97),char(100),char(100),char(105),char(110),char(103),char(91),char(52),char(93),char(0),char(109),char(95),char(116),char(121), -char(112),char(101),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(109),char(95),char(112), -char(105),char(118),char(111),char(116),char(73),char(110),char(65),char(0),char(109),char(95),char(112),char(105),char(118),char(111),char(116),char(73),char(110),char(66),char(0),char(109), -char(95),char(114),char(98),char(65),char(70),char(114),char(97),char(109),char(101),char(0),char(109),char(95),char(114),char(98),char(66),char(70),char(114),char(97),char(109),char(101), -char(0),char(109),char(95),char(117),char(115),char(101),char(82),char(101),char(102),char(101),char(114),char(101),char(110),char(99),char(101),char(70),char(114),char(97),char(109),char(101), -char(65),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(79),char(110),char(108),char(121),char(0),char(109),char(95),char(101),char(110), -char(97),char(98),char(108),char(101),char(65),char(110),char(103),char(117),char(108),char(97),char(114),char(77),char(111),char(116),char(111),char(114),char(0),char(109),char(95),char(109), -char(111),char(116),char(111),char(114),char(84),char(97),char(114),char(103),char(101),char(116),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109), -char(95),char(109),char(97),char(120),char(77),char(111),char(116),char(111),char(114),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(0),char(109),char(95),char(108), -char(111),char(119),char(101),char(114),char(76),char(105),char(109),char(105),char(116),char(0),char(109),char(95),char(117),char(112),char(112),char(101),char(114),char(76),char(105),char(109), -char(105),char(116),char(0),char(109),char(95),char(108),char(105),char(109),char(105),char(116),char(83),char(111),char(102),char(116),char(110),char(101),char(115),char(115),char(0),char(109), -char(95),char(98),char(105),char(97),char(115),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(114),char(101),char(108),char(97),char(120),char(97), -char(116),char(105),char(111),char(110),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(112),char(97),char(100),char(100),char(105),char(110),char(103), -char(49),char(91),char(52),char(93),char(0),char(109),char(95),char(115),char(119),char(105),char(110),char(103),char(83),char(112),char(97),char(110),char(49),char(0),char(109),char(95), -char(115),char(119),char(105),char(110),char(103),char(83),char(112),char(97),char(110),char(50),char(0),char(109),char(95),char(116),char(119),char(105),char(115),char(116),char(83),char(112), -char(97),char(110),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(85),char(112),char(112),char(101),char(114),char(76),char(105),char(109),char(105), -char(116),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(76),char(111),char(119),char(101),char(114),char(76),char(105),char(109),char(105),char(116), -char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(85),char(112),char(112),char(101),char(114),char(76),char(105),char(109),char(105),char(116), -char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(76),char(111),char(119),char(101),char(114),char(76),char(105),char(109),char(105),char(116), -char(0),char(109),char(95),char(117),char(115),char(101),char(76),char(105),char(110),char(101),char(97),char(114),char(82),char(101),char(102),char(101),char(114),char(101),char(110),char(99), -char(101),char(70),char(114),char(97),char(109),char(101),char(65),char(0),char(109),char(95),char(117),char(115),char(101),char(79),char(102),char(102),char(115),char(101),char(116),char(70), -char(111),char(114),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(70),char(114),char(97),char(109),char(101),char(0),char(109),char(95), -char(54),char(100),char(111),char(102),char(68),char(97),char(116),char(97),char(0),char(109),char(95),char(115),char(112),char(114),char(105),char(110),char(103),char(69),char(110),char(97), -char(98),char(108),char(101),char(100),char(91),char(54),char(93),char(0),char(109),char(95),char(101),char(113),char(117),char(105),char(108),char(105),char(98),char(114),char(105),char(117), -char(109),char(80),char(111),char(105),char(110),char(116),char(91),char(54),char(93),char(0),char(109),char(95),char(115),char(112),char(114),char(105),char(110),char(103),char(83),char(116), -char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(91),char(54),char(93),char(0),char(109),char(95),char(115),char(112),char(114),char(105),char(110),char(103),char(68), -char(97),char(109),char(112),char(105),char(110),char(103),char(91),char(54),char(93),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(66),char(111), -char(117),char(110),char(99),char(101),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(116),char(111),char(112),char(69),char(82),char(80), -char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(116),char(111),char(112),char(67),char(70),char(77),char(0),char(109),char(95),char(108), -char(105),char(110),char(101),char(97),char(114),char(77),char(111),char(116),char(111),char(114),char(69),char(82),char(80),char(0),char(109),char(95),char(108),char(105),char(110),char(101), -char(97),char(114),char(77),char(111),char(116),char(111),char(114),char(67),char(70),char(77),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(84), -char(97),char(114),char(103),char(101),char(116),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(108),char(105),char(110),char(101), -char(97),char(114),char(77),char(97),char(120),char(77),char(111),char(116),char(111),char(114),char(70),char(111),char(114),char(99),char(101),char(0),char(109),char(95),char(108),char(105), -char(110),char(101),char(97),char(114),char(83),char(101),char(114),char(118),char(111),char(84),char(97),char(114),char(103),char(101),char(116),char(0),char(109),char(95),char(108),char(105), -char(110),char(101),char(97),char(114),char(83),char(112),char(114),char(105),char(110),char(103),char(83),char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(0), -char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(112),char(114),char(105),char(110),char(103),char(68),char(97),char(109),char(112),char(105),char(110), -char(103),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(69),char(113),char(117),char(105),char(108),char(105),char(98),char(114),char(105),char(117), -char(109),char(80),char(111),char(105),char(110),char(116),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(69),char(110),char(97),char(98),char(108), -char(101),char(77),char(111),char(116),char(111),char(114),char(91),char(52),char(93),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(101), -char(114),char(118),char(111),char(77),char(111),char(116),char(111),char(114),char(91),char(52),char(93),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114), -char(69),char(110),char(97),char(98),char(108),char(101),char(83),char(112),char(114),char(105),char(110),char(103),char(91),char(52),char(93),char(0),char(109),char(95),char(108),char(105), -char(110),char(101),char(97),char(114),char(83),char(112),char(114),char(105),char(110),char(103),char(83),char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(76), -char(105),char(109),char(105),char(116),char(101),char(100),char(91),char(52),char(93),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(112), -char(114),char(105),char(110),char(103),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(76),char(105),char(109),char(105),char(116),char(101),char(100),char(91),char(52), -char(93),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(66),char(111),char(117),char(110),char(99),char(101),char(0),char(109),char(95), -char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(83),char(116),char(111),char(112),char(69),char(82),char(80),char(0),char(109),char(95),char(97),char(110),char(103), -char(117),char(108),char(97),char(114),char(83),char(116),char(111),char(112),char(67),char(70),char(77),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97), -char(114),char(77),char(111),char(116),char(111),char(114),char(69),char(82),char(80),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(77), -char(111),char(116),char(111),char(114),char(67),char(70),char(77),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(84),char(97),char(114), -char(103),char(101),char(116),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97), -char(114),char(77),char(97),char(120),char(77),char(111),char(116),char(111),char(114),char(70),char(111),char(114),char(99),char(101),char(0),char(109),char(95),char(97),char(110),char(103), -char(117),char(108),char(97),char(114),char(83),char(101),char(114),char(118),char(111),char(84),char(97),char(114),char(103),char(101),char(116),char(0),char(109),char(95),char(97),char(110), -char(103),char(117),char(108),char(97),char(114),char(83),char(112),char(114),char(105),char(110),char(103),char(83),char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115), -char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(83),char(112),char(114),char(105),char(110),char(103),char(68),char(97),char(109),char(112), -char(105),char(110),char(103),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(69),char(113),char(117),char(105),char(108),char(105),char(98), -char(114),char(105),char(117),char(109),char(80),char(111),char(105),char(110),char(116),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(69), -char(110),char(97),char(98),char(108),char(101),char(77),char(111),char(116),char(111),char(114),char(91),char(52),char(93),char(0),char(109),char(95),char(97),char(110),char(103),char(117), -char(108),char(97),char(114),char(83),char(101),char(114),char(118),char(111),char(77),char(111),char(116),char(111),char(114),char(91),char(52),char(93),char(0),char(109),char(95),char(97), -char(110),char(103),char(117),char(108),char(97),char(114),char(69),char(110),char(97),char(98),char(108),char(101),char(83),char(112),char(114),char(105),char(110),char(103),char(91),char(52), -char(93),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(83),char(112),char(114),char(105),char(110),char(103),char(83),char(116),char(105), -char(102),char(102),char(110),char(101),char(115),char(115),char(76),char(105),char(109),char(105),char(116),char(101),char(100),char(91),char(52),char(93),char(0),char(109),char(95),char(97), -char(110),char(103),char(117),char(108),char(97),char(114),char(83),char(112),char(114),char(105),char(110),char(103),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(76), -char(105),char(109),char(105),char(116),char(101),char(100),char(91),char(52),char(93),char(0),char(109),char(95),char(114),char(111),char(116),char(97),char(116),char(101),char(79),char(114), -char(100),char(101),char(114),char(0),char(109),char(95),char(97),char(120),char(105),char(115),char(73),char(110),char(65),char(0),char(109),char(95),char(97),char(120),char(105),char(115), -char(73),char(110),char(66),char(0),char(109),char(95),char(114),char(97),char(116),char(105),char(111),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114), -char(83),char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(83), -char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(118),char(111),char(108),char(117),char(109),char(101),char(83),char(116),char(105), -char(102),char(102),char(110),char(101),char(115),char(115),char(0),char(42),char(109),char(95),char(109),char(97),char(116),char(101),char(114),char(105),char(97),char(108),char(0),char(109), -char(95),char(112),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(112),char(114),char(101),char(118),char(105),char(111),char(117),char(115), -char(80),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(118),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0), -char(109),char(95),char(97),char(99),char(99),char(117),char(109),char(117),char(108),char(97),char(116),char(101),char(100),char(70),char(111),char(114),char(99),char(101),char(0),char(109), -char(95),char(110),char(111),char(114),char(109),char(97),char(108),char(0),char(109),char(95),char(97),char(114),char(101),char(97),char(0),char(109),char(95),char(97),char(116),char(116), -char(97),char(99),char(104),char(0),char(109),char(95),char(110),char(111),char(100),char(101),char(73),char(110),char(100),char(105),char(99),char(101),char(115),char(91),char(50),char(93), -char(0),char(109),char(95),char(114),char(101),char(115),char(116),char(76),char(101),char(110),char(103),char(116),char(104),char(0),char(109),char(95),char(98),char(98),char(101),char(110), -char(100),char(105),char(110),char(103),char(0),char(109),char(95),char(110),char(111),char(100),char(101),char(73),char(110),char(100),char(105),char(99),char(101),char(115),char(91),char(51), -char(93),char(0),char(109),char(95),char(114),char(101),char(115),char(116),char(65),char(114),char(101),char(97),char(0),char(109),char(95),char(99),char(48),char(91),char(52),char(93), -char(0),char(109),char(95),char(110),char(111),char(100),char(101),char(73),char(110),char(100),char(105),char(99),char(101),char(115),char(91),char(52),char(93),char(0),char(109),char(95), -char(114),char(101),char(115),char(116),char(86),char(111),char(108),char(117),char(109),char(101),char(0),char(109),char(95),char(99),char(49),char(0),char(109),char(95),char(99),char(50), -char(0),char(109),char(95),char(99),char(48),char(0),char(109),char(95),char(108),char(111),char(99),char(97),char(108),char(70),char(114),char(97),char(109),char(101),char(0),char(42), -char(109),char(95),char(114),char(105),char(103),char(105),char(100),char(66),char(111),char(100),char(121),char(0),char(109),char(95),char(110),char(111),char(100),char(101),char(73),char(110), -char(100),char(101),char(120),char(0),char(109),char(95),char(97),char(101),char(114),char(111),char(77),char(111),char(100),char(101),char(108),char(0),char(109),char(95),char(98),char(97), -char(117),char(109),char(103),char(97),char(114),char(116),char(101),char(0),char(109),char(95),char(100),char(114),char(97),char(103),char(0),char(109),char(95),char(108),char(105),char(102), -char(116),char(0),char(109),char(95),char(112),char(114),char(101),char(115),char(115),char(117),char(114),char(101),char(0),char(109),char(95),char(118),char(111),char(108),char(117),char(109), -char(101),char(0),char(109),char(95),char(100),char(121),char(110),char(97),char(109),char(105),char(99),char(70),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(0), -char(109),char(95),char(112),char(111),char(115),char(101),char(77),char(97),char(116),char(99),char(104),char(0),char(109),char(95),char(114),char(105),char(103),char(105),char(100),char(67), -char(111),char(110),char(116),char(97),char(99),char(116),char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(107),char(105),char(110), -char(101),char(116),char(105),char(99),char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115),char(0), -char(109),char(95),char(115),char(111),char(102),char(116),char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(72),char(97),char(114),char(100),char(110),char(101),char(115), -char(115),char(0),char(109),char(95),char(97),char(110),char(99),char(104),char(111),char(114),char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115),char(0),char(109), -char(95),char(115),char(111),char(102),char(116),char(82),char(105),char(103),char(105),char(100),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(72),char(97),char(114), -char(100),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(75),char(105),char(110),char(101),char(116),char(105),char(99),char(67), -char(108),char(117),char(115),char(116),char(101),char(114),char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(115),char(111),char(102), -char(116),char(83),char(111),char(102),char(116),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115), -char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(82),char(105),char(103),char(105),char(100),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(73), -char(109),char(112),char(117),char(108),char(115),char(101),char(83),char(112),char(108),char(105),char(116),char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(75),char(105), -char(110),char(101),char(116),char(105),char(99),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(83), -char(112),char(108),char(105),char(116),char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(83),char(111),char(102),char(116),char(67),char(108),char(117),char(115),char(116), -char(101),char(114),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(83),char(112),char(108),char(105),char(116),char(0),char(109),char(95),char(109),char(97),char(120), -char(86),char(111),char(108),char(117),char(109),char(101),char(0),char(109),char(95),char(116),char(105),char(109),char(101),char(83),char(99),char(97),char(108),char(101),char(0),char(109), -char(95),char(118),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(73),char(116),char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(115),char(0), -char(109),char(95),char(112),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(73),char(116),char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(115), -char(0),char(109),char(95),char(100),char(114),char(105),char(102),char(116),char(73),char(116),char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(115),char(0),char(109), -char(95),char(99),char(108),char(117),char(115),char(116),char(101),char(114),char(73),char(116),char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(115),char(0),char(109), -char(95),char(114),char(111),char(116),char(0),char(109),char(95),char(115),char(99),char(97),char(108),char(101),char(0),char(109),char(95),char(97),char(113),char(113),char(0),char(109), -char(95),char(99),char(111),char(109),char(0),char(42),char(109),char(95),char(112),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(115),char(0),char(42),char(109), -char(95),char(119),char(101),char(105),char(103),char(104),char(116),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(80),char(111),char(115),char(105),char(116),char(105), -char(111),char(110),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(87),char(101),char(105),char(103),char(116),char(115),char(0),char(109),char(95),char(98),char(118), -char(111),char(108),char(117),char(109),char(101),char(0),char(109),char(95),char(98),char(102),char(114),char(97),char(109),char(101),char(0),char(109),char(95),char(102),char(114),char(97), -char(109),char(101),char(120),char(102),char(111),char(114),char(109),char(0),char(109),char(95),char(108),char(111),char(99),char(105),char(105),char(0),char(109),char(95),char(105),char(110), -char(118),char(119),char(105),char(0),char(109),char(95),char(118),char(105),char(109),char(112),char(117),char(108),char(115),char(101),char(115),char(91),char(50),char(93),char(0),char(109), -char(95),char(100),char(105),char(109),char(112),char(117),char(108),char(115),char(101),char(115),char(91),char(50),char(93),char(0),char(109),char(95),char(108),char(118),char(0),char(109), -char(95),char(97),char(118),char(0),char(42),char(109),char(95),char(102),char(114),char(97),char(109),char(101),char(114),char(101),char(102),char(115),char(0),char(42),char(109),char(95), -char(110),char(111),char(100),char(101),char(73),char(110),char(100),char(105),char(99),char(101),char(115),char(0),char(42),char(109),char(95),char(109),char(97),char(115),char(115),char(101), -char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(70),char(114),char(97),char(109),char(101),char(82),char(101),char(102),char(115),char(0),char(109),char(95),char(110), -char(117),char(109),char(78),char(111),char(100),char(101),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(77),char(97),char(115),char(115),char(101),char(115),char(0), -char(109),char(95),char(105),char(100),char(109),char(97),char(115),char(115),char(0),char(109),char(95),char(105),char(109),char(97),char(115),char(115),char(0),char(109),char(95),char(110), -char(118),char(105),char(109),char(112),char(117),char(108),char(115),char(101),char(115),char(0),char(109),char(95),char(110),char(100),char(105),char(109),char(112),char(117),char(108),char(115), -char(101),char(115),char(0),char(109),char(95),char(110),char(100),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(108),char(100),char(97),char(109), -char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(97),char(100),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(109),char(97), -char(116),char(99),char(104),char(105),char(110),char(103),char(0),char(109),char(95),char(109),char(97),char(120),char(83),char(101),char(108),char(102),char(67),char(111),char(108),char(108), -char(105),char(115),char(105),char(111),char(110),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(0),char(109),char(95),char(115),char(101),char(108),char(102),char(67), -char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(70),char(97),char(99),char(116),char(111), -char(114),char(0),char(109),char(95),char(99),char(111),char(110),char(116),char(97),char(105),char(110),char(115),char(65),char(110),char(99),char(104),char(111),char(114),char(0),char(109), -char(95),char(99),char(111),char(108),char(108),char(105),char(100),char(101),char(0),char(109),char(95),char(99),char(108),char(117),char(115),char(116),char(101),char(114),char(73),char(110), -char(100),char(101),char(120),char(0),char(42),char(109),char(95),char(98),char(111),char(100),char(121),char(65),char(0),char(42),char(109),char(95),char(98),char(111),char(100),char(121), -char(66),char(0),char(109),char(95),char(114),char(101),char(102),char(115),char(91),char(50),char(93),char(0),char(109),char(95),char(99),char(102),char(109),char(0),char(109),char(95), -char(115),char(112),char(108),char(105),char(116),char(0),char(109),char(95),char(100),char(101),char(108),char(101),char(116),char(101),char(0),char(109),char(95),char(114),char(101),char(108), -char(80),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(91),char(50),char(93),char(0),char(109),char(95),char(98),char(111),char(100),char(121),char(65),char(116), -char(121),char(112),char(101),char(0),char(109),char(95),char(98),char(111),char(100),char(121),char(66),char(116),char(121),char(112),char(101),char(0),char(109),char(95),char(106),char(111), -char(105),char(110),char(116),char(84),char(121),char(112),char(101),char(0),char(42),char(109),char(95),char(112),char(111),char(115),char(101),char(0),char(42),char(42),char(109),char(95), -char(109),char(97),char(116),char(101),char(114),char(105),char(97),char(108),char(115),char(0),char(42),char(109),char(95),char(110),char(111),char(100),char(101),char(115),char(0),char(42), -char(109),char(95),char(108),char(105),char(110),char(107),char(115),char(0),char(42),char(109),char(95),char(102),char(97),char(99),char(101),char(115),char(0),char(42),char(109),char(95), -char(116),char(101),char(116),char(114),char(97),char(104),char(101),char(100),char(114),char(97),char(0),char(42),char(109),char(95),char(97),char(110),char(99),char(104),char(111),char(114), -char(115),char(0),char(42),char(109),char(95),char(99),char(108),char(117),char(115),char(116),char(101),char(114),char(115),char(0),char(42),char(109),char(95),char(106),char(111),char(105), -char(110),char(116),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(77),char(97),char(116),char(101),char(114),char(105),char(97),char(108),char(115),char(0),char(109), -char(95),char(110),char(117),char(109),char(76),char(105),char(110),char(107),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(70),char(97),char(99),char(101),char(115), -char(0),char(109),char(95),char(110),char(117),char(109),char(84),char(101),char(116),char(114),char(97),char(104),char(101),char(100),char(114),char(97),char(0),char(109),char(95),char(110), -char(117),char(109),char(65),char(110),char(99),char(104),char(111),char(114),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(67),char(108),char(117),char(115),char(116), -char(101),char(114),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(74),char(111),char(105),char(110),char(116),char(115),char(0),char(109),char(95),char(99),char(111), -char(110),char(102),char(105),char(103),char(0),char(109),char(95),char(122),char(101),char(114),char(111),char(82),char(111),char(116),char(80),char(97),char(114),char(101),char(110),char(116), -char(84),char(111),char(84),char(104),char(105),char(115),char(0),char(109),char(95),char(112),char(97),char(114),char(101),char(110),char(116),char(67),char(111),char(109),char(84),char(111), -char(84),char(104),char(105),char(115),char(67),char(111),char(109),char(79),char(102),char(102),char(115),char(101),char(116),char(0),char(109),char(95),char(116),char(104),char(105),char(115), -char(80),char(105),char(118),char(111),char(116),char(84),char(111),char(84),char(104),char(105),char(115),char(67),char(111),char(109),char(79),char(102),char(102),char(115),char(101),char(116), -char(0),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(65),char(120),char(105),char(115),char(84),char(111),char(112),char(91),char(54),char(93),char(0),char(109), -char(95),char(106),char(111),char(105),char(110),char(116),char(65),char(120),char(105),char(115),char(66),char(111),char(116),char(116),char(111),char(109),char(91),char(54),char(93),char(0), -char(109),char(95),char(108),char(105),char(110),char(107),char(73),char(110),char(101),char(114),char(116),char(105),char(97),char(0),char(109),char(95),char(108),char(105),char(110),char(107), -char(77),char(97),char(115),char(115),char(0),char(109),char(95),char(112),char(97),char(114),char(101),char(110),char(116),char(73),char(110),char(100),char(101),char(120),char(0),char(109), -char(95),char(100),char(111),char(102),char(67),char(111),char(117),char(110),char(116),char(0),char(109),char(95),char(112),char(111),char(115),char(86),char(97),char(114),char(67),char(111), -char(117),char(110),char(116),char(0),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(80),char(111),char(115),char(91),char(55),char(93),char(0),char(109),char(95), -char(106),char(111),char(105),char(110),char(116),char(86),char(101),char(108),char(91),char(54),char(93),char(0),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(84), -char(111),char(114),char(113),char(117),char(101),char(91),char(54),char(93),char(0),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(68),char(97),char(109),char(112), -char(105),char(110),char(103),char(0),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(70),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(0), -char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(76),char(111),char(119),char(101),char(114),char(76),char(105),char(109),char(105),char(116),char(0),char(109),char(95), -char(106),char(111),char(105),char(110),char(116),char(85),char(112),char(112),char(101),char(114),char(76),char(105),char(109),char(105),char(116),char(0),char(109),char(95),char(106),char(111), -char(105),char(110),char(116),char(77),char(97),char(120),char(70),char(111),char(114),char(99),char(101),char(0),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(77), -char(97),char(120),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(42),char(109),char(95),char(108),char(105),char(110),char(107),char(78),char(97), -char(109),char(101),char(0),char(42),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(78),char(97),char(109),char(101),char(0),char(42),char(109),char(95),char(108), -char(105),char(110),char(107),char(67),char(111),char(108),char(108),char(105),char(100),char(101),char(114),char(0),char(42),char(109),char(95),char(112),char(97),char(100),char(100),char(105), -char(110),char(103),char(80),char(116),char(114),char(0),char(109),char(95),char(98),char(97),char(115),char(101),char(87),char(111),char(114),char(108),char(100),char(84),char(114),char(97), -char(110),char(115),char(102),char(111),char(114),char(109),char(0),char(109),char(95),char(98),char(97),char(115),char(101),char(73),char(110),char(101),char(114),char(116),char(105),char(97), -char(0),char(109),char(95),char(98),char(97),char(115),char(101),char(77),char(97),char(115),char(115),char(0),char(42),char(109),char(95),char(98),char(97),char(115),char(101),char(78), -char(97),char(109),char(101),char(0),char(42),char(109),char(95),char(98),char(97),char(115),char(101),char(67),char(111),char(108),char(108),char(105),char(100),char(101),char(114),char(0), -char(84),char(89),char(80),char(69),char(95),char(0),char(0),char(0),char(99),char(104),char(97),char(114),char(0),char(117),char(99),char(104),char(97),char(114),char(0),char(115), -char(104),char(111),char(114),char(116),char(0),char(117),char(115),char(104),char(111),char(114),char(116),char(0),char(105),char(110),char(116),char(0),char(108),char(111),char(110),char(103), -char(0),char(117),char(108),char(111),char(110),char(103),char(0),char(102),char(108),char(111),char(97),char(116),char(0),char(100),char(111),char(117),char(98),char(108),char(101),char(0), -char(118),char(111),char(105),char(100),char(0),char(80),char(111),char(105),char(110),char(116),char(101),char(114),char(65),char(114),char(114),char(97),char(121),char(0),char(98),char(116), -char(80),char(104),char(121),char(115),char(105),char(99),char(115),char(83),char(121),char(115),char(116),char(101),char(109),char(0),char(76),char(105),char(115),char(116),char(66),char(97), -char(115),char(101),char(0),char(98),char(116),char(86),char(101),char(99),char(116),char(111),char(114),char(51),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116), -char(97),char(0),char(98),char(116),char(86),char(101),char(99),char(116),char(111),char(114),char(51),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116), -char(97),char(0),char(98),char(116),char(81),char(117),char(97),char(116),char(101),char(114),char(110),char(105),char(111),char(110),char(70),char(108),char(111),char(97),char(116),char(68), -char(97),char(116),char(97),char(0),char(98),char(116),char(81),char(117),char(97),char(116),char(101),char(114),char(110),char(105),char(111),char(110),char(68),char(111),char(117),char(98), -char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(77),char(97),char(116),char(114),char(105),char(120),char(51),char(120),char(51),char(70),char(108), -char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(77),char(97),char(116),char(114),char(105),char(120),char(51),char(120),char(51),char(68), -char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(84),char(114),char(97),char(110),char(115),char(102),char(111),char(114), -char(109),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(84),char(114),char(97),char(110),char(115),char(102),char(111), -char(114),char(109),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(66),char(118),char(104),char(83),char(117), -char(98),char(116),char(114),char(101),char(101),char(73),char(110),char(102),char(111),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(79),char(112),char(116),char(105), -char(109),char(105),char(122),char(101),char(100),char(66),char(118),char(104),char(78),char(111),char(100),char(101),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116), +char(109),char(75),char(101),char(121),char(115),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99),char(104),char(101),char(76),char(111), +char(99),char(97),char(108),char(80),char(111),char(105),char(110),char(116),char(65),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116), +char(67),char(97),char(99),char(104),char(101),char(76),char(111),char(99),char(97),char(108),char(80),char(111),char(105),char(110),char(116),char(66),char(91),char(52),char(93),char(0), +char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99),char(104),char(101),char(80),char(111),char(115),char(105),char(116),char(105),char(111),char(110), +char(87),char(111),char(114),char(108),char(100),char(79),char(110),char(65),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67), +char(97),char(99),char(104),char(101),char(80),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(87),char(111),char(114),char(108),char(100),char(79),char(110),char(66), +char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99),char(104),char(101),char(78),char(111),char(114),char(109), +char(97),char(108),char(87),char(111),char(114),char(108),char(100),char(79),char(110),char(66),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110), +char(116),char(67),char(97),char(99),char(104),char(101),char(76),char(97),char(116),char(101),char(114),char(97),char(108),char(70),char(114),char(105),char(99),char(116),char(105),char(111), +char(110),char(68),char(105),char(114),char(49),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99),char(104), +char(101),char(76),char(97),char(116),char(101),char(114),char(97),char(108),char(70),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(68),char(105),char(114),char(50), +char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99),char(104),char(101),char(68),char(105),char(115),char(116), +char(97),char(110),char(99),char(101),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99),char(104),char(101), +char(65),char(112),char(112),char(108),char(105),char(101),char(100),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(91),char(52),char(93),char(0),char(109),char(95), +char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99),char(104),char(101),char(67),char(111),char(109),char(98),char(105),char(110),char(101),char(100),char(70),char(114), +char(105),char(99),char(116),char(105),char(111),char(110),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99), +char(104),char(101),char(67),char(111),char(109),char(98),char(105),char(110),char(101),char(100),char(82),char(111),char(108),char(108),char(105),char(110),char(103),char(70),char(114),char(105), +char(99),char(116),char(105),char(111),char(110),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99),char(104), +char(101),char(67),char(111),char(109),char(98),char(105),char(110),char(101),char(100),char(83),char(112),char(105),char(110),char(110),char(105),char(110),char(103),char(70),char(114),char(105), +char(99),char(116),char(105),char(111),char(110),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99),char(104), +char(101),char(67),char(111),char(109),char(98),char(105),char(110),char(101),char(100),char(82),char(101),char(115),char(116),char(105),char(116),char(117),char(116),char(105),char(111),char(110), +char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99),char(104),char(101),char(80),char(97),char(114),char(116), +char(73),char(100),char(48),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99),char(104),char(101),char(80), +char(97),char(114),char(116),char(73),char(100),char(49),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99), +char(104),char(101),char(73),char(110),char(100),char(101),char(120),char(48),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67), +char(97),char(99),char(104),char(101),char(73),char(110),char(100),char(101),char(120),char(49),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110), +char(116),char(67),char(97),char(99),char(104),char(101),char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(80),char(111),char(105),char(110),char(116),char(70),char(108), +char(97),char(103),char(115),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99),char(104),char(101),char(65), +char(112),char(112),char(108),char(105),char(101),char(100),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(76),char(97),char(116),char(101),char(114),char(97),char(108), +char(49),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99),char(104),char(101),char(65),char(112),char(112), +char(108),char(105),char(101),char(100),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(76),char(97),char(116),char(101),char(114),char(97),char(108),char(50),char(91), +char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99),char(104),char(101),char(67),char(111),char(110),char(116),char(97), +char(99),char(116),char(77),char(111),char(116),char(105),char(111),char(110),char(49),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116), +char(67),char(97),char(99),char(104),char(101),char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(77),char(111),char(116),char(105),char(111),char(110),char(50),char(91), +char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99),char(104),char(101),char(67),char(111),char(110),char(116),char(97), +char(99),char(116),char(67),char(70),char(77),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99),char(104), +char(101),char(67),char(111),char(109),char(98),char(105),char(110),char(101),char(100),char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(83),char(116),char(105),char(102), +char(102),char(110),char(101),char(115),char(115),char(49),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99), +char(104),char(101),char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(69),char(82),char(80),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111), +char(105),char(110),char(116),char(67),char(97),char(99),char(104),char(101),char(67),char(111),char(109),char(98),char(105),char(110),char(101),char(100),char(67),char(111),char(110),char(116), +char(97),char(99),char(116),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(49),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105), +char(110),char(116),char(67),char(97),char(99),char(104),char(101),char(70),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(67),char(70),char(77),char(91),char(52), +char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99),char(104),char(101),char(76),char(105),char(102),char(101),char(84),char(105), +char(109),char(101),char(91),char(52),char(93),char(0),char(109),char(95),char(110),char(117),char(109),char(67),char(97),char(99),char(104),char(101),char(100),char(80),char(111),char(105), +char(110),char(116),char(115),char(0),char(109),char(95),char(99),char(111),char(109),char(112),char(97),char(110),char(105),char(111),char(110),char(73),char(100),char(65),char(0),char(109), +char(95),char(99),char(111),char(109),char(112),char(97),char(110),char(105),char(111),char(110),char(73),char(100),char(66),char(0),char(109),char(95),char(105),char(110),char(100),char(101), +char(120),char(49),char(97),char(0),char(109),char(95),char(111),char(98),char(106),char(101),char(99),char(116),char(84),char(121),char(112),char(101),char(0),char(109),char(95),char(99), +char(111),char(110),char(116),char(97),char(99),char(116),char(66),char(114),char(101),char(97),char(107),char(105),char(110),char(103),char(84),char(104),char(114),char(101),char(115),char(104), +char(111),char(108),char(100),char(0),char(109),char(95),char(99),char(111),char(110),char(116),char(97),char(99),char(116),char(80),char(114),char(111),char(99),char(101),char(115),char(115), +char(105),char(110),char(103),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(42),char(109),char(95),char(98),char(111),char(100),char(121), +char(48),char(0),char(42),char(109),char(95),char(98),char(111),char(100),char(121),char(49),char(0),char(109),char(95),char(103),char(105),char(109),char(112),char(97),char(99),char(116), +char(83),char(117),char(98),char(84),char(121),char(112),char(101),char(0),char(42),char(109),char(95),char(117),char(110),char(115),char(99),char(97),char(108),char(101),char(100),char(80), +char(111),char(105),char(110),char(116),char(115),char(70),char(108),char(111),char(97),char(116),char(80),char(116),char(114),char(0),char(42),char(109),char(95),char(117),char(110),char(115), +char(99),char(97),char(108),char(101),char(100),char(80),char(111),char(105),char(110),char(116),char(115),char(68),char(111),char(117),char(98),char(108),char(101),char(80),char(116),char(114), +char(0),char(109),char(95),char(110),char(117),char(109),char(85),char(110),char(115),char(99),char(97),char(108),char(101),char(100),char(80),char(111),char(105),char(110),char(116),char(115), +char(0),char(109),char(95),char(112),char(97),char(100),char(100),char(105),char(110),char(103),char(51),char(91),char(52),char(93),char(0),char(42),char(109),char(95),char(98),char(114), +char(111),char(97),char(100),char(112),char(104),char(97),char(115),char(101),char(72),char(97),char(110),char(100),char(108),char(101),char(0),char(42),char(109),char(95),char(99),char(111), +char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(83),char(104),char(97),char(112),char(101),char(0),char(42),char(109),char(95),char(114),char(111),char(111),char(116), +char(67),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(83),char(104),char(97),char(112),char(101),char(0),char(109),char(95),char(119),char(111),char(114), +char(108),char(100),char(84),char(114),char(97),char(110),char(115),char(102),char(111),char(114),char(109),char(0),char(109),char(95),char(105),char(110),char(116),char(101),char(114),char(112), +char(111),char(108),char(97),char(116),char(105),char(111),char(110),char(87),char(111),char(114),char(108),char(100),char(84),char(114),char(97),char(110),char(115),char(102),char(111),char(114), +char(109),char(0),char(109),char(95),char(105),char(110),char(116),char(101),char(114),char(112),char(111),char(108),char(97),char(116),char(105),char(111),char(110),char(76),char(105),char(110), +char(101),char(97),char(114),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(105),char(110),char(116),char(101),char(114),char(112), +char(111),char(108),char(97),char(116),char(105),char(111),char(110),char(65),char(110),char(103),char(117),char(108),char(97),char(114),char(86),char(101),char(108),char(111),char(99),char(105), +char(116),char(121),char(0),char(109),char(95),char(97),char(110),char(105),char(115),char(111),char(116),char(114),char(111),char(112),char(105),char(99),char(70),char(114),char(105),char(99), +char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(100),char(101),char(97),char(99),char(116),char(105),char(118),char(97),char(116),char(105),char(111),char(110),char(84), +char(105),char(109),char(101),char(0),char(109),char(95),char(102),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(114),char(111),char(108), +char(108),char(105),char(110),char(103),char(70),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(99),char(111),char(110),char(116),char(97), +char(99),char(116),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(99),char(111),char(110),char(116),char(97),char(99),char(116),char(83), +char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(114),char(101),char(115),char(116),char(105),char(116),char(117),char(116),char(105), +char(111),char(110),char(0),char(109),char(95),char(104),char(105),char(116),char(70),char(114),char(97),char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(99), +char(99),char(100),char(83),char(119),char(101),char(112),char(116),char(83),char(112),char(104),char(101),char(114),char(101),char(82),char(97),char(100),char(105),char(117),char(115),char(0), +char(109),char(95),char(99),char(99),char(100),char(77),char(111),char(116),char(105),char(111),char(110),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100), +char(0),char(109),char(95),char(104),char(97),char(115),char(65),char(110),char(105),char(115),char(111),char(116),char(114),char(111),char(112),char(105),char(99),char(70),char(114),char(105), +char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(99),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(70),char(108),char(97), +char(103),char(115),char(0),char(109),char(95),char(105),char(115),char(108),char(97),char(110),char(100),char(84),char(97),char(103),char(49),char(0),char(109),char(95),char(99),char(111), +char(109),char(112),char(97),char(110),char(105),char(111),char(110),char(73),char(100),char(0),char(109),char(95),char(97),char(99),char(116),char(105),char(118),char(97),char(116),char(105), +char(111),char(110),char(83),char(116),char(97),char(116),char(101),char(49),char(0),char(109),char(95),char(105),char(110),char(116),char(101),char(114),char(110),char(97),char(108),char(84), +char(121),char(112),char(101),char(0),char(109),char(95),char(99),char(104),char(101),char(99),char(107),char(67),char(111),char(108),char(108),char(105),char(100),char(101),char(87),char(105), +char(116),char(104),char(0),char(109),char(95),char(99),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(70),char(105),char(108),char(116),char(101),char(114), +char(71),char(114),char(111),char(117),char(112),char(0),char(109),char(95),char(99),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(70),char(105),char(108), +char(116),char(101),char(114),char(77),char(97),char(115),char(107),char(0),char(109),char(95),char(117),char(110),char(105),char(113),char(117),char(101),char(73),char(100),char(0),char(109), +char(95),char(116),char(97),char(117),char(0),char(109),char(95),char(100),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(116),char(105),char(109), +char(101),char(83),char(116),char(101),char(112),char(0),char(109),char(95),char(109),char(97),char(120),char(69),char(114),char(114),char(111),char(114),char(82),char(101),char(100),char(117), +char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(115),char(111),char(114),char(0),char(109),char(95),char(101),char(114),char(112),char(0),char(109),char(95), +char(101),char(114),char(112),char(50),char(0),char(109),char(95),char(103),char(108),char(111),char(98),char(97),char(108),char(67),char(102),char(109),char(0),char(109),char(95),char(115), +char(112),char(108),char(105),char(116),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(80),char(101),char(110),char(101),char(116),char(114),char(97),char(116),char(105), +char(111),char(110),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(115),char(112),char(108),char(105),char(116),char(73), +char(109),char(112),char(117),char(108),char(115),char(101),char(84),char(117),char(114),char(110),char(69),char(114),char(112),char(0),char(109),char(95),char(108),char(105),char(110),char(101), +char(97),char(114),char(83),char(108),char(111),char(112),char(0),char(109),char(95),char(119),char(97),char(114),char(109),char(115),char(116),char(97),char(114),char(116),char(105),char(110), +char(103),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(109),char(97),char(120),char(71),char(121),char(114),char(111),char(115),char(99),char(111), +char(112),char(105),char(99),char(70),char(111),char(114),char(99),char(101),char(0),char(109),char(95),char(115),char(105),char(110),char(103),char(108),char(101),char(65),char(120),char(105), +char(115),char(82),char(111),char(108),char(108),char(105),char(110),char(103),char(70),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(84),char(104),char(114),char(101), +char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(110),char(117),char(109),char(73),char(116),char(101),char(114),char(97),char(116),char(105),char(111),char(110), +char(115),char(0),char(109),char(95),char(115),char(111),char(108),char(118),char(101),char(114),char(77),char(111),char(100),char(101),char(0),char(109),char(95),char(114),char(101),char(115), +char(116),char(105),char(110),char(103),char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(82),char(101),char(115),char(116),char(105),char(116),char(117),char(116),char(105), +char(111),char(110),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(109),char(105),char(110),char(105),char(109),char(117), +char(109),char(83),char(111),char(108),char(118),char(101),char(114),char(66),char(97),char(116),char(99),char(104),char(83),char(105),char(122),char(101),char(0),char(109),char(95),char(115), +char(112),char(108),char(105),char(116),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(0),char(109),char(95),char(115),char(111),char(108),char(118),char(101),char(114), +char(73),char(110),char(102),char(111),char(0),char(109),char(95),char(103),char(114),char(97),char(118),char(105),char(116),char(121),char(0),char(109),char(95),char(99),char(111),char(108), +char(108),char(105),char(115),char(105),char(111),char(110),char(79),char(98),char(106),char(101),char(99),char(116),char(68),char(97),char(116),char(97),char(0),char(109),char(95),char(105), +char(110),char(118),char(73),char(110),char(101),char(114),char(116),char(105),char(97),char(84),char(101),char(110),char(115),char(111),char(114),char(87),char(111),char(114),char(108),char(100), +char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95), +char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(97),char(110), +char(103),char(117),char(108),char(97),char(114),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114), +char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(103),char(114),char(97),char(118),char(105),char(116),char(121),char(95),char(97),char(99),char(99), +char(101),char(108),char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(105),char(110),char(118),char(73),char(110),char(101),char(114),char(116), +char(105),char(97),char(76),char(111),char(99),char(97),char(108),char(0),char(109),char(95),char(116),char(111),char(116),char(97),char(108),char(70),char(111),char(114),char(99),char(101), +char(0),char(109),char(95),char(116),char(111),char(116),char(97),char(108),char(84),char(111),char(114),char(113),char(117),char(101),char(0),char(109),char(95),char(105),char(110),char(118), +char(101),char(114),char(115),char(101),char(77),char(97),char(115),char(115),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(68),char(97),char(109), +char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(68),char(97),char(109),char(112),char(105),char(110), +char(103),char(0),char(109),char(95),char(97),char(100),char(100),char(105),char(116),char(105),char(111),char(110),char(97),char(108),char(68),char(97),char(109),char(112),char(105),char(110), +char(103),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(97),char(100),char(100),char(105),char(116),char(105),char(111),char(110),char(97),char(108), +char(76),char(105),char(110),char(101),char(97),char(114),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(84),char(104),char(114),char(101),char(115),char(104),char(111), +char(108),char(100),char(83),char(113),char(114),char(0),char(109),char(95),char(97),char(100),char(100),char(105),char(116),char(105),char(111),char(110),char(97),char(108),char(65),char(110), +char(103),char(117),char(108),char(97),char(114),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108), +char(100),char(83),char(113),char(114),char(0),char(109),char(95),char(97),char(100),char(100),char(105),char(116),char(105),char(111),char(110),char(97),char(108),char(65),char(110),char(103), +char(117),char(108),char(97),char(114),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95), +char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(108),char(101),char(101),char(112),char(105),char(110),char(103),char(84),char(104),char(114),char(101),char(115),char(104), +char(111),char(108),char(100),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(83),char(108),char(101),char(101),char(112),char(105),char(110), +char(103),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(97),char(100),char(100),char(105),char(116),char(105),char(111), +char(110),char(97),char(108),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(110),char(117),char(109),char(67),char(111),char(110),char(115), +char(116),char(114),char(97),char(105),char(110),char(116),char(82),char(111),char(119),char(115),char(0),char(110),char(117),char(98),char(0),char(42),char(109),char(95),char(114),char(98), +char(65),char(0),char(42),char(109),char(95),char(114),char(98),char(66),char(0),char(109),char(95),char(117),char(115),char(101),char(114),char(67),char(111),char(110),char(115),char(116), +char(114),char(97),char(105),char(110),char(116),char(84),char(121),char(112),char(101),char(0),char(109),char(95),char(117),char(115),char(101),char(114),char(67),char(111),char(110),char(115), +char(116),char(114),char(97),char(105),char(110),char(116),char(73),char(100),char(0),char(109),char(95),char(110),char(101),char(101),char(100),char(115),char(70),char(101),char(101),char(100), +char(98),char(97),char(99),char(107),char(0),char(109),char(95),char(97),char(112),char(112),char(108),char(105),char(101),char(100),char(73),char(109),char(112),char(117),char(108),char(115), +char(101),char(0),char(109),char(95),char(100),char(98),char(103),char(68),char(114),char(97),char(119),char(83),char(105),char(122),char(101),char(0),char(109),char(95),char(100),char(105), +char(115),char(97),char(98),char(108),char(101),char(67),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(115),char(66),char(101),char(116),char(119),char(101), +char(101),char(110),char(76),char(105),char(110),char(107),char(101),char(100),char(66),char(111),char(100),char(105),char(101),char(115),char(0),char(109),char(95),char(111),char(118),char(101), +char(114),char(114),char(105),char(100),char(101),char(78),char(117),char(109),char(83),char(111),char(108),char(118),char(101),char(114),char(73),char(116),char(101),char(114),char(97),char(116), +char(105),char(111),char(110),char(115),char(0),char(109),char(95),char(98),char(114),char(101),char(97),char(107),char(105),char(110),char(103),char(73),char(109),char(112),char(117),char(108), +char(115),char(101),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(105),char(115),char(69),char(110),char(97),char(98), +char(108),char(101),char(100),char(0),char(112),char(97),char(100),char(100),char(105),char(110),char(103),char(91),char(52),char(93),char(0),char(109),char(95),char(116),char(121),char(112), +char(101),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(109),char(95),char(112),char(105), +char(118),char(111),char(116),char(73),char(110),char(65),char(0),char(109),char(95),char(112),char(105),char(118),char(111),char(116),char(73),char(110),char(66),char(0),char(109),char(95), +char(114),char(98),char(65),char(70),char(114),char(97),char(109),char(101),char(0),char(109),char(95),char(114),char(98),char(66),char(70),char(114),char(97),char(109),char(101),char(0), +char(109),char(95),char(117),char(115),char(101),char(82),char(101),char(102),char(101),char(114),char(101),char(110),char(99),char(101),char(70),char(114),char(97),char(109),char(101),char(65), +char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(79),char(110),char(108),char(121),char(0),char(109),char(95),char(101),char(110),char(97), +char(98),char(108),char(101),char(65),char(110),char(103),char(117),char(108),char(97),char(114),char(77),char(111),char(116),char(111),char(114),char(0),char(109),char(95),char(109),char(111), +char(116),char(111),char(114),char(84),char(97),char(114),char(103),char(101),char(116),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95), +char(109),char(97),char(120),char(77),char(111),char(116),char(111),char(114),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(0),char(109),char(95),char(108),char(111), +char(119),char(101),char(114),char(76),char(105),char(109),char(105),char(116),char(0),char(109),char(95),char(117),char(112),char(112),char(101),char(114),char(76),char(105),char(109),char(105), +char(116),char(0),char(109),char(95),char(108),char(105),char(109),char(105),char(116),char(83),char(111),char(102),char(116),char(110),char(101),char(115),char(115),char(0),char(109),char(95), +char(98),char(105),char(97),char(115),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(114),char(101),char(108),char(97),char(120),char(97),char(116), +char(105),char(111),char(110),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(112),char(97),char(100),char(100),char(105),char(110),char(103),char(49), +char(91),char(52),char(93),char(0),char(109),char(95),char(115),char(119),char(105),char(110),char(103),char(83),char(112),char(97),char(110),char(49),char(0),char(109),char(95),char(115), +char(119),char(105),char(110),char(103),char(83),char(112),char(97),char(110),char(50),char(0),char(109),char(95),char(116),char(119),char(105),char(115),char(116),char(83),char(112),char(97), +char(110),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(85),char(112),char(112),char(101),char(114),char(76),char(105),char(109),char(105),char(116), +char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(76),char(111),char(119),char(101),char(114),char(76),char(105),char(109),char(105),char(116),char(0), +char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(85),char(112),char(112),char(101),char(114),char(76),char(105),char(109),char(105),char(116),char(0), +char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(76),char(111),char(119),char(101),char(114),char(76),char(105),char(109),char(105),char(116),char(0), +char(109),char(95),char(117),char(115),char(101),char(76),char(105),char(110),char(101),char(97),char(114),char(82),char(101),char(102),char(101),char(114),char(101),char(110),char(99),char(101), +char(70),char(114),char(97),char(109),char(101),char(65),char(0),char(109),char(95),char(117),char(115),char(101),char(79),char(102),char(102),char(115),char(101),char(116),char(70),char(111), +char(114),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(70),char(114),char(97),char(109),char(101),char(0),char(109),char(95),char(54), +char(100),char(111),char(102),char(68),char(97),char(116),char(97),char(0),char(109),char(95),char(115),char(112),char(114),char(105),char(110),char(103),char(69),char(110),char(97),char(98), +char(108),char(101),char(100),char(91),char(54),char(93),char(0),char(109),char(95),char(101),char(113),char(117),char(105),char(108),char(105),char(98),char(114),char(105),char(117),char(109), +char(80),char(111),char(105),char(110),char(116),char(91),char(54),char(93),char(0),char(109),char(95),char(115),char(112),char(114),char(105),char(110),char(103),char(83),char(116),char(105), +char(102),char(102),char(110),char(101),char(115),char(115),char(91),char(54),char(93),char(0),char(109),char(95),char(115),char(112),char(114),char(105),char(110),char(103),char(68),char(97), +char(109),char(112),char(105),char(110),char(103),char(91),char(54),char(93),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(66),char(111),char(117), +char(110),char(99),char(101),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(116),char(111),char(112),char(69),char(82),char(80),char(0), +char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(116),char(111),char(112),char(67),char(70),char(77),char(0),char(109),char(95),char(108),char(105), +char(110),char(101),char(97),char(114),char(77),char(111),char(116),char(111),char(114),char(69),char(82),char(80),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97), +char(114),char(77),char(111),char(116),char(111),char(114),char(67),char(70),char(77),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(84),char(97), +char(114),char(103),char(101),char(116),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97), +char(114),char(77),char(97),char(120),char(77),char(111),char(116),char(111),char(114),char(70),char(111),char(114),char(99),char(101),char(0),char(109),char(95),char(108),char(105),char(110), +char(101),char(97),char(114),char(83),char(101),char(114),char(118),char(111),char(84),char(97),char(114),char(103),char(101),char(116),char(0),char(109),char(95),char(108),char(105),char(110), +char(101),char(97),char(114),char(83),char(112),char(114),char(105),char(110),char(103),char(83),char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(0),char(109), +char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(112),char(114),char(105),char(110),char(103),char(68),char(97),char(109),char(112),char(105),char(110),char(103), +char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(69),char(113),char(117),char(105),char(108),char(105),char(98),char(114),char(105),char(117),char(109), +char(80),char(111),char(105),char(110),char(116),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(69),char(110),char(97),char(98),char(108),char(101), +char(77),char(111),char(116),char(111),char(114),char(91),char(52),char(93),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(101),char(114), +char(118),char(111),char(77),char(111),char(116),char(111),char(114),char(91),char(52),char(93),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(69), +char(110),char(97),char(98),char(108),char(101),char(83),char(112),char(114),char(105),char(110),char(103),char(91),char(52),char(93),char(0),char(109),char(95),char(108),char(105),char(110), +char(101),char(97),char(114),char(83),char(112),char(114),char(105),char(110),char(103),char(83),char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(76),char(105), +char(109),char(105),char(116),char(101),char(100),char(91),char(52),char(93),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(112),char(114), +char(105),char(110),char(103),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(76),char(105),char(109),char(105),char(116),char(101),char(100),char(91),char(52),char(93), +char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(66),char(111),char(117),char(110),char(99),char(101),char(0),char(109),char(95),char(97), +char(110),char(103),char(117),char(108),char(97),char(114),char(83),char(116),char(111),char(112),char(69),char(82),char(80),char(0),char(109),char(95),char(97),char(110),char(103),char(117), +char(108),char(97),char(114),char(83),char(116),char(111),char(112),char(67),char(70),char(77),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114), +char(77),char(111),char(116),char(111),char(114),char(69),char(82),char(80),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(77),char(111), +char(116),char(111),char(114),char(67),char(70),char(77),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(84),char(97),char(114),char(103), +char(101),char(116),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114), +char(77),char(97),char(120),char(77),char(111),char(116),char(111),char(114),char(70),char(111),char(114),char(99),char(101),char(0),char(109),char(95),char(97),char(110),char(103),char(117), +char(108),char(97),char(114),char(83),char(101),char(114),char(118),char(111),char(84),char(97),char(114),char(103),char(101),char(116),char(0),char(109),char(95),char(97),char(110),char(103), +char(117),char(108),char(97),char(114),char(83),char(112),char(114),char(105),char(110),char(103),char(83),char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(0), +char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(83),char(112),char(114),char(105),char(110),char(103),char(68),char(97),char(109),char(112),char(105), +char(110),char(103),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(69),char(113),char(117),char(105),char(108),char(105),char(98),char(114), +char(105),char(117),char(109),char(80),char(111),char(105),char(110),char(116),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(69),char(110), +char(97),char(98),char(108),char(101),char(77),char(111),char(116),char(111),char(114),char(91),char(52),char(93),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108), +char(97),char(114),char(83),char(101),char(114),char(118),char(111),char(77),char(111),char(116),char(111),char(114),char(91),char(52),char(93),char(0),char(109),char(95),char(97),char(110), +char(103),char(117),char(108),char(97),char(114),char(69),char(110),char(97),char(98),char(108),char(101),char(83),char(112),char(114),char(105),char(110),char(103),char(91),char(52),char(93), +char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(83),char(112),char(114),char(105),char(110),char(103),char(83),char(116),char(105),char(102), +char(102),char(110),char(101),char(115),char(115),char(76),char(105),char(109),char(105),char(116),char(101),char(100),char(91),char(52),char(93),char(0),char(109),char(95),char(97),char(110), +char(103),char(117),char(108),char(97),char(114),char(83),char(112),char(114),char(105),char(110),char(103),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(76),char(105), +char(109),char(105),char(116),char(101),char(100),char(91),char(52),char(93),char(0),char(109),char(95),char(114),char(111),char(116),char(97),char(116),char(101),char(79),char(114),char(100), +char(101),char(114),char(0),char(109),char(95),char(97),char(120),char(105),char(115),char(73),char(110),char(65),char(0),char(109),char(95),char(97),char(120),char(105),char(115),char(73), +char(110),char(66),char(0),char(109),char(95),char(114),char(97),char(116),char(105),char(111),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83), +char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(83),char(116), +char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(118),char(111),char(108),char(117),char(109),char(101),char(83),char(116),char(105),char(102), +char(102),char(110),char(101),char(115),char(115),char(0),char(42),char(109),char(95),char(109),char(97),char(116),char(101),char(114),char(105),char(97),char(108),char(0),char(109),char(95), +char(112),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(112),char(114),char(101),char(118),char(105),char(111),char(117),char(115),char(80), +char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(118),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109), +char(95),char(97),char(99),char(99),char(117),char(109),char(117),char(108),char(97),char(116),char(101),char(100),char(70),char(111),char(114),char(99),char(101),char(0),char(109),char(95), +char(110),char(111),char(114),char(109),char(97),char(108),char(0),char(109),char(95),char(97),char(114),char(101),char(97),char(0),char(109),char(95),char(97),char(116),char(116),char(97), +char(99),char(104),char(0),char(109),char(95),char(110),char(111),char(100),char(101),char(73),char(110),char(100),char(105),char(99),char(101),char(115),char(91),char(50),char(93),char(0), +char(109),char(95),char(114),char(101),char(115),char(116),char(76),char(101),char(110),char(103),char(116),char(104),char(0),char(109),char(95),char(98),char(98),char(101),char(110),char(100), +char(105),char(110),char(103),char(0),char(109),char(95),char(110),char(111),char(100),char(101),char(73),char(110),char(100),char(105),char(99),char(101),char(115),char(91),char(51),char(93), +char(0),char(109),char(95),char(114),char(101),char(115),char(116),char(65),char(114),char(101),char(97),char(0),char(109),char(95),char(99),char(48),char(91),char(52),char(93),char(0), +char(109),char(95),char(110),char(111),char(100),char(101),char(73),char(110),char(100),char(105),char(99),char(101),char(115),char(91),char(52),char(93),char(0),char(109),char(95),char(114), +char(101),char(115),char(116),char(86),char(111),char(108),char(117),char(109),char(101),char(0),char(109),char(95),char(99),char(49),char(0),char(109),char(95),char(99),char(50),char(0), +char(109),char(95),char(99),char(48),char(0),char(109),char(95),char(108),char(111),char(99),char(97),char(108),char(70),char(114),char(97),char(109),char(101),char(0),char(42),char(109), +char(95),char(114),char(105),char(103),char(105),char(100),char(66),char(111),char(100),char(121),char(0),char(109),char(95),char(110),char(111),char(100),char(101),char(73),char(110),char(100), +char(101),char(120),char(0),char(109),char(95),char(97),char(101),char(114),char(111),char(77),char(111),char(100),char(101),char(108),char(0),char(109),char(95),char(98),char(97),char(117), +char(109),char(103),char(97),char(114),char(116),char(101),char(0),char(109),char(95),char(100),char(114),char(97),char(103),char(0),char(109),char(95),char(108),char(105),char(102),char(116), +char(0),char(109),char(95),char(112),char(114),char(101),char(115),char(115),char(117),char(114),char(101),char(0),char(109),char(95),char(118),char(111),char(108),char(117),char(109),char(101), +char(0),char(109),char(95),char(100),char(121),char(110),char(97),char(109),char(105),char(99),char(70),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(0),char(109), +char(95),char(112),char(111),char(115),char(101),char(77),char(97),char(116),char(99),char(104),char(0),char(109),char(95),char(114),char(105),char(103),char(105),char(100),char(67),char(111), +char(110),char(116),char(97),char(99),char(116),char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(107),char(105),char(110),char(101), +char(116),char(105),char(99),char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115),char(0),char(109), +char(95),char(115),char(111),char(102),char(116),char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115), +char(0),char(109),char(95),char(97),char(110),char(99),char(104),char(111),char(114),char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115),char(0),char(109),char(95), +char(115),char(111),char(102),char(116),char(82),char(105),char(103),char(105),char(100),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(72),char(97),char(114),char(100), +char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(75),char(105),char(110),char(101),char(116),char(105),char(99),char(67),char(108), +char(117),char(115),char(116),char(101),char(114),char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(115),char(111),char(102),char(116), +char(83),char(111),char(102),char(116),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115),char(0), +char(109),char(95),char(115),char(111),char(102),char(116),char(82),char(105),char(103),char(105),char(100),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(73),char(109), +char(112),char(117),char(108),char(115),char(101),char(83),char(112),char(108),char(105),char(116),char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(75),char(105),char(110), +char(101),char(116),char(105),char(99),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(83),char(112), +char(108),char(105),char(116),char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(83),char(111),char(102),char(116),char(67),char(108),char(117),char(115),char(116),char(101), +char(114),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(83),char(112),char(108),char(105),char(116),char(0),char(109),char(95),char(109),char(97),char(120),char(86), +char(111),char(108),char(117),char(109),char(101),char(0),char(109),char(95),char(116),char(105),char(109),char(101),char(83),char(99),char(97),char(108),char(101),char(0),char(109),char(95), +char(118),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(73),char(116),char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(115),char(0),char(109), +char(95),char(112),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(73),char(116),char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(115),char(0), +char(109),char(95),char(100),char(114),char(105),char(102),char(116),char(73),char(116),char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95), +char(99),char(108),char(117),char(115),char(116),char(101),char(114),char(73),char(116),char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95), +char(114),char(111),char(116),char(0),char(109),char(95),char(115),char(99),char(97),char(108),char(101),char(0),char(109),char(95),char(97),char(113),char(113),char(0),char(109),char(95), +char(99),char(111),char(109),char(0),char(42),char(109),char(95),char(112),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(115),char(0),char(42),char(109),char(95), +char(119),char(101),char(105),char(103),char(104),char(116),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(80),char(111),char(115),char(105),char(116),char(105),char(111), +char(110),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(87),char(101),char(105),char(103),char(116),char(115),char(0),char(109),char(95),char(98),char(118),char(111), +char(108),char(117),char(109),char(101),char(0),char(109),char(95),char(98),char(102),char(114),char(97),char(109),char(101),char(0),char(109),char(95),char(102),char(114),char(97),char(109), +char(101),char(120),char(102),char(111),char(114),char(109),char(0),char(109),char(95),char(108),char(111),char(99),char(105),char(105),char(0),char(109),char(95),char(105),char(110),char(118), +char(119),char(105),char(0),char(109),char(95),char(118),char(105),char(109),char(112),char(117),char(108),char(115),char(101),char(115),char(91),char(50),char(93),char(0),char(109),char(95), +char(100),char(105),char(109),char(112),char(117),char(108),char(115),char(101),char(115),char(91),char(50),char(93),char(0),char(109),char(95),char(108),char(118),char(0),char(109),char(95), +char(97),char(118),char(0),char(42),char(109),char(95),char(102),char(114),char(97),char(109),char(101),char(114),char(101),char(102),char(115),char(0),char(42),char(109),char(95),char(110), +char(111),char(100),char(101),char(73),char(110),char(100),char(105),char(99),char(101),char(115),char(0),char(42),char(109),char(95),char(109),char(97),char(115),char(115),char(101),char(115), +char(0),char(109),char(95),char(110),char(117),char(109),char(70),char(114),char(97),char(109),char(101),char(82),char(101),char(102),char(115),char(0),char(109),char(95),char(110),char(117), +char(109),char(78),char(111),char(100),char(101),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(77),char(97),char(115),char(115),char(101),char(115),char(0),char(109), +char(95),char(105),char(100),char(109),char(97),char(115),char(115),char(0),char(109),char(95),char(105),char(109),char(97),char(115),char(115),char(0),char(109),char(95),char(110),char(118), +char(105),char(109),char(112),char(117),char(108),char(115),char(101),char(115),char(0),char(109),char(95),char(110),char(100),char(105),char(109),char(112),char(117),char(108),char(115),char(101), +char(115),char(0),char(109),char(95),char(110),char(100),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(108),char(100),char(97),char(109),char(112), +char(105),char(110),char(103),char(0),char(109),char(95),char(97),char(100),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(109),char(97),char(116), +char(99),char(104),char(105),char(110),char(103),char(0),char(109),char(95),char(109),char(97),char(120),char(83),char(101),char(108),char(102),char(67),char(111),char(108),char(108),char(105), +char(115),char(105),char(111),char(110),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(0),char(109),char(95),char(115),char(101),char(108),char(102),char(67),char(111), +char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(70),char(97),char(99),char(116),char(111),char(114), +char(0),char(109),char(95),char(99),char(111),char(110),char(116),char(97),char(105),char(110),char(115),char(65),char(110),char(99),char(104),char(111),char(114),char(0),char(109),char(95), +char(99),char(111),char(108),char(108),char(105),char(100),char(101),char(0),char(109),char(95),char(99),char(108),char(117),char(115),char(116),char(101),char(114),char(73),char(110),char(100), +char(101),char(120),char(0),char(42),char(109),char(95),char(98),char(111),char(100),char(121),char(65),char(0),char(42),char(109),char(95),char(98),char(111),char(100),char(121),char(66), +char(0),char(109),char(95),char(114),char(101),char(102),char(115),char(91),char(50),char(93),char(0),char(109),char(95),char(99),char(102),char(109),char(0),char(109),char(95),char(115), +char(112),char(108),char(105),char(116),char(0),char(109),char(95),char(100),char(101),char(108),char(101),char(116),char(101),char(0),char(109),char(95),char(114),char(101),char(108),char(80), +char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(91),char(50),char(93),char(0),char(109),char(95),char(98),char(111),char(100),char(121),char(65),char(116),char(121), +char(112),char(101),char(0),char(109),char(95),char(98),char(111),char(100),char(121),char(66),char(116),char(121),char(112),char(101),char(0),char(109),char(95),char(106),char(111),char(105), +char(110),char(116),char(84),char(121),char(112),char(101),char(0),char(42),char(109),char(95),char(112),char(111),char(115),char(101),char(0),char(42),char(42),char(109),char(95),char(109), +char(97),char(116),char(101),char(114),char(105),char(97),char(108),char(115),char(0),char(42),char(109),char(95),char(110),char(111),char(100),char(101),char(115),char(0),char(42),char(109), +char(95),char(108),char(105),char(110),char(107),char(115),char(0),char(42),char(109),char(95),char(102),char(97),char(99),char(101),char(115),char(0),char(42),char(109),char(95),char(116), +char(101),char(116),char(114),char(97),char(104),char(101),char(100),char(114),char(97),char(0),char(42),char(109),char(95),char(97),char(110),char(99),char(104),char(111),char(114),char(115), +char(0),char(42),char(109),char(95),char(99),char(108),char(117),char(115),char(116),char(101),char(114),char(115),char(0),char(42),char(109),char(95),char(106),char(111),char(105),char(110), +char(116),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(77),char(97),char(116),char(101),char(114),char(105),char(97),char(108),char(115),char(0),char(109),char(95), +char(110),char(117),char(109),char(76),char(105),char(110),char(107),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(70),char(97),char(99),char(101),char(115),char(0), +char(109),char(95),char(110),char(117),char(109),char(84),char(101),char(116),char(114),char(97),char(104),char(101),char(100),char(114),char(97),char(0),char(109),char(95),char(110),char(117), +char(109),char(65),char(110),char(99),char(104),char(111),char(114),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(67),char(108),char(117),char(115),char(116),char(101), +char(114),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(74),char(111),char(105),char(110),char(116),char(115),char(0),char(109),char(95),char(99),char(111),char(110), +char(102),char(105),char(103),char(0),char(109),char(95),char(122),char(101),char(114),char(111),char(82),char(111),char(116),char(80),char(97),char(114),char(101),char(110),char(116),char(84), +char(111),char(84),char(104),char(105),char(115),char(0),char(109),char(95),char(112),char(97),char(114),char(101),char(110),char(116),char(67),char(111),char(109),char(84),char(111),char(84), +char(104),char(105),char(115),char(80),char(105),char(118),char(111),char(116),char(79),char(102),char(102),char(115),char(101),char(116),char(0),char(109),char(95),char(116),char(104),char(105), +char(115),char(80),char(105),char(118),char(111),char(116),char(84),char(111),char(84),char(104),char(105),char(115),char(67),char(111),char(109),char(79),char(102),char(102),char(115),char(101), +char(116),char(0),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(65),char(120),char(105),char(115),char(84),char(111),char(112),char(91),char(54),char(93),char(0), +char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(65),char(120),char(105),char(115),char(66),char(111),char(116),char(116),char(111),char(109),char(91),char(54),char(93), +char(0),char(109),char(95),char(108),char(105),char(110),char(107),char(73),char(110),char(101),char(114),char(116),char(105),char(97),char(0),char(109),char(95),char(97),char(98),char(115), +char(70),char(114),char(97),char(109),char(101),char(84),char(111),char(116),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(84),char(111),char(112),char(0), +char(109),char(95),char(97),char(98),char(115),char(70),char(114),char(97),char(109),char(101),char(84),char(111),char(116),char(86),char(101),char(108),char(111),char(99),char(105),char(116), +char(121),char(66),char(111),char(116),char(116),char(111),char(109),char(0),char(109),char(95),char(97),char(98),char(115),char(70),char(114),char(97),char(109),char(101),char(76),char(111), +char(99),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(84),char(111),char(112),char(0),char(109),char(95),char(97),char(98),char(115),char(70),char(114), +char(97),char(109),char(101),char(76),char(111),char(99),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(66),char(111),char(116),char(116),char(111),char(109), +char(0),char(109),char(95),char(108),char(105),char(110),char(107),char(77),char(97),char(115),char(115),char(0),char(109),char(95),char(112),char(97),char(114),char(101),char(110),char(116), +char(73),char(110),char(100),char(101),char(120),char(0),char(109),char(95),char(100),char(111),char(102),char(67),char(111),char(117),char(110),char(116),char(0),char(109),char(95),char(112), +char(111),char(115),char(86),char(97),char(114),char(67),char(111),char(117),char(110),char(116),char(0),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(80),char(111), +char(115),char(91),char(55),char(93),char(0),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(86),char(101),char(108),char(91),char(54),char(93),char(0),char(109), +char(95),char(106),char(111),char(105),char(110),char(116),char(84),char(111),char(114),char(113),char(117),char(101),char(91),char(54),char(93),char(0),char(109),char(95),char(106),char(111), +char(105),char(110),char(116),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(70),char(114), +char(105),char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(76),char(111),char(119),char(101),char(114),char(76), +char(105),char(109),char(105),char(116),char(0),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(85),char(112),char(112),char(101),char(114),char(76),char(105),char(109), +char(105),char(116),char(0),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(77),char(97),char(120),char(70),char(111),char(114),char(99),char(101),char(0),char(109), +char(95),char(106),char(111),char(105),char(110),char(116),char(77),char(97),char(120),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(42),char(109), +char(95),char(108),char(105),char(110),char(107),char(78),char(97),char(109),char(101),char(0),char(42),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(78),char(97), +char(109),char(101),char(0),char(42),char(109),char(95),char(108),char(105),char(110),char(107),char(67),char(111),char(108),char(108),char(105),char(100),char(101),char(114),char(0),char(42), +char(109),char(95),char(112),char(97),char(100),char(100),char(105),char(110),char(103),char(80),char(116),char(114),char(0),char(109),char(95),char(98),char(97),char(115),char(101),char(87), +char(111),char(114),char(108),char(100),char(80),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(98),char(97),char(115),char(101),char(87), +char(111),char(114),char(108),char(100),char(79),char(114),char(105),char(101),char(110),char(116),char(97),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(98),char(97), +char(115),char(101),char(76),char(105),char(110),char(101),char(97),char(114),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(98), +char(97),char(115),char(101),char(65),char(110),char(103),char(117),char(108),char(97),char(114),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109), +char(95),char(98),char(97),char(115),char(101),char(73),char(110),char(101),char(114),char(116),char(105),char(97),char(0),char(109),char(95),char(98),char(97),char(115),char(101),char(77), +char(97),char(115),char(115),char(0),char(42),char(109),char(95),char(98),char(97),char(115),char(101),char(78),char(97),char(109),char(101),char(0),char(42),char(109),char(95),char(98), +char(97),char(115),char(101),char(67),char(111),char(108),char(108),char(105),char(100),char(101),char(114),char(0),char(109),char(95),char(99),char(111),char(108),char(79),char(98),char(106), +char(68),char(97),char(116),char(97),char(0),char(42),char(109),char(95),char(109),char(117),char(108),char(116),char(105),char(66),char(111),char(100),char(121),char(0),char(109),char(95), +char(108),char(105),char(110),char(107),char(0),char(0),char(0),char(0),char(84),char(89),char(80),char(69),char(99),char(0),char(0),char(0),char(99),char(104),char(97),char(114), +char(0),char(117),char(99),char(104),char(97),char(114),char(0),char(115),char(104),char(111),char(114),char(116),char(0),char(117),char(115),char(104),char(111),char(114),char(116),char(0), +char(105),char(110),char(116),char(0),char(108),char(111),char(110),char(103),char(0),char(117),char(108),char(111),char(110),char(103),char(0),char(102),char(108),char(111),char(97),char(116), +char(0),char(100),char(111),char(117),char(98),char(108),char(101),char(0),char(118),char(111),char(105),char(100),char(0),char(80),char(111),char(105),char(110),char(116),char(101),char(114), +char(65),char(114),char(114),char(97),char(121),char(0),char(98),char(116),char(80),char(104),char(121),char(115),char(105),char(99),char(115),char(83),char(121),char(115),char(116),char(101), +char(109),char(0),char(76),char(105),char(115),char(116),char(66),char(97),char(115),char(101),char(0),char(98),char(116),char(86),char(101),char(99),char(116),char(111),char(114),char(51), +char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(86),char(101),char(99),char(116),char(111),char(114),char(51),char(68), +char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(81),char(117),char(97),char(116),char(101),char(114),char(110),char(105), +char(111),char(110),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(81),char(117),char(97),char(116),char(101),char(114), +char(110),char(105),char(111),char(110),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(77),char(97),char(116), +char(114),char(105),char(120),char(51),char(120),char(51),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(77),char(97), +char(116),char(114),char(105),char(120),char(51),char(120),char(51),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116), +char(84),char(114),char(97),char(110),char(115),char(102),char(111),char(114),char(109),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98), +char(116),char(84),char(114),char(97),char(110),char(115),char(102),char(111),char(114),char(109),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97), +char(0),char(98),char(116),char(66),char(118),char(104),char(83),char(117),char(98),char(116),char(114),char(101),char(101),char(73),char(110),char(102),char(111),char(68),char(97),char(116), char(97),char(0),char(98),char(116),char(79),char(112),char(116),char(105),char(109),char(105),char(122),char(101),char(100),char(66),char(118),char(104),char(78),char(111),char(100),char(101), -char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(81),char(117),char(97),char(110),char(116),char(105),char(122), -char(101),char(100),char(66),char(118),char(104),char(78),char(111),char(100),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(81),char(117),char(97),char(110), -char(116),char(105),char(122),char(101),char(100),char(66),char(118),char(104),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116), -char(81),char(117),char(97),char(110),char(116),char(105),char(122),char(101),char(100),char(66),char(118),char(104),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97), -char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(83),char(104),char(97),char(112),char(101),char(68), -char(97),char(116),char(97),char(0),char(98),char(116),char(83),char(116),char(97),char(116),char(105),char(99),char(80),char(108),char(97),char(110),char(101),char(83),char(104),char(97), -char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(110),char(118),char(101),char(120),char(73),char(110),char(116),char(101),char(114), -char(110),char(97),char(108),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(80),char(111),char(115),char(105),char(116), -char(105),char(111),char(110),char(65),char(110),char(100),char(82),char(97),char(100),char(105),char(117),char(115),char(0),char(98),char(116),char(77),char(117),char(108),char(116),char(105), -char(83),char(112),char(104),char(101),char(114),char(101),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(73),char(110), -char(116),char(73),char(110),char(100),char(101),char(120),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(83),char(104),char(111),char(114),char(116),char(73),char(110), -char(116),char(73),char(110),char(100),char(101),char(120),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(83),char(104),char(111),char(114),char(116),char(73),char(110), -char(116),char(73),char(110),char(100),char(101),char(120),char(84),char(114),char(105),char(112),char(108),char(101),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116), -char(67),char(104),char(97),char(114),char(73),char(110),char(100),char(101),char(120),char(84),char(114),char(105),char(112),char(108),char(101),char(116),char(68),char(97),char(116),char(97), -char(0),char(98),char(116),char(77),char(101),char(115),char(104),char(80),char(97),char(114),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(83),char(116), -char(114),char(105),char(100),char(105),char(110),char(103),char(77),char(101),char(115),char(104),char(73),char(110),char(116),char(101),char(114),char(102),char(97),char(99),char(101),char(68), -char(97),char(116),char(97),char(0),char(98),char(116),char(84),char(114),char(105),char(97),char(110),char(103),char(108),char(101),char(77),char(101),char(115),char(104),char(83),char(104), +char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(79),char(112),char(116),char(105),char(109),char(105),char(122),char(101), +char(100),char(66),char(118),char(104),char(78),char(111),char(100),char(101),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98), +char(116),char(81),char(117),char(97),char(110),char(116),char(105),char(122),char(101),char(100),char(66),char(118),char(104),char(78),char(111),char(100),char(101),char(68),char(97),char(116), +char(97),char(0),char(98),char(116),char(81),char(117),char(97),char(110),char(116),char(105),char(122),char(101),char(100),char(66),char(118),char(104),char(70),char(108),char(111),char(97), +char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(81),char(117),char(97),char(110),char(116),char(105),char(122),char(101),char(100),char(66),char(118),char(104), +char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(108),char(108),char(105),char(115),char(105), +char(111),char(110),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(83),char(116),char(97),char(116),char(105),char(99), +char(80),char(108),char(97),char(110),char(101),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(110), +char(118),char(101),char(120),char(73),char(110),char(116),char(101),char(114),char(110),char(97),char(108),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97), +char(0),char(98),char(116),char(80),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(65),char(110),char(100),char(82),char(97),char(100),char(105),char(117),char(115), +char(0),char(98),char(116),char(77),char(117),char(108),char(116),char(105),char(83),char(112),char(104),char(101),char(114),char(101),char(83),char(104),char(97),char(112),char(101),char(68), +char(97),char(116),char(97),char(0),char(98),char(116),char(73),char(110),char(116),char(73),char(110),char(100),char(101),char(120),char(68),char(97),char(116),char(97),char(0),char(98), +char(116),char(83),char(104),char(111),char(114),char(116),char(73),char(110),char(116),char(73),char(110),char(100),char(101),char(120),char(68),char(97),char(116),char(97),char(0),char(98), +char(116),char(83),char(104),char(111),char(114),char(116),char(73),char(110),char(116),char(73),char(110),char(100),char(101),char(120),char(84),char(114),char(105),char(112),char(108),char(101), +char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(104),char(97),char(114),char(73),char(110),char(100),char(101),char(120),char(84),char(114),char(105), +char(112),char(108),char(101),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(77),char(101),char(115),char(104),char(80),char(97),char(114),char(116),char(68), +char(97),char(116),char(97),char(0),char(98),char(116),char(83),char(116),char(114),char(105),char(100),char(105),char(110),char(103),char(77),char(101),char(115),char(104),char(73),char(110), +char(116),char(101),char(114),char(102),char(97),char(99),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(84),char(114),char(105),char(97),char(110),char(103), +char(108),char(101),char(77),char(101),char(115),char(104),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(84),char(114), +char(105),char(97),char(110),char(103),char(108),char(101),char(73),char(110),char(102),char(111),char(77),char(97),char(112),char(68),char(97),char(116),char(97),char(0),char(98),char(116), +char(83),char(99),char(97),char(108),char(101),char(100),char(84),char(114),char(105),char(97),char(110),char(103),char(108),char(101),char(77),char(101),char(115),char(104),char(83),char(104), +char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(109),char(112),char(111),char(117),char(110),char(100),char(83),char(104), +char(97),char(112),char(101),char(67),char(104),char(105),char(108),char(100),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(109),char(112),char(111), +char(117),char(110),char(100),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(121),char(108),char(105),char(110), +char(100),char(101),char(114),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(110),char(101),char(83), +char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(97),char(112),char(115),char(117),char(108),char(101),char(83),char(104), char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(84),char(114),char(105),char(97),char(110),char(103),char(108),char(101),char(73),char(110), -char(102),char(111),char(77),char(97),char(112),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(83),char(99),char(97),char(108),char(101),char(100),char(84),char(114), -char(105),char(97),char(110),char(103),char(108),char(101),char(77),char(101),char(115),char(104),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0), -char(98),char(116),char(67),char(111),char(109),char(112),char(111),char(117),char(110),char(100),char(83),char(104),char(97),char(112),char(101),char(67),char(104),char(105),char(108),char(100), -char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(109),char(112),char(111),char(117),char(110),char(100),char(83),char(104),char(97),char(112),char(101), -char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(121),char(108),char(105),char(110),char(100),char(101),char(114),char(83),char(104),char(97),char(112),char(101), -char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(110),char(101),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97), -char(0),char(98),char(116),char(67),char(97),char(112),char(115),char(117),char(108),char(101),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0), -char(98),char(116),char(84),char(114),char(105),char(97),char(110),char(103),char(108),char(101),char(73),char(110),char(102),char(111),char(68),char(97),char(116),char(97),char(0),char(98), -char(116),char(71),char(73),char(109),char(112),char(97),char(99),char(116),char(77),char(101),char(115),char(104),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116), -char(97),char(0),char(98),char(116),char(67),char(111),char(110),char(118),char(101),char(120),char(72),char(117),char(108),char(108),char(83),char(104),char(97),char(112),char(101),char(68), -char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(79),char(98),char(106),char(101),char(99), -char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(108),char(108),char(105),char(115), -char(105),char(111),char(110),char(79),char(98),char(106),char(101),char(99),char(116),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98), -char(116),char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(83),char(111),char(108),char(118),char(101),char(114),char(73),char(110),char(102),char(111),char(68),char(111), -char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(83),char(111), -char(108),char(118),char(101),char(114),char(73),char(110),char(102),char(111),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116), -char(68),char(121),char(110),char(97),char(109),char(105),char(99),char(115),char(87),char(111),char(114),char(108),char(100),char(68),char(111),char(117),char(98),char(108),char(101),char(68), -char(97),char(116),char(97),char(0),char(98),char(116),char(68),char(121),char(110),char(97),char(109),char(105),char(99),char(115),char(87),char(111),char(114),char(108),char(100),char(70), -char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(82),char(105),char(103),char(105),char(100),char(66),char(111),char(100),char(121), -char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(82),char(105),char(103),char(105),char(100),char(66),char(111),char(100), -char(121),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(110),char(115),char(116),char(114), -char(97),char(105),char(110),char(116),char(73),char(110),char(102),char(111),char(49),char(0),char(98),char(116),char(84),char(121),char(112),char(101),char(100),char(67),char(111),char(110), -char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(84), -char(121),char(112),char(101),char(100),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(98), -char(116),char(82),char(105),char(103),char(105),char(100),char(66),char(111),char(100),char(121),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(84),char(121),char(112), -char(101),char(100),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97), -char(116),char(97),char(0),char(98),char(116),char(80),char(111),char(105),char(110),char(116),char(50),char(80),char(111),char(105),char(110),char(116),char(67),char(111),char(110),char(115), -char(116),char(114),char(97),char(105),char(110),char(116),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(80),char(111), -char(105),char(110),char(116),char(50),char(80),char(111),char(105),char(110),char(116),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68), -char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(50),char(0),char(98),char(116),char(80),char(111),char(105),char(110),char(116),char(50),char(80), -char(111),char(105),char(110),char(116),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101), -char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(72),char(105),char(110),char(103),char(101),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105), -char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(72),char(105),char(110),char(103),char(101), -char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0), -char(98),char(116),char(72),char(105),char(110),char(103),char(101),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117), -char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(50),char(0),char(98),char(116),char(67),char(111),char(110),char(101),char(84),char(119),char(105),char(115),char(116), -char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97), -char(0),char(98),char(116),char(67),char(111),char(110),char(101),char(84),char(119),char(105),char(115),char(116),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105), -char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(71),char(101),char(110),char(101),char(114),char(105),char(99),char(54),char(68),char(111),char(102), -char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(71),char(101),char(110), -char(101),char(114),char(105),char(99),char(54),char(68),char(111),char(102),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111), -char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(50),char(0),char(98),char(116),char(71),char(101),char(110),char(101),char(114),char(105),char(99),char(54), -char(68),char(111),char(102),char(83),char(112),char(114),char(105),char(110),char(103),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68), -char(97),char(116),char(97),char(0),char(98),char(116),char(71),char(101),char(110),char(101),char(114),char(105),char(99),char(54),char(68),char(111),char(102),char(83),char(112),char(114), -char(105),char(110),char(103),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68), -char(97),char(116),char(97),char(50),char(0),char(98),char(116),char(71),char(101),char(110),char(101),char(114),char(105),char(99),char(54),char(68),char(111),char(102),char(83),char(112), -char(114),char(105),char(110),char(103),char(50),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0), -char(98),char(116),char(71),char(101),char(110),char(101),char(114),char(105),char(99),char(54),char(68),char(111),char(102),char(83),char(112),char(114),char(105),char(110),char(103),char(50), -char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97), -char(50),char(0),char(98),char(116),char(83),char(108),char(105),char(100),char(101),char(114),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116), -char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(83),char(108),char(105),char(100),char(101),char(114),char(67),char(111),char(110),char(115),char(116),char(114),char(97), -char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(71),char(101),char(97),char(114), -char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0), -char(98),char(116),char(71),char(101),char(97),char(114),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98), -char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(77),char(97),char(116),char(101),char(114), -char(105),char(97),char(108),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(78),char(111),char(100),char(101), -char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(76),char(105),char(110),char(107),char(68),char(97),char(116), -char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(70),char(97),char(99),char(101),char(68),char(97),char(116),char(97),char(0),char(83), -char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(84),char(101),char(116),char(114),char(97),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102), -char(116),char(82),char(105),char(103),char(105),char(100),char(65),char(110),char(99),char(104),char(111),char(114),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102), -char(116),char(66),char(111),char(100),char(121),char(67),char(111),char(110),char(102),char(105),char(103),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116), -char(66),char(111),char(100),char(121),char(80),char(111),char(115),char(101),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100), -char(121),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(83),char(111),char(102),char(116),char(66), -char(111),char(100),char(121),char(74),char(111),char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(83),char(111),char(102),char(116),char(66), -char(111),char(100),char(121),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(77),char(117),char(108),char(116),char(105), -char(66),char(111),char(100),char(121),char(76),char(105),char(110),char(107),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98), -char(116),char(77),char(117),char(108),char(116),char(105),char(66),char(111),char(100),char(121),char(76),char(105),char(110),char(107),char(70),char(108),char(111),char(97),char(116),char(68), -char(97),char(116),char(97),char(0),char(98),char(116),char(77),char(117),char(108),char(116),char(105),char(66),char(111),char(100),char(121),char(68),char(111),char(117),char(98),char(108), -char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(77),char(117),char(108),char(116),char(105),char(66),char(111),char(100),char(121),char(70),char(108),char(111), -char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(0),char(84),char(76),char(69),char(78),char(1),char(0),char(1),char(0),char(2),char(0),char(2),char(0), -char(4),char(0),char(4),char(0),char(4),char(0),char(4),char(0),char(8),char(0),char(0),char(0),char(12),char(0),char(36),char(0),char(8),char(0),char(16),char(0), -char(32),char(0),char(16),char(0),char(32),char(0),char(48),char(0),char(96),char(0),char(64),char(0),char(-128),char(0),char(20),char(0),char(48),char(0),char(80),char(0), -char(16),char(0),char(84),char(0),char(-124),char(0),char(12),char(0),char(52),char(0),char(52),char(0),char(20),char(0),char(64),char(0),char(4),char(0),char(4),char(0), -char(8),char(0),char(4),char(0),char(32),char(0),char(28),char(0),char(60),char(0),char(56),char(0),char(76),char(0),char(76),char(0),char(24),char(0),char(60),char(0), -char(60),char(0),char(60),char(0),char(16),char(0),char(64),char(0),char(68),char(0),char(-32),char(1),char(8),char(1),char(-104),char(0),char(88),char(0),char(-72),char(0), -char(104),char(0),char(-16),char(1),char(-80),char(3),char(8),char(0),char(52),char(0),char(52),char(0),char(0),char(0),char(68),char(0),char(84),char(0),char(-124),char(0), +char(102),char(111),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(80),char(101),char(114),char(115),char(105),char(115),char(116),char(101),char(110),char(116),char(77), +char(97),char(110),char(105),char(102),char(111),char(108),char(100),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116), +char(67),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(79),char(98),char(106),char(101),char(99),char(116),char(68),char(111),char(117),char(98),char(108), +char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(80),char(101),char(114),char(115),char(105),char(115),char(116),char(101),char(110),char(116),char(77),char(97), +char(110),char(105),char(102),char(111),char(108),char(100),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111), +char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(79),char(98),char(106),char(101),char(99),char(116),char(70),char(108),char(111),char(97),char(116),char(68),char(97), +char(116),char(97),char(0),char(98),char(116),char(71),char(73),char(109),char(112),char(97),char(99),char(116),char(77),char(101),char(115),char(104),char(83),char(104),char(97),char(112), +char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(110),char(118),char(101),char(120),char(72),char(117),char(108),char(108),char(83),char(104), +char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(83),char(111),char(108), +char(118),char(101),char(114),char(73),char(110),char(102),char(111),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116), +char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(83),char(111),char(108),char(118),char(101),char(114),char(73),char(110),char(102),char(111),char(70),char(108),char(111), +char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(68),char(121),char(110),char(97),char(109),char(105),char(99),char(115),char(87),char(111),char(114), +char(108),char(100),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(68),char(121),char(110),char(97),char(109), +char(105),char(99),char(115),char(87),char(111),char(114),char(108),char(100),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116), +char(82),char(105),char(103),char(105),char(100),char(66),char(111),char(100),char(121),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98), +char(116),char(82),char(105),char(103),char(105),char(100),char(66),char(111),char(100),char(121),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97), +char(0),char(98),char(116),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(73),char(110),char(102),char(111),char(49),char(0),char(98), +char(116),char(84),char(121),char(112),char(101),char(100),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(70),char(108),char(111),char(97), +char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(84),char(121),char(112),char(101),char(100),char(67),char(111),char(110),char(115),char(116),char(114),char(97), +char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(82),char(105),char(103),char(105),char(100),char(66),char(111),char(100),char(121),char(68), +char(97),char(116),char(97),char(0),char(98),char(116),char(84),char(121),char(112),char(101),char(100),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110), +char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(80),char(111),char(105),char(110),char(116),char(50), +char(80),char(111),char(105),char(110),char(116),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(70),char(108),char(111),char(97),char(116), +char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(80),char(111),char(105),char(110),char(116),char(50),char(80),char(111),char(105),char(110),char(116),char(67),char(111), +char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(50),char(0), +char(98),char(116),char(80),char(111),char(105),char(110),char(116),char(50),char(80),char(111),char(105),char(110),char(116),char(67),char(111),char(110),char(115),char(116),char(114),char(97), +char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(72),char(105),char(110),char(103), +char(101),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116), +char(97),char(0),char(98),char(116),char(72),char(105),char(110),char(103),char(101),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(70), +char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(72),char(105),char(110),char(103),char(101),char(67),char(111),char(110),char(115), +char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(50),char(0),char(98),char(116), +char(67),char(111),char(110),char(101),char(84),char(119),char(105),char(115),char(116),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68), +char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(110),char(101),char(84),char(119),char(105),char(115), +char(116),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(71),char(101), +char(110),char(101),char(114),char(105),char(99),char(54),char(68),char(111),char(102),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68), +char(97),char(116),char(97),char(0),char(98),char(116),char(71),char(101),char(110),char(101),char(114),char(105),char(99),char(54),char(68),char(111),char(102),char(67),char(111),char(110), +char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(50),char(0),char(98), +char(116),char(71),char(101),char(110),char(101),char(114),char(105),char(99),char(54),char(68),char(111),char(102),char(83),char(112),char(114),char(105),char(110),char(103),char(67),char(111), +char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(71),char(101),char(110),char(101),char(114), +char(105),char(99),char(54),char(68),char(111),char(102),char(83),char(112),char(114),char(105),char(110),char(103),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105), +char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(50),char(0),char(98),char(116),char(71),char(101),char(110),char(101), +char(114),char(105),char(99),char(54),char(68),char(111),char(102),char(83),char(112),char(114),char(105),char(110),char(103),char(50),char(67),char(111),char(110),char(115),char(116),char(114), +char(97),char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(71),char(101),char(110),char(101),char(114),char(105),char(99),char(54),char(68), +char(111),char(102),char(83),char(112),char(114),char(105),char(110),char(103),char(50),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68), +char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(50),char(0),char(98),char(116),char(83),char(108),char(105),char(100),char(101),char(114),char(67), +char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(83),char(108),char(105),char(100), +char(101),char(114),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97), +char(116),char(97),char(0),char(98),char(116),char(71),char(101),char(97),char(114),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(70), +char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(71),char(101),char(97),char(114),char(67),char(111),char(110),char(115),char(116), +char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116), +char(66),char(111),char(100),char(121),char(77),char(97),char(116),char(101),char(114),char(105),char(97),char(108),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102), +char(116),char(66),char(111),char(100),char(121),char(78),char(111),char(100),char(101),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111), +char(100),char(121),char(76),char(105),char(110),char(107),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(70), +char(97),char(99),char(101),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(84),char(101),char(116),char(114), +char(97),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(82),char(105),char(103),char(105),char(100),char(65),char(110),char(99),char(104),char(111), +char(114),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(67),char(111),char(110),char(102),char(105),char(103), +char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(80),char(111),char(115),char(101),char(68),char(97),char(116), +char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(68),char(97),char(116), +char(97),char(0),char(98),char(116),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(74),char(111),char(105),char(110),char(116),char(68),char(97),char(116), +char(97),char(0),char(98),char(116),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116), +char(97),char(0),char(98),char(116),char(77),char(117),char(108),char(116),char(105),char(66),char(111),char(100),char(121),char(76),char(105),char(110),char(107),char(68),char(111),char(117), +char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(77),char(117),char(108),char(116),char(105),char(66),char(111),char(100),char(121),char(76), +char(105),char(110),char(107),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(77),char(117),char(108),char(116),char(105), +char(66),char(111),char(100),char(121),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(77),char(117),char(108), +char(116),char(105),char(66),char(111),char(100),char(121),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(77),char(117), +char(108),char(116),char(105),char(66),char(111),char(100),char(121),char(76),char(105),char(110),char(107),char(67),char(111),char(108),char(108),char(105),char(100),char(101),char(114),char(70), +char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(77),char(117),char(108),char(116),char(105),char(66),char(111),char(100),char(121), +char(76),char(105),char(110),char(107),char(67),char(111),char(108),char(108),char(105),char(100),char(101),char(114),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97), +char(116),char(97),char(0),char(0),char(84),char(76),char(69),char(78),char(1),char(0),char(1),char(0),char(2),char(0),char(2),char(0),char(4),char(0),char(4),char(0), +char(4),char(0),char(4),char(0),char(8),char(0),char(0),char(0),char(12),char(0),char(36),char(0),char(8),char(0),char(16),char(0),char(32),char(0),char(16),char(0), +char(32),char(0),char(48),char(0),char(96),char(0),char(64),char(0),char(-128),char(0),char(20),char(0),char(48),char(0),char(80),char(0),char(16),char(0),char(84),char(0), +char(-124),char(0),char(12),char(0),char(52),char(0),char(52),char(0),char(20),char(0),char(64),char(0),char(4),char(0),char(4),char(0),char(8),char(0),char(4),char(0), +char(32),char(0),char(28),char(0),char(60),char(0),char(56),char(0),char(76),char(0),char(76),char(0),char(24),char(0),char(60),char(0),char(60),char(0),char(60),char(0), +char(16),char(0),char(-16),char(5),char(-24),char(1),char(56),char(3),char(16),char(1),char(64),char(0),char(68),char(0),char(-104),char(0),char(88),char(0),char(-72),char(0), +char(104),char(0),char(-8),char(1),char(-72),char(3),char(8),char(0),char(52),char(0),char(52),char(0),char(0),char(0),char(68),char(0),char(84),char(0),char(-124),char(0), char(116),char(0),char(92),char(1),char(-36),char(0),char(-116),char(1),char(124),char(1),char(-44),char(0),char(-4),char(0),char(-52),char(1),char(92),char(1),char(116),char(2), char(-124),char(2),char(-76),char(4),char(-52),char(0),char(108),char(1),char(92),char(0),char(-116),char(0),char(16),char(0),char(100),char(0),char(20),char(0),char(36),char(0), -char(100),char(0),char(92),char(0),char(104),char(0),char(-64),char(0),char(92),char(1),char(104),char(0),char(-76),char(1),char(-16),char(2),char(-120),char(1),char(-64),char(0), -char(100),char(0),char(0),char(0),char(83),char(84),char(82),char(67),char(84),char(0),char(0),char(0),char(10),char(0),char(3),char(0),char(4),char(0),char(0),char(0), -char(4),char(0),char(1),char(0),char(9),char(0),char(2),char(0),char(11),char(0),char(3),char(0),char(10),char(0),char(3),char(0),char(10),char(0),char(4),char(0), -char(10),char(0),char(5),char(0),char(12),char(0),char(2),char(0),char(9),char(0),char(6),char(0),char(9),char(0),char(7),char(0),char(13),char(0),char(1),char(0), -char(7),char(0),char(8),char(0),char(14),char(0),char(1),char(0),char(8),char(0),char(8),char(0),char(15),char(0),char(1),char(0),char(7),char(0),char(8),char(0), -char(16),char(0),char(1),char(0),char(8),char(0),char(8),char(0),char(17),char(0),char(1),char(0),char(13),char(0),char(9),char(0),char(18),char(0),char(1),char(0), -char(14),char(0),char(9),char(0),char(19),char(0),char(2),char(0),char(17),char(0),char(10),char(0),char(13),char(0),char(11),char(0),char(20),char(0),char(2),char(0), -char(18),char(0),char(10),char(0),char(14),char(0),char(11),char(0),char(21),char(0),char(4),char(0),char(4),char(0),char(12),char(0),char(4),char(0),char(13),char(0), -char(2),char(0),char(14),char(0),char(2),char(0),char(15),char(0),char(22),char(0),char(6),char(0),char(13),char(0),char(16),char(0),char(13),char(0),char(17),char(0), -char(4),char(0),char(18),char(0),char(4),char(0),char(19),char(0),char(4),char(0),char(20),char(0),char(0),char(0),char(21),char(0),char(23),char(0),char(6),char(0), -char(14),char(0),char(16),char(0),char(14),char(0),char(17),char(0),char(4),char(0),char(18),char(0),char(4),char(0),char(19),char(0),char(4),char(0),char(20),char(0), -char(0),char(0),char(21),char(0),char(24),char(0),char(3),char(0),char(2),char(0),char(14),char(0),char(2),char(0),char(15),char(0),char(4),char(0),char(22),char(0), -char(25),char(0),char(12),char(0),char(13),char(0),char(23),char(0),char(13),char(0),char(24),char(0),char(13),char(0),char(25),char(0),char(4),char(0),char(26),char(0), -char(4),char(0),char(27),char(0),char(4),char(0),char(28),char(0),char(4),char(0),char(29),char(0),char(22),char(0),char(30),char(0),char(24),char(0),char(31),char(0), -char(21),char(0),char(32),char(0),char(4),char(0),char(33),char(0),char(4),char(0),char(34),char(0),char(26),char(0),char(12),char(0),char(14),char(0),char(23),char(0), -char(14),char(0),char(24),char(0),char(14),char(0),char(25),char(0),char(4),char(0),char(26),char(0),char(4),char(0),char(27),char(0),char(4),char(0),char(28),char(0), -char(4),char(0),char(29),char(0),char(23),char(0),char(30),char(0),char(24),char(0),char(31),char(0),char(4),char(0),char(33),char(0),char(4),char(0),char(34),char(0), -char(21),char(0),char(32),char(0),char(27),char(0),char(3),char(0),char(0),char(0),char(35),char(0),char(4),char(0),char(36),char(0),char(0),char(0),char(37),char(0), -char(28),char(0),char(5),char(0),char(27),char(0),char(38),char(0),char(13),char(0),char(39),char(0),char(13),char(0),char(40),char(0),char(7),char(0),char(41),char(0), -char(0),char(0),char(21),char(0),char(29),char(0),char(5),char(0),char(27),char(0),char(38),char(0),char(13),char(0),char(39),char(0),char(13),char(0),char(42),char(0), -char(7),char(0),char(43),char(0),char(4),char(0),char(44),char(0),char(30),char(0),char(2),char(0),char(13),char(0),char(45),char(0),char(7),char(0),char(46),char(0), -char(31),char(0),char(4),char(0),char(29),char(0),char(47),char(0),char(30),char(0),char(48),char(0),char(4),char(0),char(49),char(0),char(0),char(0),char(37),char(0), -char(32),char(0),char(1),char(0),char(4),char(0),char(50),char(0),char(33),char(0),char(2),char(0),char(2),char(0),char(50),char(0),char(0),char(0),char(51),char(0), -char(34),char(0),char(2),char(0),char(2),char(0),char(52),char(0),char(0),char(0),char(51),char(0),char(35),char(0),char(2),char(0),char(0),char(0),char(52),char(0), -char(0),char(0),char(53),char(0),char(36),char(0),char(8),char(0),char(13),char(0),char(54),char(0),char(14),char(0),char(55),char(0),char(32),char(0),char(56),char(0), -char(34),char(0),char(57),char(0),char(35),char(0),char(58),char(0),char(33),char(0),char(59),char(0),char(4),char(0),char(60),char(0),char(4),char(0),char(61),char(0), -char(37),char(0),char(4),char(0),char(36),char(0),char(62),char(0),char(13),char(0),char(63),char(0),char(4),char(0),char(64),char(0),char(0),char(0),char(37),char(0), -char(38),char(0),char(7),char(0),char(27),char(0),char(38),char(0),char(37),char(0),char(65),char(0),char(25),char(0),char(66),char(0),char(26),char(0),char(67),char(0), -char(39),char(0),char(68),char(0),char(7),char(0),char(43),char(0),char(0),char(0),char(69),char(0),char(40),char(0),char(2),char(0),char(38),char(0),char(70),char(0), -char(13),char(0),char(39),char(0),char(41),char(0),char(4),char(0),char(19),char(0),char(71),char(0),char(27),char(0),char(72),char(0),char(4),char(0),char(73),char(0), -char(7),char(0),char(74),char(0),char(42),char(0),char(4),char(0),char(27),char(0),char(38),char(0),char(41),char(0),char(75),char(0),char(4),char(0),char(76),char(0), -char(7),char(0),char(43),char(0),char(43),char(0),char(3),char(0),char(29),char(0),char(47),char(0),char(4),char(0),char(77),char(0),char(0),char(0),char(37),char(0), -char(44),char(0),char(3),char(0),char(29),char(0),char(47),char(0),char(4),char(0),char(78),char(0),char(0),char(0),char(37),char(0),char(45),char(0),char(3),char(0), -char(29),char(0),char(47),char(0),char(4),char(0),char(77),char(0),char(0),char(0),char(37),char(0),char(46),char(0),char(4),char(0),char(4),char(0),char(79),char(0), -char(7),char(0),char(80),char(0),char(7),char(0),char(81),char(0),char(7),char(0),char(82),char(0),char(39),char(0),char(14),char(0),char(4),char(0),char(83),char(0), -char(4),char(0),char(84),char(0),char(46),char(0),char(85),char(0),char(4),char(0),char(86),char(0),char(7),char(0),char(87),char(0),char(7),char(0),char(88),char(0), -char(7),char(0),char(89),char(0),char(7),char(0),char(90),char(0),char(7),char(0),char(91),char(0),char(4),char(0),char(92),char(0),char(4),char(0),char(93),char(0), -char(4),char(0),char(94),char(0),char(4),char(0),char(95),char(0),char(0),char(0),char(37),char(0),char(47),char(0),char(5),char(0),char(27),char(0),char(38),char(0), -char(37),char(0),char(65),char(0),char(13),char(0),char(39),char(0),char(7),char(0),char(43),char(0),char(4),char(0),char(96),char(0),char(48),char(0),char(5),char(0), -char(29),char(0),char(47),char(0),char(13),char(0),char(97),char(0),char(14),char(0),char(98),char(0),char(4),char(0),char(99),char(0),char(0),char(0),char(100),char(0), -char(49),char(0),char(27),char(0),char(9),char(0),char(101),char(0),char(9),char(0),char(102),char(0),char(27),char(0),char(103),char(0),char(0),char(0),char(35),char(0), -char(20),char(0),char(104),char(0),char(20),char(0),char(105),char(0),char(14),char(0),char(106),char(0),char(14),char(0),char(107),char(0),char(14),char(0),char(108),char(0), -char(8),char(0),char(109),char(0),char(8),char(0),char(110),char(0),char(8),char(0),char(111),char(0),char(8),char(0),char(112),char(0),char(8),char(0),char(113),char(0), -char(8),char(0),char(114),char(0),char(8),char(0),char(115),char(0),char(8),char(0),char(116),char(0),char(8),char(0),char(117),char(0),char(8),char(0),char(118),char(0), -char(4),char(0),char(119),char(0),char(4),char(0),char(120),char(0),char(4),char(0),char(121),char(0),char(4),char(0),char(122),char(0),char(4),char(0),char(123),char(0), -char(4),char(0),char(124),char(0),char(4),char(0),char(125),char(0),char(0),char(0),char(37),char(0),char(50),char(0),char(27),char(0),char(9),char(0),char(101),char(0), -char(9),char(0),char(102),char(0),char(27),char(0),char(103),char(0),char(0),char(0),char(35),char(0),char(19),char(0),char(104),char(0),char(19),char(0),char(105),char(0), -char(13),char(0),char(106),char(0),char(13),char(0),char(107),char(0),char(13),char(0),char(108),char(0),char(7),char(0),char(109),char(0),char(7),char(0),char(110),char(0), -char(7),char(0),char(111),char(0),char(7),char(0),char(112),char(0),char(7),char(0),char(113),char(0),char(7),char(0),char(114),char(0),char(7),char(0),char(115),char(0), -char(7),char(0),char(116),char(0),char(7),char(0),char(117),char(0),char(7),char(0),char(118),char(0),char(4),char(0),char(119),char(0),char(4),char(0),char(120),char(0), -char(4),char(0),char(121),char(0),char(4),char(0),char(122),char(0),char(4),char(0),char(123),char(0),char(4),char(0),char(124),char(0),char(4),char(0),char(125),char(0), -char(0),char(0),char(37),char(0),char(51),char(0),char(22),char(0),char(8),char(0),char(126),char(0),char(8),char(0),char(127),char(0),char(8),char(0),char(111),char(0), -char(8),char(0),char(-128),char(0),char(8),char(0),char(115),char(0),char(8),char(0),char(-127),char(0),char(8),char(0),char(-126),char(0),char(8),char(0),char(-125),char(0), -char(8),char(0),char(-124),char(0),char(8),char(0),char(-123),char(0),char(8),char(0),char(-122),char(0),char(8),char(0),char(-121),char(0),char(8),char(0),char(-120),char(0), -char(8),char(0),char(-119),char(0),char(8),char(0),char(-118),char(0),char(8),char(0),char(-117),char(0),char(4),char(0),char(-116),char(0),char(4),char(0),char(-115),char(0), -char(4),char(0),char(-114),char(0),char(4),char(0),char(-113),char(0),char(4),char(0),char(-112),char(0),char(0),char(0),char(37),char(0),char(52),char(0),char(22),char(0), -char(7),char(0),char(126),char(0),char(7),char(0),char(127),char(0),char(7),char(0),char(111),char(0),char(7),char(0),char(-128),char(0),char(7),char(0),char(115),char(0), -char(7),char(0),char(-127),char(0),char(7),char(0),char(-126),char(0),char(7),char(0),char(-125),char(0),char(7),char(0),char(-124),char(0),char(7),char(0),char(-123),char(0), -char(7),char(0),char(-122),char(0),char(7),char(0),char(-121),char(0),char(7),char(0),char(-120),char(0),char(7),char(0),char(-119),char(0),char(7),char(0),char(-118),char(0), -char(7),char(0),char(-117),char(0),char(4),char(0),char(-116),char(0),char(4),char(0),char(-115),char(0),char(4),char(0),char(-114),char(0),char(4),char(0),char(-113),char(0), -char(4),char(0),char(-112),char(0),char(0),char(0),char(37),char(0),char(53),char(0),char(2),char(0),char(51),char(0),char(-111),char(0),char(14),char(0),char(-110),char(0), -char(54),char(0),char(2),char(0),char(52),char(0),char(-111),char(0),char(13),char(0),char(-110),char(0),char(55),char(0),char(21),char(0),char(50),char(0),char(-109),char(0), -char(17),char(0),char(-108),char(0),char(13),char(0),char(-107),char(0),char(13),char(0),char(-106),char(0),char(13),char(0),char(-105),char(0),char(13),char(0),char(-104),char(0), -char(13),char(0),char(-110),char(0),char(13),char(0),char(-103),char(0),char(13),char(0),char(-102),char(0),char(13),char(0),char(-101),char(0),char(13),char(0),char(-100),char(0), -char(7),char(0),char(-99),char(0),char(7),char(0),char(-98),char(0),char(7),char(0),char(-97),char(0),char(7),char(0),char(-96),char(0),char(7),char(0),char(-95),char(0), -char(7),char(0),char(-94),char(0),char(7),char(0),char(-93),char(0),char(7),char(0),char(-92),char(0),char(7),char(0),char(-91),char(0),char(4),char(0),char(-90),char(0), -char(56),char(0),char(22),char(0),char(49),char(0),char(-109),char(0),char(18),char(0),char(-108),char(0),char(14),char(0),char(-107),char(0),char(14),char(0),char(-106),char(0), -char(14),char(0),char(-105),char(0),char(14),char(0),char(-104),char(0),char(14),char(0),char(-110),char(0),char(14),char(0),char(-103),char(0),char(14),char(0),char(-102),char(0), -char(14),char(0),char(-101),char(0),char(14),char(0),char(-100),char(0),char(8),char(0),char(-99),char(0),char(8),char(0),char(-98),char(0),char(8),char(0),char(-97),char(0), -char(8),char(0),char(-96),char(0),char(8),char(0),char(-95),char(0),char(8),char(0),char(-94),char(0),char(8),char(0),char(-93),char(0),char(8),char(0),char(-92),char(0), -char(8),char(0),char(-91),char(0),char(4),char(0),char(-90),char(0),char(0),char(0),char(37),char(0),char(57),char(0),char(2),char(0),char(4),char(0),char(-89),char(0), -char(4),char(0),char(-88),char(0),char(58),char(0),char(13),char(0),char(55),char(0),char(-87),char(0),char(55),char(0),char(-86),char(0),char(0),char(0),char(35),char(0), -char(4),char(0),char(-85),char(0),char(4),char(0),char(-84),char(0),char(4),char(0),char(-83),char(0),char(4),char(0),char(-82),char(0),char(7),char(0),char(-81),char(0), -char(7),char(0),char(-80),char(0),char(4),char(0),char(-79),char(0),char(4),char(0),char(-78),char(0),char(7),char(0),char(-77),char(0),char(4),char(0),char(-76),char(0), -char(59),char(0),char(13),char(0),char(60),char(0),char(-87),char(0),char(60),char(0),char(-86),char(0),char(0),char(0),char(35),char(0),char(4),char(0),char(-85),char(0), -char(4),char(0),char(-84),char(0),char(4),char(0),char(-83),char(0),char(4),char(0),char(-82),char(0),char(7),char(0),char(-81),char(0),char(7),char(0),char(-80),char(0), -char(4),char(0),char(-79),char(0),char(4),char(0),char(-78),char(0),char(7),char(0),char(-77),char(0),char(4),char(0),char(-76),char(0),char(61),char(0),char(14),char(0), -char(56),char(0),char(-87),char(0),char(56),char(0),char(-86),char(0),char(0),char(0),char(35),char(0),char(4),char(0),char(-85),char(0),char(4),char(0),char(-84),char(0), -char(4),char(0),char(-83),char(0),char(4),char(0),char(-82),char(0),char(8),char(0),char(-81),char(0),char(8),char(0),char(-80),char(0),char(4),char(0),char(-79),char(0), -char(4),char(0),char(-78),char(0),char(8),char(0),char(-77),char(0),char(4),char(0),char(-76),char(0),char(0),char(0),char(-75),char(0),char(62),char(0),char(3),char(0), -char(59),char(0),char(-74),char(0),char(13),char(0),char(-73),char(0),char(13),char(0),char(-72),char(0),char(63),char(0),char(3),char(0),char(61),char(0),char(-74),char(0), -char(14),char(0),char(-73),char(0),char(14),char(0),char(-72),char(0),char(64),char(0),char(3),char(0),char(59),char(0),char(-74),char(0),char(14),char(0),char(-73),char(0), -char(14),char(0),char(-72),char(0),char(65),char(0),char(13),char(0),char(59),char(0),char(-74),char(0),char(20),char(0),char(-71),char(0),char(20),char(0),char(-70),char(0), -char(4),char(0),char(-69),char(0),char(4),char(0),char(-68),char(0),char(4),char(0),char(-67),char(0),char(7),char(0),char(-66),char(0),char(7),char(0),char(-65),char(0), -char(7),char(0),char(-64),char(0),char(7),char(0),char(-63),char(0),char(7),char(0),char(-62),char(0),char(7),char(0),char(-61),char(0),char(7),char(0),char(-60),char(0), -char(66),char(0),char(13),char(0),char(59),char(0),char(-74),char(0),char(19),char(0),char(-71),char(0),char(19),char(0),char(-70),char(0),char(4),char(0),char(-69),char(0), -char(4),char(0),char(-68),char(0),char(4),char(0),char(-67),char(0),char(7),char(0),char(-66),char(0),char(7),char(0),char(-65),char(0),char(7),char(0),char(-64),char(0), -char(7),char(0),char(-63),char(0),char(7),char(0),char(-62),char(0),char(7),char(0),char(-61),char(0),char(7),char(0),char(-60),char(0),char(67),char(0),char(14),char(0), -char(61),char(0),char(-74),char(0),char(20),char(0),char(-71),char(0),char(20),char(0),char(-70),char(0),char(4),char(0),char(-69),char(0),char(4),char(0),char(-68),char(0), -char(4),char(0),char(-67),char(0),char(8),char(0),char(-66),char(0),char(8),char(0),char(-65),char(0),char(8),char(0),char(-64),char(0),char(8),char(0),char(-63),char(0), -char(8),char(0),char(-62),char(0),char(8),char(0),char(-61),char(0),char(8),char(0),char(-60),char(0),char(0),char(0),char(-59),char(0),char(68),char(0),char(10),char(0), -char(61),char(0),char(-74),char(0),char(20),char(0),char(-71),char(0),char(20),char(0),char(-70),char(0),char(8),char(0),char(-58),char(0),char(8),char(0),char(-57),char(0), -char(8),char(0),char(-56),char(0),char(8),char(0),char(-62),char(0),char(8),char(0),char(-61),char(0),char(8),char(0),char(-60),char(0),char(8),char(0),char(127),char(0), -char(69),char(0),char(11),char(0),char(59),char(0),char(-74),char(0),char(19),char(0),char(-71),char(0),char(19),char(0),char(-70),char(0),char(7),char(0),char(-58),char(0), -char(7),char(0),char(-57),char(0),char(7),char(0),char(-56),char(0),char(7),char(0),char(-62),char(0),char(7),char(0),char(-61),char(0),char(7),char(0),char(-60),char(0), -char(7),char(0),char(127),char(0),char(0),char(0),char(21),char(0),char(70),char(0),char(9),char(0),char(59),char(0),char(-74),char(0),char(19),char(0),char(-71),char(0), -char(19),char(0),char(-70),char(0),char(13),char(0),char(-55),char(0),char(13),char(0),char(-54),char(0),char(13),char(0),char(-53),char(0),char(13),char(0),char(-52),char(0), -char(4),char(0),char(-51),char(0),char(4),char(0),char(-50),char(0),char(71),char(0),char(9),char(0),char(61),char(0),char(-74),char(0),char(20),char(0),char(-71),char(0), -char(20),char(0),char(-70),char(0),char(14),char(0),char(-55),char(0),char(14),char(0),char(-54),char(0),char(14),char(0),char(-53),char(0),char(14),char(0),char(-52),char(0), -char(4),char(0),char(-51),char(0),char(4),char(0),char(-50),char(0),char(72),char(0),char(5),char(0),char(70),char(0),char(-49),char(0),char(4),char(0),char(-48),char(0), -char(7),char(0),char(-47),char(0),char(7),char(0),char(-46),char(0),char(7),char(0),char(-45),char(0),char(73),char(0),char(5),char(0),char(71),char(0),char(-49),char(0), -char(4),char(0),char(-48),char(0),char(8),char(0),char(-47),char(0),char(8),char(0),char(-46),char(0),char(8),char(0),char(-45),char(0),char(74),char(0),char(41),char(0), -char(59),char(0),char(-74),char(0),char(19),char(0),char(-71),char(0),char(19),char(0),char(-70),char(0),char(13),char(0),char(-55),char(0),char(13),char(0),char(-54),char(0), -char(13),char(0),char(-44),char(0),char(13),char(0),char(-43),char(0),char(13),char(0),char(-42),char(0),char(13),char(0),char(-41),char(0),char(13),char(0),char(-40),char(0), -char(13),char(0),char(-39),char(0),char(13),char(0),char(-38),char(0),char(13),char(0),char(-37),char(0),char(13),char(0),char(-36),char(0),char(13),char(0),char(-35),char(0), -char(13),char(0),char(-34),char(0),char(0),char(0),char(-33),char(0),char(0),char(0),char(-32),char(0),char(0),char(0),char(-31),char(0),char(0),char(0),char(-30),char(0), -char(0),char(0),char(-29),char(0),char(0),char(0),char(-59),char(0),char(13),char(0),char(-53),char(0),char(13),char(0),char(-52),char(0),char(13),char(0),char(-28),char(0), -char(13),char(0),char(-27),char(0),char(13),char(0),char(-26),char(0),char(13),char(0),char(-25),char(0),char(13),char(0),char(-24),char(0),char(13),char(0),char(-23),char(0), -char(13),char(0),char(-22),char(0),char(13),char(0),char(-21),char(0),char(13),char(0),char(-20),char(0),char(13),char(0),char(-19),char(0),char(13),char(0),char(-18),char(0), -char(0),char(0),char(-17),char(0),char(0),char(0),char(-16),char(0),char(0),char(0),char(-15),char(0),char(0),char(0),char(-14),char(0),char(0),char(0),char(-13),char(0), -char(4),char(0),char(-12),char(0),char(75),char(0),char(41),char(0),char(61),char(0),char(-74),char(0),char(20),char(0),char(-71),char(0),char(20),char(0),char(-70),char(0), -char(14),char(0),char(-55),char(0),char(14),char(0),char(-54),char(0),char(14),char(0),char(-44),char(0),char(14),char(0),char(-43),char(0),char(14),char(0),char(-42),char(0), -char(14),char(0),char(-41),char(0),char(14),char(0),char(-40),char(0),char(14),char(0),char(-39),char(0),char(14),char(0),char(-38),char(0),char(14),char(0),char(-37),char(0), -char(14),char(0),char(-36),char(0),char(14),char(0),char(-35),char(0),char(14),char(0),char(-34),char(0),char(0),char(0),char(-33),char(0),char(0),char(0),char(-32),char(0), -char(0),char(0),char(-31),char(0),char(0),char(0),char(-30),char(0),char(0),char(0),char(-29),char(0),char(0),char(0),char(-59),char(0),char(14),char(0),char(-53),char(0), -char(14),char(0),char(-52),char(0),char(14),char(0),char(-28),char(0),char(14),char(0),char(-27),char(0),char(14),char(0),char(-26),char(0),char(14),char(0),char(-25),char(0), -char(14),char(0),char(-24),char(0),char(14),char(0),char(-23),char(0),char(14),char(0),char(-22),char(0),char(14),char(0),char(-21),char(0),char(14),char(0),char(-20),char(0), -char(14),char(0),char(-19),char(0),char(14),char(0),char(-18),char(0),char(0),char(0),char(-17),char(0),char(0),char(0),char(-16),char(0),char(0),char(0),char(-15),char(0), -char(0),char(0),char(-14),char(0),char(0),char(0),char(-13),char(0),char(4),char(0),char(-12),char(0),char(76),char(0),char(9),char(0),char(59),char(0),char(-74),char(0), -char(19),char(0),char(-71),char(0),char(19),char(0),char(-70),char(0),char(7),char(0),char(-55),char(0),char(7),char(0),char(-54),char(0),char(7),char(0),char(-53),char(0), -char(7),char(0),char(-52),char(0),char(4),char(0),char(-51),char(0),char(4),char(0),char(-50),char(0),char(77),char(0),char(9),char(0),char(61),char(0),char(-74),char(0), -char(20),char(0),char(-71),char(0),char(20),char(0),char(-70),char(0),char(8),char(0),char(-55),char(0),char(8),char(0),char(-54),char(0),char(8),char(0),char(-53),char(0), -char(8),char(0),char(-52),char(0),char(4),char(0),char(-51),char(0),char(4),char(0),char(-50),char(0),char(78),char(0),char(5),char(0),char(58),char(0),char(-74),char(0), -char(13),char(0),char(-11),char(0),char(13),char(0),char(-10),char(0),char(7),char(0),char(-9),char(0),char(0),char(0),char(37),char(0),char(79),char(0),char(4),char(0), -char(61),char(0),char(-74),char(0),char(14),char(0),char(-11),char(0),char(14),char(0),char(-10),char(0),char(8),char(0),char(-9),char(0),char(80),char(0),char(4),char(0), -char(7),char(0),char(-8),char(0),char(7),char(0),char(-7),char(0),char(7),char(0),char(-6),char(0),char(4),char(0),char(79),char(0),char(81),char(0),char(10),char(0), -char(80),char(0),char(-5),char(0),char(13),char(0),char(-4),char(0),char(13),char(0),char(-3),char(0),char(13),char(0),char(-2),char(0),char(13),char(0),char(-1),char(0), -char(13),char(0),char(0),char(1),char(7),char(0),char(-99),char(0),char(7),char(0),char(1),char(1),char(4),char(0),char(2),char(1),char(4),char(0),char(53),char(0), -char(82),char(0),char(4),char(0),char(80),char(0),char(-5),char(0),char(4),char(0),char(3),char(1),char(7),char(0),char(4),char(1),char(4),char(0),char(5),char(1), -char(83),char(0),char(4),char(0),char(13),char(0),char(0),char(1),char(80),char(0),char(-5),char(0),char(4),char(0),char(6),char(1),char(7),char(0),char(7),char(1), -char(84),char(0),char(7),char(0),char(13),char(0),char(8),char(1),char(80),char(0),char(-5),char(0),char(4),char(0),char(9),char(1),char(7),char(0),char(10),char(1), -char(7),char(0),char(11),char(1),char(7),char(0),char(12),char(1),char(4),char(0),char(53),char(0),char(85),char(0),char(6),char(0),char(17),char(0),char(13),char(1), -char(13),char(0),char(11),char(1),char(13),char(0),char(14),char(1),char(60),char(0),char(15),char(1),char(4),char(0),char(16),char(1),char(7),char(0),char(12),char(1), -char(86),char(0),char(26),char(0),char(4),char(0),char(17),char(1),char(7),char(0),char(18),char(1),char(7),char(0),char(127),char(0),char(7),char(0),char(19),char(1), -char(7),char(0),char(20),char(1),char(7),char(0),char(21),char(1),char(7),char(0),char(22),char(1),char(7),char(0),char(23),char(1),char(7),char(0),char(24),char(1), -char(7),char(0),char(25),char(1),char(7),char(0),char(26),char(1),char(7),char(0),char(27),char(1),char(7),char(0),char(28),char(1),char(7),char(0),char(29),char(1), -char(7),char(0),char(30),char(1),char(7),char(0),char(31),char(1),char(7),char(0),char(32),char(1),char(7),char(0),char(33),char(1),char(7),char(0),char(34),char(1), -char(7),char(0),char(35),char(1),char(7),char(0),char(36),char(1),char(4),char(0),char(37),char(1),char(4),char(0),char(38),char(1),char(4),char(0),char(39),char(1), -char(4),char(0),char(40),char(1),char(4),char(0),char(120),char(0),char(87),char(0),char(12),char(0),char(17),char(0),char(41),char(1),char(17),char(0),char(42),char(1), -char(17),char(0),char(43),char(1),char(13),char(0),char(44),char(1),char(13),char(0),char(45),char(1),char(7),char(0),char(46),char(1),char(4),char(0),char(47),char(1), -char(4),char(0),char(48),char(1),char(4),char(0),char(49),char(1),char(4),char(0),char(50),char(1),char(7),char(0),char(10),char(1),char(4),char(0),char(53),char(0), -char(88),char(0),char(27),char(0),char(19),char(0),char(51),char(1),char(17),char(0),char(52),char(1),char(17),char(0),char(53),char(1),char(13),char(0),char(44),char(1), -char(13),char(0),char(54),char(1),char(13),char(0),char(55),char(1),char(13),char(0),char(56),char(1),char(13),char(0),char(57),char(1),char(13),char(0),char(58),char(1), -char(4),char(0),char(59),char(1),char(7),char(0),char(60),char(1),char(4),char(0),char(61),char(1),char(4),char(0),char(62),char(1),char(4),char(0),char(63),char(1), -char(7),char(0),char(64),char(1),char(7),char(0),char(65),char(1),char(4),char(0),char(66),char(1),char(4),char(0),char(67),char(1),char(7),char(0),char(68),char(1), -char(7),char(0),char(69),char(1),char(7),char(0),char(70),char(1),char(7),char(0),char(71),char(1),char(7),char(0),char(72),char(1),char(7),char(0),char(73),char(1), -char(4),char(0),char(74),char(1),char(4),char(0),char(75),char(1),char(4),char(0),char(76),char(1),char(89),char(0),char(12),char(0),char(9),char(0),char(77),char(1), -char(9),char(0),char(78),char(1),char(13),char(0),char(79),char(1),char(7),char(0),char(80),char(1),char(7),char(0),char(-125),char(0),char(7),char(0),char(81),char(1), -char(4),char(0),char(82),char(1),char(13),char(0),char(83),char(1),char(4),char(0),char(84),char(1),char(4),char(0),char(85),char(1),char(4),char(0),char(86),char(1), -char(4),char(0),char(53),char(0),char(90),char(0),char(19),char(0),char(50),char(0),char(-109),char(0),char(87),char(0),char(87),char(1),char(80),char(0),char(88),char(1), -char(81),char(0),char(89),char(1),char(82),char(0),char(90),char(1),char(83),char(0),char(91),char(1),char(84),char(0),char(92),char(1),char(85),char(0),char(93),char(1), -char(88),char(0),char(94),char(1),char(89),char(0),char(95),char(1),char(4),char(0),char(96),char(1),char(4),char(0),char(62),char(1),char(4),char(0),char(97),char(1), -char(4),char(0),char(98),char(1),char(4),char(0),char(99),char(1),char(4),char(0),char(100),char(1),char(4),char(0),char(101),char(1),char(4),char(0),char(102),char(1), -char(86),char(0),char(103),char(1),char(91),char(0),char(24),char(0),char(16),char(0),char(104),char(1),char(14),char(0),char(105),char(1),char(14),char(0),char(106),char(1), -char(14),char(0),char(107),char(1),char(14),char(0),char(108),char(1),char(14),char(0),char(109),char(1),char(8),char(0),char(110),char(1),char(4),char(0),char(111),char(1), -char(4),char(0),char(86),char(1),char(4),char(0),char(112),char(1),char(4),char(0),char(113),char(1),char(8),char(0),char(114),char(1),char(8),char(0),char(115),char(1), -char(8),char(0),char(116),char(1),char(8),char(0),char(117),char(1),char(8),char(0),char(118),char(1),char(8),char(0),char(119),char(1),char(8),char(0),char(120),char(1), -char(8),char(0),char(121),char(1),char(8),char(0),char(122),char(1),char(0),char(0),char(123),char(1),char(0),char(0),char(124),char(1),char(49),char(0),char(125),char(1), -char(0),char(0),char(126),char(1),char(92),char(0),char(24),char(0),char(15),char(0),char(104),char(1),char(13),char(0),char(105),char(1),char(13),char(0),char(106),char(1), -char(13),char(0),char(107),char(1),char(13),char(0),char(108),char(1),char(13),char(0),char(109),char(1),char(4),char(0),char(112),char(1),char(7),char(0),char(110),char(1), -char(4),char(0),char(111),char(1),char(4),char(0),char(86),char(1),char(7),char(0),char(114),char(1),char(7),char(0),char(115),char(1),char(7),char(0),char(116),char(1), -char(4),char(0),char(113),char(1),char(7),char(0),char(117),char(1),char(7),char(0),char(118),char(1),char(7),char(0),char(119),char(1),char(7),char(0),char(120),char(1), -char(7),char(0),char(121),char(1),char(7),char(0),char(122),char(1),char(0),char(0),char(123),char(1),char(0),char(0),char(124),char(1),char(50),char(0),char(125),char(1), -char(0),char(0),char(126),char(1),char(93),char(0),char(9),char(0),char(20),char(0),char(127),char(1),char(14),char(0),char(-128),char(1),char(8),char(0),char(-127),char(1), -char(0),char(0),char(-126),char(1),char(91),char(0),char(90),char(1),char(49),char(0),char(-125),char(1),char(0),char(0),char(126),char(1),char(4),char(0),char(97),char(1), -char(0),char(0),char(37),char(0),char(94),char(0),char(7),char(0),char(0),char(0),char(-126),char(1),char(92),char(0),char(90),char(1),char(50),char(0),char(-125),char(1), -char(19),char(0),char(127),char(1),char(13),char(0),char(-128),char(1),char(7),char(0),char(-127),char(1),char(4),char(0),char(97),char(1),}; +char(100),char(0),char(92),char(0),char(104),char(0),char(-64),char(0),char(92),char(1),char(104),char(0),char(-68),char(1),char(112),char(3),char(-56),char(1),char(-68),char(0), +char(100),char(0),char(28),char(1),char(-12),char(1),char(0),char(0),char(83),char(84),char(82),char(67),char(88),char(0),char(0),char(0),char(10),char(0),char(3),char(0), +char(4),char(0),char(0),char(0),char(4),char(0),char(1),char(0),char(9),char(0),char(2),char(0),char(11),char(0),char(3),char(0),char(10),char(0),char(3),char(0), +char(10),char(0),char(4),char(0),char(10),char(0),char(5),char(0),char(12),char(0),char(2),char(0),char(9),char(0),char(6),char(0),char(9),char(0),char(7),char(0), +char(13),char(0),char(1),char(0),char(7),char(0),char(8),char(0),char(14),char(0),char(1),char(0),char(8),char(0),char(8),char(0),char(15),char(0),char(1),char(0), +char(7),char(0),char(8),char(0),char(16),char(0),char(1),char(0),char(8),char(0),char(8),char(0),char(17),char(0),char(1),char(0),char(13),char(0),char(9),char(0), +char(18),char(0),char(1),char(0),char(14),char(0),char(9),char(0),char(19),char(0),char(2),char(0),char(17),char(0),char(10),char(0),char(13),char(0),char(11),char(0), +char(20),char(0),char(2),char(0),char(18),char(0),char(10),char(0),char(14),char(0),char(11),char(0),char(21),char(0),char(4),char(0),char(4),char(0),char(12),char(0), +char(4),char(0),char(13),char(0),char(2),char(0),char(14),char(0),char(2),char(0),char(15),char(0),char(22),char(0),char(6),char(0),char(13),char(0),char(16),char(0), +char(13),char(0),char(17),char(0),char(4),char(0),char(18),char(0),char(4),char(0),char(19),char(0),char(4),char(0),char(20),char(0),char(0),char(0),char(21),char(0), +char(23),char(0),char(6),char(0),char(14),char(0),char(16),char(0),char(14),char(0),char(17),char(0),char(4),char(0),char(18),char(0),char(4),char(0),char(19),char(0), +char(4),char(0),char(20),char(0),char(0),char(0),char(21),char(0),char(24),char(0),char(3),char(0),char(2),char(0),char(14),char(0),char(2),char(0),char(15),char(0), +char(4),char(0),char(22),char(0),char(25),char(0),char(12),char(0),char(13),char(0),char(23),char(0),char(13),char(0),char(24),char(0),char(13),char(0),char(25),char(0), +char(4),char(0),char(26),char(0),char(4),char(0),char(27),char(0),char(4),char(0),char(28),char(0),char(4),char(0),char(29),char(0),char(22),char(0),char(30),char(0), +char(24),char(0),char(31),char(0),char(21),char(0),char(32),char(0),char(4),char(0),char(33),char(0),char(4),char(0),char(34),char(0),char(26),char(0),char(12),char(0), +char(14),char(0),char(23),char(0),char(14),char(0),char(24),char(0),char(14),char(0),char(25),char(0),char(4),char(0),char(26),char(0),char(4),char(0),char(27),char(0), +char(4),char(0),char(28),char(0),char(4),char(0),char(29),char(0),char(23),char(0),char(30),char(0),char(24),char(0),char(31),char(0),char(4),char(0),char(33),char(0), +char(4),char(0),char(34),char(0),char(21),char(0),char(32),char(0),char(27),char(0),char(3),char(0),char(0),char(0),char(35),char(0),char(4),char(0),char(36),char(0), +char(0),char(0),char(37),char(0),char(28),char(0),char(5),char(0),char(27),char(0),char(38),char(0),char(13),char(0),char(39),char(0),char(13),char(0),char(40),char(0), +char(7),char(0),char(41),char(0),char(0),char(0),char(21),char(0),char(29),char(0),char(5),char(0),char(27),char(0),char(38),char(0),char(13),char(0),char(39),char(0), +char(13),char(0),char(42),char(0),char(7),char(0),char(43),char(0),char(4),char(0),char(44),char(0),char(30),char(0),char(2),char(0),char(13),char(0),char(45),char(0), +char(7),char(0),char(46),char(0),char(31),char(0),char(4),char(0),char(29),char(0),char(47),char(0),char(30),char(0),char(48),char(0),char(4),char(0),char(49),char(0), +char(0),char(0),char(37),char(0),char(32),char(0),char(1),char(0),char(4),char(0),char(50),char(0),char(33),char(0),char(2),char(0),char(2),char(0),char(50),char(0), +char(0),char(0),char(51),char(0),char(34),char(0),char(2),char(0),char(2),char(0),char(52),char(0),char(0),char(0),char(51),char(0),char(35),char(0),char(2),char(0), +char(0),char(0),char(52),char(0),char(0),char(0),char(53),char(0),char(36),char(0),char(8),char(0),char(13),char(0),char(54),char(0),char(14),char(0),char(55),char(0), +char(32),char(0),char(56),char(0),char(34),char(0),char(57),char(0),char(35),char(0),char(58),char(0),char(33),char(0),char(59),char(0),char(4),char(0),char(60),char(0), +char(4),char(0),char(61),char(0),char(37),char(0),char(4),char(0),char(36),char(0),char(62),char(0),char(13),char(0),char(63),char(0),char(4),char(0),char(64),char(0), +char(0),char(0),char(37),char(0),char(38),char(0),char(7),char(0),char(27),char(0),char(38),char(0),char(37),char(0),char(65),char(0),char(25),char(0),char(66),char(0), +char(26),char(0),char(67),char(0),char(39),char(0),char(68),char(0),char(7),char(0),char(43),char(0),char(0),char(0),char(69),char(0),char(40),char(0),char(2),char(0), +char(38),char(0),char(70),char(0),char(13),char(0),char(39),char(0),char(41),char(0),char(4),char(0),char(19),char(0),char(71),char(0),char(27),char(0),char(72),char(0), +char(4),char(0),char(73),char(0),char(7),char(0),char(74),char(0),char(42),char(0),char(4),char(0),char(27),char(0),char(38),char(0),char(41),char(0),char(75),char(0), +char(4),char(0),char(76),char(0),char(7),char(0),char(43),char(0),char(43),char(0),char(3),char(0),char(29),char(0),char(47),char(0),char(4),char(0),char(77),char(0), +char(0),char(0),char(37),char(0),char(44),char(0),char(3),char(0),char(29),char(0),char(47),char(0),char(4),char(0),char(78),char(0),char(0),char(0),char(37),char(0), +char(45),char(0),char(3),char(0),char(29),char(0),char(47),char(0),char(4),char(0),char(77),char(0),char(0),char(0),char(37),char(0),char(46),char(0),char(4),char(0), +char(4),char(0),char(79),char(0),char(7),char(0),char(80),char(0),char(7),char(0),char(81),char(0),char(7),char(0),char(82),char(0),char(39),char(0),char(14),char(0), +char(4),char(0),char(83),char(0),char(4),char(0),char(84),char(0),char(46),char(0),char(85),char(0),char(4),char(0),char(86),char(0),char(7),char(0),char(87),char(0), +char(7),char(0),char(88),char(0),char(7),char(0),char(89),char(0),char(7),char(0),char(90),char(0),char(7),char(0),char(91),char(0),char(4),char(0),char(92),char(0), +char(4),char(0),char(93),char(0),char(4),char(0),char(94),char(0),char(4),char(0),char(95),char(0),char(0),char(0),char(37),char(0),char(47),char(0),char(38),char(0), +char(14),char(0),char(96),char(0),char(14),char(0),char(97),char(0),char(14),char(0),char(98),char(0),char(14),char(0),char(99),char(0),char(14),char(0),char(100),char(0), +char(14),char(0),char(101),char(0),char(14),char(0),char(102),char(0),char(8),char(0),char(103),char(0),char(8),char(0),char(104),char(0),char(8),char(0),char(105),char(0), +char(8),char(0),char(106),char(0),char(8),char(0),char(107),char(0),char(8),char(0),char(108),char(0),char(4),char(0),char(109),char(0),char(4),char(0),char(110),char(0), +char(4),char(0),char(111),char(0),char(4),char(0),char(112),char(0),char(4),char(0),char(113),char(0),char(8),char(0),char(114),char(0),char(8),char(0),char(115),char(0), +char(8),char(0),char(116),char(0),char(8),char(0),char(117),char(0),char(8),char(0),char(118),char(0),char(8),char(0),char(119),char(0),char(8),char(0),char(120),char(0), +char(8),char(0),char(121),char(0),char(8),char(0),char(122),char(0),char(4),char(0),char(123),char(0),char(4),char(0),char(124),char(0),char(4),char(0),char(125),char(0), +char(4),char(0),char(126),char(0),char(4),char(0),char(127),char(0),char(4),char(0),char(-128),char(0),char(8),char(0),char(-127),char(0),char(8),char(0),char(-126),char(0), +char(4),char(0),char(44),char(0),char(48),char(0),char(-125),char(0),char(48),char(0),char(-124),char(0),char(49),char(0),char(38),char(0),char(13),char(0),char(96),char(0), +char(13),char(0),char(97),char(0),char(13),char(0),char(98),char(0),char(13),char(0),char(99),char(0),char(13),char(0),char(100),char(0),char(13),char(0),char(101),char(0), +char(13),char(0),char(102),char(0),char(7),char(0),char(103),char(0),char(7),char(0),char(104),char(0),char(7),char(0),char(105),char(0),char(7),char(0),char(106),char(0), +char(7),char(0),char(107),char(0),char(7),char(0),char(108),char(0),char(4),char(0),char(109),char(0),char(4),char(0),char(110),char(0),char(4),char(0),char(111),char(0), +char(4),char(0),char(112),char(0),char(4),char(0),char(113),char(0),char(7),char(0),char(114),char(0),char(7),char(0),char(115),char(0),char(7),char(0),char(116),char(0), +char(7),char(0),char(117),char(0),char(7),char(0),char(118),char(0),char(7),char(0),char(119),char(0),char(7),char(0),char(120),char(0),char(7),char(0),char(121),char(0), +char(7),char(0),char(122),char(0),char(4),char(0),char(123),char(0),char(4),char(0),char(124),char(0),char(4),char(0),char(125),char(0),char(4),char(0),char(126),char(0), +char(4),char(0),char(127),char(0),char(4),char(0),char(-128),char(0),char(7),char(0),char(-127),char(0),char(7),char(0),char(-126),char(0),char(4),char(0),char(44),char(0), +char(50),char(0),char(-125),char(0),char(50),char(0),char(-124),char(0),char(51),char(0),char(5),char(0),char(27),char(0),char(38),char(0),char(37),char(0),char(65),char(0), +char(13),char(0),char(39),char(0),char(7),char(0),char(43),char(0),char(4),char(0),char(-123),char(0),char(52),char(0),char(5),char(0),char(29),char(0),char(47),char(0), +char(13),char(0),char(-122),char(0),char(14),char(0),char(-121),char(0),char(4),char(0),char(-120),char(0),char(0),char(0),char(-119),char(0),char(48),char(0),char(29),char(0), +char(9),char(0),char(-118),char(0),char(9),char(0),char(-117),char(0),char(27),char(0),char(-116),char(0),char(0),char(0),char(35),char(0),char(20),char(0),char(-115),char(0), +char(20),char(0),char(-114),char(0),char(14),char(0),char(-113),char(0),char(14),char(0),char(-112),char(0),char(14),char(0),char(-111),char(0),char(8),char(0),char(-126),char(0), +char(8),char(0),char(-110),char(0),char(8),char(0),char(-109),char(0),char(8),char(0),char(-108),char(0),char(8),char(0),char(-107),char(0),char(8),char(0),char(-106),char(0), +char(8),char(0),char(-105),char(0),char(8),char(0),char(-104),char(0),char(8),char(0),char(-103),char(0),char(8),char(0),char(-102),char(0),char(4),char(0),char(-101),char(0), +char(4),char(0),char(-100),char(0),char(4),char(0),char(-99),char(0),char(4),char(0),char(-98),char(0),char(4),char(0),char(-97),char(0),char(4),char(0),char(-96),char(0), +char(4),char(0),char(-95),char(0),char(4),char(0),char(-94),char(0),char(4),char(0),char(-93),char(0),char(4),char(0),char(-92),char(0),char(50),char(0),char(29),char(0), +char(9),char(0),char(-118),char(0),char(9),char(0),char(-117),char(0),char(27),char(0),char(-116),char(0),char(0),char(0),char(35),char(0),char(19),char(0),char(-115),char(0), +char(19),char(0),char(-114),char(0),char(13),char(0),char(-113),char(0),char(13),char(0),char(-112),char(0),char(13),char(0),char(-111),char(0),char(7),char(0),char(-126),char(0), +char(7),char(0),char(-110),char(0),char(7),char(0),char(-109),char(0),char(7),char(0),char(-108),char(0),char(7),char(0),char(-107),char(0),char(7),char(0),char(-106),char(0), +char(7),char(0),char(-105),char(0),char(7),char(0),char(-104),char(0),char(7),char(0),char(-103),char(0),char(7),char(0),char(-102),char(0),char(4),char(0),char(-101),char(0), +char(4),char(0),char(-100),char(0),char(4),char(0),char(-99),char(0),char(4),char(0),char(-98),char(0),char(4),char(0),char(-97),char(0),char(4),char(0),char(-96),char(0), +char(4),char(0),char(-95),char(0),char(4),char(0),char(-94),char(0),char(4),char(0),char(-93),char(0),char(4),char(0),char(-92),char(0),char(53),char(0),char(22),char(0), +char(8),char(0),char(-91),char(0),char(8),char(0),char(-90),char(0),char(8),char(0),char(-109),char(0),char(8),char(0),char(-89),char(0),char(8),char(0),char(-105),char(0), +char(8),char(0),char(-88),char(0),char(8),char(0),char(-87),char(0),char(8),char(0),char(-86),char(0),char(8),char(0),char(-85),char(0),char(8),char(0),char(-84),char(0), +char(8),char(0),char(-83),char(0),char(8),char(0),char(-82),char(0),char(8),char(0),char(-81),char(0),char(8),char(0),char(-80),char(0),char(8),char(0),char(-79),char(0), +char(8),char(0),char(-78),char(0),char(4),char(0),char(-77),char(0),char(4),char(0),char(-76),char(0),char(4),char(0),char(-75),char(0),char(4),char(0),char(-74),char(0), +char(4),char(0),char(-73),char(0),char(0),char(0),char(37),char(0),char(54),char(0),char(22),char(0),char(7),char(0),char(-91),char(0),char(7),char(0),char(-90),char(0), +char(7),char(0),char(-109),char(0),char(7),char(0),char(-89),char(0),char(7),char(0),char(-105),char(0),char(7),char(0),char(-88),char(0),char(7),char(0),char(-87),char(0), +char(7),char(0),char(-86),char(0),char(7),char(0),char(-85),char(0),char(7),char(0),char(-84),char(0),char(7),char(0),char(-83),char(0),char(7),char(0),char(-82),char(0), +char(7),char(0),char(-81),char(0),char(7),char(0),char(-80),char(0),char(7),char(0),char(-79),char(0),char(7),char(0),char(-78),char(0),char(4),char(0),char(-77),char(0), +char(4),char(0),char(-76),char(0),char(4),char(0),char(-75),char(0),char(4),char(0),char(-74),char(0),char(4),char(0),char(-73),char(0),char(0),char(0),char(37),char(0), +char(55),char(0),char(2),char(0),char(53),char(0),char(-72),char(0),char(14),char(0),char(-71),char(0),char(56),char(0),char(2),char(0),char(54),char(0),char(-72),char(0), +char(13),char(0),char(-71),char(0),char(57),char(0),char(21),char(0),char(50),char(0),char(-70),char(0),char(17),char(0),char(-69),char(0),char(13),char(0),char(-68),char(0), +char(13),char(0),char(-67),char(0),char(13),char(0),char(-66),char(0),char(13),char(0),char(-65),char(0),char(13),char(0),char(-71),char(0),char(13),char(0),char(-64),char(0), +char(13),char(0),char(-63),char(0),char(13),char(0),char(-62),char(0),char(13),char(0),char(-61),char(0),char(7),char(0),char(-60),char(0),char(7),char(0),char(-59),char(0), +char(7),char(0),char(-58),char(0),char(7),char(0),char(-57),char(0),char(7),char(0),char(-56),char(0),char(7),char(0),char(-55),char(0),char(7),char(0),char(-54),char(0), +char(7),char(0),char(-53),char(0),char(7),char(0),char(-52),char(0),char(4),char(0),char(-51),char(0),char(58),char(0),char(22),char(0),char(48),char(0),char(-70),char(0), +char(18),char(0),char(-69),char(0),char(14),char(0),char(-68),char(0),char(14),char(0),char(-67),char(0),char(14),char(0),char(-66),char(0),char(14),char(0),char(-65),char(0), +char(14),char(0),char(-71),char(0),char(14),char(0),char(-64),char(0),char(14),char(0),char(-63),char(0),char(14),char(0),char(-62),char(0),char(14),char(0),char(-61),char(0), +char(8),char(0),char(-60),char(0),char(8),char(0),char(-59),char(0),char(8),char(0),char(-58),char(0),char(8),char(0),char(-57),char(0),char(8),char(0),char(-56),char(0), +char(8),char(0),char(-55),char(0),char(8),char(0),char(-54),char(0),char(8),char(0),char(-53),char(0),char(8),char(0),char(-52),char(0),char(4),char(0),char(-51),char(0), +char(0),char(0),char(37),char(0),char(59),char(0),char(2),char(0),char(4),char(0),char(-50),char(0),char(4),char(0),char(-49),char(0),char(60),char(0),char(13),char(0), +char(57),char(0),char(-48),char(0),char(57),char(0),char(-47),char(0),char(0),char(0),char(35),char(0),char(4),char(0),char(-128),char(0),char(4),char(0),char(-46),char(0), +char(4),char(0),char(-45),char(0),char(4),char(0),char(-44),char(0),char(7),char(0),char(-43),char(0),char(7),char(0),char(-42),char(0),char(4),char(0),char(-41),char(0), +char(4),char(0),char(-40),char(0),char(7),char(0),char(-39),char(0),char(4),char(0),char(-38),char(0),char(61),char(0),char(13),char(0),char(62),char(0),char(-48),char(0), +char(62),char(0),char(-47),char(0),char(0),char(0),char(35),char(0),char(4),char(0),char(-128),char(0),char(4),char(0),char(-46),char(0),char(4),char(0),char(-45),char(0), +char(4),char(0),char(-44),char(0),char(7),char(0),char(-43),char(0),char(7),char(0),char(-42),char(0),char(4),char(0),char(-41),char(0),char(4),char(0),char(-40),char(0), +char(7),char(0),char(-39),char(0),char(4),char(0),char(-38),char(0),char(63),char(0),char(14),char(0),char(58),char(0),char(-48),char(0),char(58),char(0),char(-47),char(0), +char(0),char(0),char(35),char(0),char(4),char(0),char(-128),char(0),char(4),char(0),char(-46),char(0),char(4),char(0),char(-45),char(0),char(4),char(0),char(-44),char(0), +char(8),char(0),char(-43),char(0),char(8),char(0),char(-42),char(0),char(4),char(0),char(-41),char(0),char(4),char(0),char(-40),char(0),char(8),char(0),char(-39),char(0), +char(4),char(0),char(-38),char(0),char(0),char(0),char(-37),char(0),char(64),char(0),char(3),char(0),char(61),char(0),char(-36),char(0),char(13),char(0),char(-35),char(0), +char(13),char(0),char(-34),char(0),char(65),char(0),char(3),char(0),char(63),char(0),char(-36),char(0),char(14),char(0),char(-35),char(0),char(14),char(0),char(-34),char(0), +char(66),char(0),char(3),char(0),char(61),char(0),char(-36),char(0),char(14),char(0),char(-35),char(0),char(14),char(0),char(-34),char(0),char(67),char(0),char(13),char(0), +char(61),char(0),char(-36),char(0),char(20),char(0),char(-33),char(0),char(20),char(0),char(-32),char(0),char(4),char(0),char(-31),char(0),char(4),char(0),char(-30),char(0), +char(4),char(0),char(-29),char(0),char(7),char(0),char(-28),char(0),char(7),char(0),char(-27),char(0),char(7),char(0),char(-26),char(0),char(7),char(0),char(-25),char(0), +char(7),char(0),char(-24),char(0),char(7),char(0),char(-23),char(0),char(7),char(0),char(-22),char(0),char(68),char(0),char(13),char(0),char(61),char(0),char(-36),char(0), +char(19),char(0),char(-33),char(0),char(19),char(0),char(-32),char(0),char(4),char(0),char(-31),char(0),char(4),char(0),char(-30),char(0),char(4),char(0),char(-29),char(0), +char(7),char(0),char(-28),char(0),char(7),char(0),char(-27),char(0),char(7),char(0),char(-26),char(0),char(7),char(0),char(-25),char(0),char(7),char(0),char(-24),char(0), +char(7),char(0),char(-23),char(0),char(7),char(0),char(-22),char(0),char(69),char(0),char(14),char(0),char(63),char(0),char(-36),char(0),char(20),char(0),char(-33),char(0), +char(20),char(0),char(-32),char(0),char(4),char(0),char(-31),char(0),char(4),char(0),char(-30),char(0),char(4),char(0),char(-29),char(0),char(8),char(0),char(-28),char(0), +char(8),char(0),char(-27),char(0),char(8),char(0),char(-26),char(0),char(8),char(0),char(-25),char(0),char(8),char(0),char(-24),char(0),char(8),char(0),char(-23),char(0), +char(8),char(0),char(-22),char(0),char(0),char(0),char(-21),char(0),char(70),char(0),char(10),char(0),char(63),char(0),char(-36),char(0),char(20),char(0),char(-33),char(0), +char(20),char(0),char(-32),char(0),char(8),char(0),char(-20),char(0),char(8),char(0),char(-19),char(0),char(8),char(0),char(-18),char(0),char(8),char(0),char(-24),char(0), +char(8),char(0),char(-23),char(0),char(8),char(0),char(-22),char(0),char(8),char(0),char(-90),char(0),char(71),char(0),char(11),char(0),char(61),char(0),char(-36),char(0), +char(19),char(0),char(-33),char(0),char(19),char(0),char(-32),char(0),char(7),char(0),char(-20),char(0),char(7),char(0),char(-19),char(0),char(7),char(0),char(-18),char(0), +char(7),char(0),char(-24),char(0),char(7),char(0),char(-23),char(0),char(7),char(0),char(-22),char(0),char(7),char(0),char(-90),char(0),char(0),char(0),char(21),char(0), +char(72),char(0),char(9),char(0),char(61),char(0),char(-36),char(0),char(19),char(0),char(-33),char(0),char(19),char(0),char(-32),char(0),char(13),char(0),char(-17),char(0), +char(13),char(0),char(-16),char(0),char(13),char(0),char(-15),char(0),char(13),char(0),char(-14),char(0),char(4),char(0),char(-13),char(0),char(4),char(0),char(-12),char(0), +char(73),char(0),char(9),char(0),char(63),char(0),char(-36),char(0),char(20),char(0),char(-33),char(0),char(20),char(0),char(-32),char(0),char(14),char(0),char(-17),char(0), +char(14),char(0),char(-16),char(0),char(14),char(0),char(-15),char(0),char(14),char(0),char(-14),char(0),char(4),char(0),char(-13),char(0),char(4),char(0),char(-12),char(0), +char(74),char(0),char(5),char(0),char(72),char(0),char(-11),char(0),char(4),char(0),char(-10),char(0),char(7),char(0),char(-9),char(0),char(7),char(0),char(-8),char(0), +char(7),char(0),char(-7),char(0),char(75),char(0),char(5),char(0),char(73),char(0),char(-11),char(0),char(4),char(0),char(-10),char(0),char(8),char(0),char(-9),char(0), +char(8),char(0),char(-8),char(0),char(8),char(0),char(-7),char(0),char(76),char(0),char(41),char(0),char(61),char(0),char(-36),char(0),char(19),char(0),char(-33),char(0), +char(19),char(0),char(-32),char(0),char(13),char(0),char(-17),char(0),char(13),char(0),char(-16),char(0),char(13),char(0),char(-6),char(0),char(13),char(0),char(-5),char(0), +char(13),char(0),char(-4),char(0),char(13),char(0),char(-3),char(0),char(13),char(0),char(-2),char(0),char(13),char(0),char(-1),char(0),char(13),char(0),char(0),char(1), +char(13),char(0),char(1),char(1),char(13),char(0),char(2),char(1),char(13),char(0),char(3),char(1),char(13),char(0),char(4),char(1),char(0),char(0),char(5),char(1), +char(0),char(0),char(6),char(1),char(0),char(0),char(7),char(1),char(0),char(0),char(8),char(1),char(0),char(0),char(9),char(1),char(0),char(0),char(-21),char(0), +char(13),char(0),char(-15),char(0),char(13),char(0),char(-14),char(0),char(13),char(0),char(10),char(1),char(13),char(0),char(11),char(1),char(13),char(0),char(12),char(1), +char(13),char(0),char(13),char(1),char(13),char(0),char(14),char(1),char(13),char(0),char(15),char(1),char(13),char(0),char(16),char(1),char(13),char(0),char(17),char(1), +char(13),char(0),char(18),char(1),char(13),char(0),char(19),char(1),char(13),char(0),char(20),char(1),char(0),char(0),char(21),char(1),char(0),char(0),char(22),char(1), +char(0),char(0),char(23),char(1),char(0),char(0),char(24),char(1),char(0),char(0),char(25),char(1),char(4),char(0),char(26),char(1),char(77),char(0),char(41),char(0), +char(63),char(0),char(-36),char(0),char(20),char(0),char(-33),char(0),char(20),char(0),char(-32),char(0),char(14),char(0),char(-17),char(0),char(14),char(0),char(-16),char(0), +char(14),char(0),char(-6),char(0),char(14),char(0),char(-5),char(0),char(14),char(0),char(-4),char(0),char(14),char(0),char(-3),char(0),char(14),char(0),char(-2),char(0), +char(14),char(0),char(-1),char(0),char(14),char(0),char(0),char(1),char(14),char(0),char(1),char(1),char(14),char(0),char(2),char(1),char(14),char(0),char(3),char(1), +char(14),char(0),char(4),char(1),char(0),char(0),char(5),char(1),char(0),char(0),char(6),char(1),char(0),char(0),char(7),char(1),char(0),char(0),char(8),char(1), +char(0),char(0),char(9),char(1),char(0),char(0),char(-21),char(0),char(14),char(0),char(-15),char(0),char(14),char(0),char(-14),char(0),char(14),char(0),char(10),char(1), +char(14),char(0),char(11),char(1),char(14),char(0),char(12),char(1),char(14),char(0),char(13),char(1),char(14),char(0),char(14),char(1),char(14),char(0),char(15),char(1), +char(14),char(0),char(16),char(1),char(14),char(0),char(17),char(1),char(14),char(0),char(18),char(1),char(14),char(0),char(19),char(1),char(14),char(0),char(20),char(1), +char(0),char(0),char(21),char(1),char(0),char(0),char(22),char(1),char(0),char(0),char(23),char(1),char(0),char(0),char(24),char(1),char(0),char(0),char(25),char(1), +char(4),char(0),char(26),char(1),char(78),char(0),char(9),char(0),char(61),char(0),char(-36),char(0),char(19),char(0),char(-33),char(0),char(19),char(0),char(-32),char(0), +char(7),char(0),char(-17),char(0),char(7),char(0),char(-16),char(0),char(7),char(0),char(-15),char(0),char(7),char(0),char(-14),char(0),char(4),char(0),char(-13),char(0), +char(4),char(0),char(-12),char(0),char(79),char(0),char(9),char(0),char(63),char(0),char(-36),char(0),char(20),char(0),char(-33),char(0),char(20),char(0),char(-32),char(0), +char(8),char(0),char(-17),char(0),char(8),char(0),char(-16),char(0),char(8),char(0),char(-15),char(0),char(8),char(0),char(-14),char(0),char(4),char(0),char(-13),char(0), +char(4),char(0),char(-12),char(0),char(80),char(0),char(5),char(0),char(60),char(0),char(-36),char(0),char(13),char(0),char(27),char(1),char(13),char(0),char(28),char(1), +char(7),char(0),char(29),char(1),char(0),char(0),char(37),char(0),char(81),char(0),char(4),char(0),char(63),char(0),char(-36),char(0),char(14),char(0),char(27),char(1), +char(14),char(0),char(28),char(1),char(8),char(0),char(29),char(1),char(82),char(0),char(4),char(0),char(7),char(0),char(30),char(1),char(7),char(0),char(31),char(1), +char(7),char(0),char(32),char(1),char(4),char(0),char(79),char(0),char(83),char(0),char(10),char(0),char(82),char(0),char(33),char(1),char(13),char(0),char(34),char(1), +char(13),char(0),char(35),char(1),char(13),char(0),char(36),char(1),char(13),char(0),char(37),char(1),char(13),char(0),char(38),char(1),char(7),char(0),char(-60),char(0), +char(7),char(0),char(39),char(1),char(4),char(0),char(40),char(1),char(4),char(0),char(53),char(0),char(84),char(0),char(4),char(0),char(82),char(0),char(33),char(1), +char(4),char(0),char(41),char(1),char(7),char(0),char(42),char(1),char(4),char(0),char(43),char(1),char(85),char(0),char(4),char(0),char(13),char(0),char(38),char(1), +char(82),char(0),char(33),char(1),char(4),char(0),char(44),char(1),char(7),char(0),char(45),char(1),char(86),char(0),char(7),char(0),char(13),char(0),char(46),char(1), +char(82),char(0),char(33),char(1),char(4),char(0),char(47),char(1),char(7),char(0),char(48),char(1),char(7),char(0),char(49),char(1),char(7),char(0),char(50),char(1), +char(4),char(0),char(53),char(0),char(87),char(0),char(6),char(0),char(17),char(0),char(51),char(1),char(13),char(0),char(49),char(1),char(13),char(0),char(52),char(1), +char(62),char(0),char(53),char(1),char(4),char(0),char(54),char(1),char(7),char(0),char(50),char(1),char(88),char(0),char(26),char(0),char(4),char(0),char(55),char(1), +char(7),char(0),char(56),char(1),char(7),char(0),char(-90),char(0),char(7),char(0),char(57),char(1),char(7),char(0),char(58),char(1),char(7),char(0),char(59),char(1), +char(7),char(0),char(60),char(1),char(7),char(0),char(61),char(1),char(7),char(0),char(62),char(1),char(7),char(0),char(63),char(1),char(7),char(0),char(64),char(1), +char(7),char(0),char(65),char(1),char(7),char(0),char(66),char(1),char(7),char(0),char(67),char(1),char(7),char(0),char(68),char(1),char(7),char(0),char(69),char(1), +char(7),char(0),char(70),char(1),char(7),char(0),char(71),char(1),char(7),char(0),char(72),char(1),char(7),char(0),char(73),char(1),char(7),char(0),char(74),char(1), +char(4),char(0),char(75),char(1),char(4),char(0),char(76),char(1),char(4),char(0),char(77),char(1),char(4),char(0),char(78),char(1),char(4),char(0),char(-100),char(0), +char(89),char(0),char(12),char(0),char(17),char(0),char(79),char(1),char(17),char(0),char(80),char(1),char(17),char(0),char(81),char(1),char(13),char(0),char(82),char(1), +char(13),char(0),char(83),char(1),char(7),char(0),char(84),char(1),char(4),char(0),char(85),char(1),char(4),char(0),char(86),char(1),char(4),char(0),char(87),char(1), +char(4),char(0),char(88),char(1),char(7),char(0),char(48),char(1),char(4),char(0),char(53),char(0),char(90),char(0),char(27),char(0),char(19),char(0),char(89),char(1), +char(17),char(0),char(90),char(1),char(17),char(0),char(91),char(1),char(13),char(0),char(82),char(1),char(13),char(0),char(92),char(1),char(13),char(0),char(93),char(1), +char(13),char(0),char(94),char(1),char(13),char(0),char(95),char(1),char(13),char(0),char(96),char(1),char(4),char(0),char(97),char(1),char(7),char(0),char(98),char(1), +char(4),char(0),char(99),char(1),char(4),char(0),char(100),char(1),char(4),char(0),char(101),char(1),char(7),char(0),char(102),char(1),char(7),char(0),char(103),char(1), +char(4),char(0),char(104),char(1),char(4),char(0),char(105),char(1),char(7),char(0),char(106),char(1),char(7),char(0),char(107),char(1),char(7),char(0),char(108),char(1), +char(7),char(0),char(109),char(1),char(7),char(0),char(110),char(1),char(7),char(0),char(111),char(1),char(4),char(0),char(112),char(1),char(4),char(0),char(113),char(1), +char(4),char(0),char(114),char(1),char(91),char(0),char(12),char(0),char(9),char(0),char(115),char(1),char(9),char(0),char(116),char(1),char(13),char(0),char(117),char(1), +char(7),char(0),char(118),char(1),char(7),char(0),char(-86),char(0),char(7),char(0),char(119),char(1),char(4),char(0),char(120),char(1),char(13),char(0),char(121),char(1), +char(4),char(0),char(122),char(1),char(4),char(0),char(123),char(1),char(4),char(0),char(124),char(1),char(4),char(0),char(53),char(0),char(92),char(0),char(19),char(0), +char(50),char(0),char(-70),char(0),char(89),char(0),char(125),char(1),char(82),char(0),char(126),char(1),char(83),char(0),char(127),char(1),char(84),char(0),char(-128),char(1), +char(85),char(0),char(-127),char(1),char(86),char(0),char(-126),char(1),char(87),char(0),char(-125),char(1),char(90),char(0),char(-124),char(1),char(91),char(0),char(-123),char(1), +char(4),char(0),char(-122),char(1),char(4),char(0),char(100),char(1),char(4),char(0),char(-121),char(1),char(4),char(0),char(-120),char(1),char(4),char(0),char(-119),char(1), +char(4),char(0),char(-118),char(1),char(4),char(0),char(-117),char(1),char(4),char(0),char(-116),char(1),char(88),char(0),char(-115),char(1),char(93),char(0),char(28),char(0), +char(16),char(0),char(-114),char(1),char(14),char(0),char(-113),char(1),char(14),char(0),char(-112),char(1),char(14),char(0),char(-111),char(1),char(14),char(0),char(-110),char(1), +char(14),char(0),char(-109),char(1),char(14),char(0),char(-108),char(1),char(14),char(0),char(-107),char(1),char(14),char(0),char(-106),char(1),char(14),char(0),char(-105),char(1), +char(8),char(0),char(-104),char(1),char(4),char(0),char(-103),char(1),char(4),char(0),char(124),char(1),char(4),char(0),char(-102),char(1),char(4),char(0),char(-101),char(1), +char(8),char(0),char(-100),char(1),char(8),char(0),char(-99),char(1),char(8),char(0),char(-98),char(1),char(8),char(0),char(-97),char(1),char(8),char(0),char(-96),char(1), +char(8),char(0),char(-95),char(1),char(8),char(0),char(-94),char(1),char(8),char(0),char(-93),char(1),char(8),char(0),char(-92),char(1),char(0),char(0),char(-91),char(1), +char(0),char(0),char(-90),char(1),char(48),char(0),char(-89),char(1),char(0),char(0),char(-88),char(1),char(94),char(0),char(28),char(0),char(15),char(0),char(-114),char(1), +char(13),char(0),char(-113),char(1),char(13),char(0),char(-112),char(1),char(13),char(0),char(-111),char(1),char(13),char(0),char(-110),char(1),char(13),char(0),char(-109),char(1), +char(13),char(0),char(-108),char(1),char(13),char(0),char(-107),char(1),char(13),char(0),char(-106),char(1),char(13),char(0),char(-105),char(1),char(4),char(0),char(-102),char(1), +char(7),char(0),char(-104),char(1),char(4),char(0),char(-103),char(1),char(4),char(0),char(124),char(1),char(7),char(0),char(-100),char(1),char(7),char(0),char(-99),char(1), +char(7),char(0),char(-98),char(1),char(4),char(0),char(-101),char(1),char(7),char(0),char(-97),char(1),char(7),char(0),char(-96),char(1),char(7),char(0),char(-95),char(1), +char(7),char(0),char(-94),char(1),char(7),char(0),char(-93),char(1),char(7),char(0),char(-92),char(1),char(0),char(0),char(-91),char(1),char(0),char(0),char(-90),char(1), +char(50),char(0),char(-89),char(1),char(0),char(0),char(-88),char(1),char(95),char(0),char(11),char(0),char(14),char(0),char(-87),char(1),char(16),char(0),char(-86),char(1), +char(14),char(0),char(-85),char(1),char(14),char(0),char(-84),char(1),char(14),char(0),char(-83),char(1),char(8),char(0),char(-82),char(1),char(4),char(0),char(-121),char(1), +char(0),char(0),char(37),char(0),char(0),char(0),char(-81),char(1),char(93),char(0),char(-128),char(1),char(48),char(0),char(-80),char(1),char(96),char(0),char(10),char(0), +char(13),char(0),char(-87),char(1),char(15),char(0),char(-86),char(1),char(13),char(0),char(-85),char(1),char(13),char(0),char(-84),char(1),char(13),char(0),char(-83),char(1), +char(7),char(0),char(-82),char(1),char(4),char(0),char(-121),char(1),char(0),char(0),char(-81),char(1),char(94),char(0),char(-128),char(1),char(50),char(0),char(-80),char(1), +char(97),char(0),char(4),char(0),char(50),char(0),char(-79),char(1),char(96),char(0),char(-78),char(1),char(4),char(0),char(-77),char(1),char(0),char(0),char(37),char(0), +char(98),char(0),char(4),char(0),char(48),char(0),char(-79),char(1),char(95),char(0),char(-78),char(1),char(4),char(0),char(-77),char(1),char(0),char(0),char(37),char(0), +}; int sBulletDNAlen= sizeof(sBulletDNAstr); diff --git a/thirdparty/bullet/LinearMath/btSerializer.h b/thirdparty/bullet/LinearMath/btSerializer.h index 89b4d74683..39be3f810e 100644 --- a/thirdparty/bullet/LinearMath/btSerializer.h +++ b/thirdparty/bullet/LinearMath/btSerializer.h @@ -62,7 +62,8 @@ enum btSerializationFlags { BT_SERIALIZE_NO_BVH = 1, BT_SERIALIZE_NO_TRIANGLEINFOMAP = 2, - BT_SERIALIZE_NO_DUPLICATE_ASSERT = 4 + BT_SERIALIZE_NO_DUPLICATE_ASSERT = 4, + BT_SERIALIZE_CONTACT_MANIFOLDS = 8, }; class btSerializer @@ -115,6 +116,7 @@ public: #define BT_MULTIBODY_CODE BT_MAKE_ID('M','B','D','Y') +#define BT_MB_LINKCOLLIDER_CODE BT_MAKE_ID('M','B','L','C') #define BT_SOFTBODY_CODE BT_MAKE_ID('S','B','D','Y') #define BT_COLLISIONOBJECT_CODE BT_MAKE_ID('C','O','B','J') #define BT_RIGIDBODY_CODE BT_MAKE_ID('R','B','D','Y') @@ -127,9 +129,9 @@ public: #define BT_SBMATERIAL_CODE BT_MAKE_ID('S','B','M','T') #define BT_SBNODE_CODE BT_MAKE_ID('S','B','N','D') #define BT_DYNAMICSWORLD_CODE BT_MAKE_ID('D','W','L','D') +#define BT_CONTACTMANIFOLD_CODE BT_MAKE_ID('C','O','N','T') #define BT_DNA_CODE BT_MAKE_ID('D','N','A','1') - struct btPointerUid { union @@ -505,7 +507,7 @@ public: buffer[9] = '2'; buffer[10] = '8'; - buffer[11] = '7'; + buffer[11] = '8'; } diff --git a/thirdparty/bullet/LinearMath/btSerializer64.cpp b/thirdparty/bullet/LinearMath/btSerializer64.cpp index 05f59202d7..0aa5cbf30e 100644 --- a/thirdparty/bullet/LinearMath/btSerializer64.cpp +++ b/thirdparty/bullet/LinearMath/btSerializer64.cpp @@ -1,5 +1,5 @@ char sBulletDNAstr64[]= { -char(83),char(68),char(78),char(65),char(78),char(65),char(77),char(69),char(-124),char(1),char(0),char(0),char(109),char(95),char(115),char(105),char(122),char(101),char(0),char(109), +char(83),char(68),char(78),char(65),char(78),char(65),char(77),char(69),char(-76),char(1),char(0),char(0),char(109),char(95),char(115),char(105),char(122),char(101),char(0),char(109), char(95),char(99),char(97),char(112),char(97),char(99),char(105),char(116),char(121),char(0),char(42),char(109),char(95),char(100),char(97),char(116),char(97),char(0),char(109),char(95), char(99),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(83),char(104),char(97),char(112),char(101),char(115),char(0),char(109),char(95),char(99),char(111), char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(79),char(98),char(106),char(101),char(99),char(116),char(115),char(0),char(109),char(95),char(99),char(111),char(110), @@ -72,528 +72,618 @@ char(105),char(115),char(116),char(97),char(110),char(99),char(101),char(84),cha char(101),char(114),char(111),char(65),char(114),char(101),char(97),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(110), char(101),char(120),char(116),char(83),char(105),char(122),char(101),char(0),char(109),char(95),char(104),char(97),char(115),char(104),char(84),char(97),char(98),char(108),char(101),char(83), char(105),char(122),char(101),char(0),char(109),char(95),char(110),char(117),char(109),char(86),char(97),char(108),char(117),char(101),char(115),char(0),char(109),char(95),char(110),char(117), -char(109),char(75),char(101),char(121),char(115),char(0),char(109),char(95),char(103),char(105),char(109),char(112),char(97),char(99),char(116),char(83),char(117),char(98),char(84),char(121), -char(112),char(101),char(0),char(42),char(109),char(95),char(117),char(110),char(115),char(99),char(97),char(108),char(101),char(100),char(80),char(111),char(105),char(110),char(116),char(115), -char(70),char(108),char(111),char(97),char(116),char(80),char(116),char(114),char(0),char(42),char(109),char(95),char(117),char(110),char(115),char(99),char(97),char(108),char(101),char(100), -char(80),char(111),char(105),char(110),char(116),char(115),char(68),char(111),char(117),char(98),char(108),char(101),char(80),char(116),char(114),char(0),char(109),char(95),char(110),char(117), -char(109),char(85),char(110),char(115),char(99),char(97),char(108),char(101),char(100),char(80),char(111),char(105),char(110),char(116),char(115),char(0),char(109),char(95),char(112),char(97), -char(100),char(100),char(105),char(110),char(103),char(51),char(91),char(52),char(93),char(0),char(42),char(109),char(95),char(98),char(114),char(111),char(97),char(100),char(112),char(104), -char(97),char(115),char(101),char(72),char(97),char(110),char(100),char(108),char(101),char(0),char(42),char(109),char(95),char(99),char(111),char(108),char(108),char(105),char(115),char(105), -char(111),char(110),char(83),char(104),char(97),char(112),char(101),char(0),char(42),char(109),char(95),char(114),char(111),char(111),char(116),char(67),char(111),char(108),char(108),char(105), -char(115),char(105),char(111),char(110),char(83),char(104),char(97),char(112),char(101),char(0),char(109),char(95),char(119),char(111),char(114),char(108),char(100),char(84),char(114),char(97), -char(110),char(115),char(102),char(111),char(114),char(109),char(0),char(109),char(95),char(105),char(110),char(116),char(101),char(114),char(112),char(111),char(108),char(97),char(116),char(105), -char(111),char(110),char(87),char(111),char(114),char(108),char(100),char(84),char(114),char(97),char(110),char(115),char(102),char(111),char(114),char(109),char(0),char(109),char(95),char(105), -char(110),char(116),char(101),char(114),char(112),char(111),char(108),char(97),char(116),char(105),char(111),char(110),char(76),char(105),char(110),char(101),char(97),char(114),char(86),char(101), -char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(105),char(110),char(116),char(101),char(114),char(112),char(111),char(108),char(97),char(116),char(105), -char(111),char(110),char(65),char(110),char(103),char(117),char(108),char(97),char(114),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95), -char(97),char(110),char(105),char(115),char(111),char(116),char(114),char(111),char(112),char(105),char(99),char(70),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(0), -char(109),char(95),char(99),char(111),char(110),char(116),char(97),char(99),char(116),char(80),char(114),char(111),char(99),char(101),char(115),char(115),char(105),char(110),char(103),char(84), -char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(100),char(101),char(97),char(99),char(116),char(105),char(118),char(97),char(116), -char(105),char(111),char(110),char(84),char(105),char(109),char(101),char(0),char(109),char(95),char(102),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(0),char(109), -char(95),char(114),char(111),char(108),char(108),char(105),char(110),char(103),char(70),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(99), -char(111),char(110),char(116),char(97),char(99),char(116),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(99),char(111),char(110),char(116), -char(97),char(99),char(116),char(83),char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(114),char(101),char(115),char(116),char(105), -char(116),char(117),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(104),char(105),char(116),char(70),char(114),char(97),char(99),char(116),char(105),char(111),char(110), -char(0),char(109),char(95),char(99),char(99),char(100),char(83),char(119),char(101),char(112),char(116),char(83),char(112),char(104),char(101),char(114),char(101),char(82),char(97),char(100), -char(105),char(117),char(115),char(0),char(109),char(95),char(99),char(99),char(100),char(77),char(111),char(116),char(105),char(111),char(110),char(84),char(104),char(114),char(101),char(115), -char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(104),char(97),char(115),char(65),char(110),char(105),char(115),char(111),char(116),char(114),char(111),char(112),char(105), -char(99),char(70),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(99),char(111),char(108),char(108),char(105),char(115),char(105),char(111), -char(110),char(70),char(108),char(97),char(103),char(115),char(0),char(109),char(95),char(105),char(115),char(108),char(97),char(110),char(100),char(84),char(97),char(103),char(49),char(0), -char(109),char(95),char(99),char(111),char(109),char(112),char(97),char(110),char(105),char(111),char(110),char(73),char(100),char(0),char(109),char(95),char(97),char(99),char(116),char(105), -char(118),char(97),char(116),char(105),char(111),char(110),char(83),char(116),char(97),char(116),char(101),char(49),char(0),char(109),char(95),char(105),char(110),char(116),char(101),char(114), -char(110),char(97),char(108),char(84),char(121),char(112),char(101),char(0),char(109),char(95),char(99),char(104),char(101),char(99),char(107),char(67),char(111),char(108),char(108),char(105), -char(100),char(101),char(87),char(105),char(116),char(104),char(0),char(109),char(95),char(116),char(97),char(117),char(0),char(109),char(95),char(100),char(97),char(109),char(112),char(105), -char(110),char(103),char(0),char(109),char(95),char(116),char(105),char(109),char(101),char(83),char(116),char(101),char(112),char(0),char(109),char(95),char(109),char(97),char(120),char(69), -char(114),char(114),char(111),char(114),char(82),char(101),char(100),char(117),char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(115),char(111),char(114),char(0), -char(109),char(95),char(101),char(114),char(112),char(0),char(109),char(95),char(101),char(114),char(112),char(50),char(0),char(109),char(95),char(103),char(108),char(111),char(98),char(97), -char(108),char(67),char(102),char(109),char(0),char(109),char(95),char(115),char(112),char(108),char(105),char(116),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(80), -char(101),char(110),char(101),char(116),char(114),char(97),char(116),char(105),char(111),char(110),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0), -char(109),char(95),char(115),char(112),char(108),char(105),char(116),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(84),char(117),char(114),char(110),char(69),char(114), -char(112),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(108),char(111),char(112),char(0),char(109),char(95),char(119),char(97),char(114), -char(109),char(115),char(116),char(97),char(114),char(116),char(105),char(110),char(103),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(109),char(97), -char(120),char(71),char(121),char(114),char(111),char(115),char(99),char(111),char(112),char(105),char(99),char(70),char(111),char(114),char(99),char(101),char(0),char(109),char(95),char(115), -char(105),char(110),char(103),char(108),char(101),char(65),char(120),char(105),char(115),char(82),char(111),char(108),char(108),char(105),char(110),char(103),char(70),char(114),char(105),char(99), -char(116),char(105),char(111),char(110),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(110),char(117),char(109),char(73), -char(116),char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95),char(115),char(111),char(108),char(118),char(101),char(114),char(77),char(111), -char(100),char(101),char(0),char(109),char(95),char(114),char(101),char(115),char(116),char(105),char(110),char(103),char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(82), -char(101),char(115),char(116),char(105),char(116),char(117),char(116),char(105),char(111),char(110),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0), -char(109),char(95),char(109),char(105),char(110),char(105),char(109),char(117),char(109),char(83),char(111),char(108),char(118),char(101),char(114),char(66),char(97),char(116),char(99),char(104), -char(83),char(105),char(122),char(101),char(0),char(109),char(95),char(115),char(112),char(108),char(105),char(116),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(0), -char(109),char(95),char(115),char(111),char(108),char(118),char(101),char(114),char(73),char(110),char(102),char(111),char(0),char(109),char(95),char(103),char(114),char(97),char(118),char(105), -char(116),char(121),char(0),char(109),char(95),char(99),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(79),char(98),char(106),char(101),char(99),char(116), -char(68),char(97),char(116),char(97),char(0),char(109),char(95),char(105),char(110),char(118),char(73),char(110),char(101),char(114),char(116),char(105),char(97),char(84),char(101),char(110), -char(115),char(111),char(114),char(87),char(111),char(114),char(108),char(100),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(86),char(101),char(108), -char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(86),char(101),char(108),char(111),char(99), -char(105),char(116),char(121),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(70),char(97),char(99),char(116),char(111),char(114),char(0), -char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(103),char(114),char(97), -char(118),char(105),char(116),char(121),char(95),char(97),char(99),char(99),char(101),char(108),char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(0),char(109),char(95), -char(105),char(110),char(118),char(73),char(110),char(101),char(114),char(116),char(105),char(97),char(76),char(111),char(99),char(97),char(108),char(0),char(109),char(95),char(116),char(111), -char(116),char(97),char(108),char(70),char(111),char(114),char(99),char(101),char(0),char(109),char(95),char(116),char(111),char(116),char(97),char(108),char(84),char(111),char(114),char(113), -char(117),char(101),char(0),char(109),char(95),char(105),char(110),char(118),char(101),char(114),char(115),char(101),char(77),char(97),char(115),char(115),char(0),char(109),char(95),char(108), -char(105),char(110),char(101),char(97),char(114),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108), -char(97),char(114),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(97),char(100),char(100),char(105),char(116),char(105),char(111),char(110), -char(97),char(108),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(97),char(100), -char(100),char(105),char(116),char(105),char(111),char(110),char(97),char(108),char(76),char(105),char(110),char(101),char(97),char(114),char(68),char(97),char(109),char(112),char(105),char(110), -char(103),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(83),char(113),char(114),char(0),char(109),char(95),char(97),char(100),char(100),char(105), -char(116),char(105),char(111),char(110),char(97),char(108),char(65),char(110),char(103),char(117),char(108),char(97),char(114),char(68),char(97),char(109),char(112),char(105),char(110),char(103), -char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(83),char(113),char(114),char(0),char(109),char(95),char(97),char(100),char(100),char(105),char(116), -char(105),char(111),char(110),char(97),char(108),char(65),char(110),char(103),char(117),char(108),char(97),char(114),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(70), -char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(108),char(101),char(101),char(112),char(105), -char(110),char(103),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97), -char(114),char(83),char(108),char(101),char(101),char(112),char(105),char(110),char(103),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109), -char(95),char(97),char(100),char(100),char(105),char(116),char(105),char(111),char(110),char(97),char(108),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109), -char(95),char(110),char(117),char(109),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(82),char(111),char(119),char(115),char(0),char(110), -char(117),char(98),char(0),char(42),char(109),char(95),char(114),char(98),char(65),char(0),char(42),char(109),char(95),char(114),char(98),char(66),char(0),char(109),char(95),char(111), -char(98),char(106),char(101),char(99),char(116),char(84),char(121),char(112),char(101),char(0),char(109),char(95),char(117),char(115),char(101),char(114),char(67),char(111),char(110),char(115), -char(116),char(114),char(97),char(105),char(110),char(116),char(84),char(121),char(112),char(101),char(0),char(109),char(95),char(117),char(115),char(101),char(114),char(67),char(111),char(110), -char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(73),char(100),char(0),char(109),char(95),char(110),char(101),char(101),char(100),char(115),char(70),char(101),char(101), -char(100),char(98),char(97),char(99),char(107),char(0),char(109),char(95),char(97),char(112),char(112),char(108),char(105),char(101),char(100),char(73),char(109),char(112),char(117),char(108), -char(115),char(101),char(0),char(109),char(95),char(100),char(98),char(103),char(68),char(114),char(97),char(119),char(83),char(105),char(122),char(101),char(0),char(109),char(95),char(100), -char(105),char(115),char(97),char(98),char(108),char(101),char(67),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(115),char(66),char(101),char(116),char(119), -char(101),char(101),char(110),char(76),char(105),char(110),char(107),char(101),char(100),char(66),char(111),char(100),char(105),char(101),char(115),char(0),char(109),char(95),char(111),char(118), -char(101),char(114),char(114),char(105),char(100),char(101),char(78),char(117),char(109),char(83),char(111),char(108),char(118),char(101),char(114),char(73),char(116),char(101),char(114),char(97), -char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95),char(98),char(114),char(101),char(97),char(107),char(105),char(110),char(103),char(73),char(109),char(112),char(117), -char(108),char(115),char(101),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(105),char(115),char(69),char(110),char(97), -char(98),char(108),char(101),char(100),char(0),char(112),char(97),char(100),char(100),char(105),char(110),char(103),char(91),char(52),char(93),char(0),char(109),char(95),char(116),char(121), -char(112),char(101),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(109),char(95),char(112), -char(105),char(118),char(111),char(116),char(73),char(110),char(65),char(0),char(109),char(95),char(112),char(105),char(118),char(111),char(116),char(73),char(110),char(66),char(0),char(109), -char(95),char(114),char(98),char(65),char(70),char(114),char(97),char(109),char(101),char(0),char(109),char(95),char(114),char(98),char(66),char(70),char(114),char(97),char(109),char(101), -char(0),char(109),char(95),char(117),char(115),char(101),char(82),char(101),char(102),char(101),char(114),char(101),char(110),char(99),char(101),char(70),char(114),char(97),char(109),char(101), -char(65),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(79),char(110),char(108),char(121),char(0),char(109),char(95),char(101),char(110), -char(97),char(98),char(108),char(101),char(65),char(110),char(103),char(117),char(108),char(97),char(114),char(77),char(111),char(116),char(111),char(114),char(0),char(109),char(95),char(109), -char(111),char(116),char(111),char(114),char(84),char(97),char(114),char(103),char(101),char(116),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109), -char(95),char(109),char(97),char(120),char(77),char(111),char(116),char(111),char(114),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(0),char(109),char(95),char(108), -char(111),char(119),char(101),char(114),char(76),char(105),char(109),char(105),char(116),char(0),char(109),char(95),char(117),char(112),char(112),char(101),char(114),char(76),char(105),char(109), -char(105),char(116),char(0),char(109),char(95),char(108),char(105),char(109),char(105),char(116),char(83),char(111),char(102),char(116),char(110),char(101),char(115),char(115),char(0),char(109), -char(95),char(98),char(105),char(97),char(115),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(114),char(101),char(108),char(97),char(120),char(97), -char(116),char(105),char(111),char(110),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(112),char(97),char(100),char(100),char(105),char(110),char(103), -char(49),char(91),char(52),char(93),char(0),char(109),char(95),char(115),char(119),char(105),char(110),char(103),char(83),char(112),char(97),char(110),char(49),char(0),char(109),char(95), -char(115),char(119),char(105),char(110),char(103),char(83),char(112),char(97),char(110),char(50),char(0),char(109),char(95),char(116),char(119),char(105),char(115),char(116),char(83),char(112), -char(97),char(110),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(85),char(112),char(112),char(101),char(114),char(76),char(105),char(109),char(105), -char(116),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(76),char(111),char(119),char(101),char(114),char(76),char(105),char(109),char(105),char(116), -char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(85),char(112),char(112),char(101),char(114),char(76),char(105),char(109),char(105),char(116), -char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(76),char(111),char(119),char(101),char(114),char(76),char(105),char(109),char(105),char(116), -char(0),char(109),char(95),char(117),char(115),char(101),char(76),char(105),char(110),char(101),char(97),char(114),char(82),char(101),char(102),char(101),char(114),char(101),char(110),char(99), -char(101),char(70),char(114),char(97),char(109),char(101),char(65),char(0),char(109),char(95),char(117),char(115),char(101),char(79),char(102),char(102),char(115),char(101),char(116),char(70), -char(111),char(114),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(70),char(114),char(97),char(109),char(101),char(0),char(109),char(95), -char(54),char(100),char(111),char(102),char(68),char(97),char(116),char(97),char(0),char(109),char(95),char(115),char(112),char(114),char(105),char(110),char(103),char(69),char(110),char(97), -char(98),char(108),char(101),char(100),char(91),char(54),char(93),char(0),char(109),char(95),char(101),char(113),char(117),char(105),char(108),char(105),char(98),char(114),char(105),char(117), -char(109),char(80),char(111),char(105),char(110),char(116),char(91),char(54),char(93),char(0),char(109),char(95),char(115),char(112),char(114),char(105),char(110),char(103),char(83),char(116), -char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(91),char(54),char(93),char(0),char(109),char(95),char(115),char(112),char(114),char(105),char(110),char(103),char(68), -char(97),char(109),char(112),char(105),char(110),char(103),char(91),char(54),char(93),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(66),char(111), -char(117),char(110),char(99),char(101),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(116),char(111),char(112),char(69),char(82),char(80), -char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(116),char(111),char(112),char(67),char(70),char(77),char(0),char(109),char(95),char(108), -char(105),char(110),char(101),char(97),char(114),char(77),char(111),char(116),char(111),char(114),char(69),char(82),char(80),char(0),char(109),char(95),char(108),char(105),char(110),char(101), -char(97),char(114),char(77),char(111),char(116),char(111),char(114),char(67),char(70),char(77),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(84), -char(97),char(114),char(103),char(101),char(116),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(108),char(105),char(110),char(101), -char(97),char(114),char(77),char(97),char(120),char(77),char(111),char(116),char(111),char(114),char(70),char(111),char(114),char(99),char(101),char(0),char(109),char(95),char(108),char(105), -char(110),char(101),char(97),char(114),char(83),char(101),char(114),char(118),char(111),char(84),char(97),char(114),char(103),char(101),char(116),char(0),char(109),char(95),char(108),char(105), -char(110),char(101),char(97),char(114),char(83),char(112),char(114),char(105),char(110),char(103),char(83),char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(0), -char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(112),char(114),char(105),char(110),char(103),char(68),char(97),char(109),char(112),char(105),char(110), -char(103),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(69),char(113),char(117),char(105),char(108),char(105),char(98),char(114),char(105),char(117), -char(109),char(80),char(111),char(105),char(110),char(116),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(69),char(110),char(97),char(98),char(108), -char(101),char(77),char(111),char(116),char(111),char(114),char(91),char(52),char(93),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(101), -char(114),char(118),char(111),char(77),char(111),char(116),char(111),char(114),char(91),char(52),char(93),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114), -char(69),char(110),char(97),char(98),char(108),char(101),char(83),char(112),char(114),char(105),char(110),char(103),char(91),char(52),char(93),char(0),char(109),char(95),char(108),char(105), -char(110),char(101),char(97),char(114),char(83),char(112),char(114),char(105),char(110),char(103),char(83),char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(76), -char(105),char(109),char(105),char(116),char(101),char(100),char(91),char(52),char(93),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(112), -char(114),char(105),char(110),char(103),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(76),char(105),char(109),char(105),char(116),char(101),char(100),char(91),char(52), -char(93),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(66),char(111),char(117),char(110),char(99),char(101),char(0),char(109),char(95), -char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(83),char(116),char(111),char(112),char(69),char(82),char(80),char(0),char(109),char(95),char(97),char(110),char(103), -char(117),char(108),char(97),char(114),char(83),char(116),char(111),char(112),char(67),char(70),char(77),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97), -char(114),char(77),char(111),char(116),char(111),char(114),char(69),char(82),char(80),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(77), -char(111),char(116),char(111),char(114),char(67),char(70),char(77),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(84),char(97),char(114), -char(103),char(101),char(116),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97), -char(114),char(77),char(97),char(120),char(77),char(111),char(116),char(111),char(114),char(70),char(111),char(114),char(99),char(101),char(0),char(109),char(95),char(97),char(110),char(103), -char(117),char(108),char(97),char(114),char(83),char(101),char(114),char(118),char(111),char(84),char(97),char(114),char(103),char(101),char(116),char(0),char(109),char(95),char(97),char(110), -char(103),char(117),char(108),char(97),char(114),char(83),char(112),char(114),char(105),char(110),char(103),char(83),char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115), -char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(83),char(112),char(114),char(105),char(110),char(103),char(68),char(97),char(109),char(112), -char(105),char(110),char(103),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(69),char(113),char(117),char(105),char(108),char(105),char(98), -char(114),char(105),char(117),char(109),char(80),char(111),char(105),char(110),char(116),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(69), -char(110),char(97),char(98),char(108),char(101),char(77),char(111),char(116),char(111),char(114),char(91),char(52),char(93),char(0),char(109),char(95),char(97),char(110),char(103),char(117), -char(108),char(97),char(114),char(83),char(101),char(114),char(118),char(111),char(77),char(111),char(116),char(111),char(114),char(91),char(52),char(93),char(0),char(109),char(95),char(97), -char(110),char(103),char(117),char(108),char(97),char(114),char(69),char(110),char(97),char(98),char(108),char(101),char(83),char(112),char(114),char(105),char(110),char(103),char(91),char(52), -char(93),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(83),char(112),char(114),char(105),char(110),char(103),char(83),char(116),char(105), -char(102),char(102),char(110),char(101),char(115),char(115),char(76),char(105),char(109),char(105),char(116),char(101),char(100),char(91),char(52),char(93),char(0),char(109),char(95),char(97), -char(110),char(103),char(117),char(108),char(97),char(114),char(83),char(112),char(114),char(105),char(110),char(103),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(76), -char(105),char(109),char(105),char(116),char(101),char(100),char(91),char(52),char(93),char(0),char(109),char(95),char(114),char(111),char(116),char(97),char(116),char(101),char(79),char(114), -char(100),char(101),char(114),char(0),char(109),char(95),char(97),char(120),char(105),char(115),char(73),char(110),char(65),char(0),char(109),char(95),char(97),char(120),char(105),char(115), -char(73),char(110),char(66),char(0),char(109),char(95),char(114),char(97),char(116),char(105),char(111),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114), -char(83),char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(83), -char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(118),char(111),char(108),char(117),char(109),char(101),char(83),char(116),char(105), -char(102),char(102),char(110),char(101),char(115),char(115),char(0),char(42),char(109),char(95),char(109),char(97),char(116),char(101),char(114),char(105),char(97),char(108),char(0),char(109), -char(95),char(112),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(112),char(114),char(101),char(118),char(105),char(111),char(117),char(115), -char(80),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(118),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0), -char(109),char(95),char(97),char(99),char(99),char(117),char(109),char(117),char(108),char(97),char(116),char(101),char(100),char(70),char(111),char(114),char(99),char(101),char(0),char(109), -char(95),char(110),char(111),char(114),char(109),char(97),char(108),char(0),char(109),char(95),char(97),char(114),char(101),char(97),char(0),char(109),char(95),char(97),char(116),char(116), -char(97),char(99),char(104),char(0),char(109),char(95),char(110),char(111),char(100),char(101),char(73),char(110),char(100),char(105),char(99),char(101),char(115),char(91),char(50),char(93), -char(0),char(109),char(95),char(114),char(101),char(115),char(116),char(76),char(101),char(110),char(103),char(116),char(104),char(0),char(109),char(95),char(98),char(98),char(101),char(110), -char(100),char(105),char(110),char(103),char(0),char(109),char(95),char(110),char(111),char(100),char(101),char(73),char(110),char(100),char(105),char(99),char(101),char(115),char(91),char(51), -char(93),char(0),char(109),char(95),char(114),char(101),char(115),char(116),char(65),char(114),char(101),char(97),char(0),char(109),char(95),char(99),char(48),char(91),char(52),char(93), -char(0),char(109),char(95),char(110),char(111),char(100),char(101),char(73),char(110),char(100),char(105),char(99),char(101),char(115),char(91),char(52),char(93),char(0),char(109),char(95), -char(114),char(101),char(115),char(116),char(86),char(111),char(108),char(117),char(109),char(101),char(0),char(109),char(95),char(99),char(49),char(0),char(109),char(95),char(99),char(50), -char(0),char(109),char(95),char(99),char(48),char(0),char(109),char(95),char(108),char(111),char(99),char(97),char(108),char(70),char(114),char(97),char(109),char(101),char(0),char(42), -char(109),char(95),char(114),char(105),char(103),char(105),char(100),char(66),char(111),char(100),char(121),char(0),char(109),char(95),char(110),char(111),char(100),char(101),char(73),char(110), -char(100),char(101),char(120),char(0),char(109),char(95),char(97),char(101),char(114),char(111),char(77),char(111),char(100),char(101),char(108),char(0),char(109),char(95),char(98),char(97), -char(117),char(109),char(103),char(97),char(114),char(116),char(101),char(0),char(109),char(95),char(100),char(114),char(97),char(103),char(0),char(109),char(95),char(108),char(105),char(102), -char(116),char(0),char(109),char(95),char(112),char(114),char(101),char(115),char(115),char(117),char(114),char(101),char(0),char(109),char(95),char(118),char(111),char(108),char(117),char(109), -char(101),char(0),char(109),char(95),char(100),char(121),char(110),char(97),char(109),char(105),char(99),char(70),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(0), -char(109),char(95),char(112),char(111),char(115),char(101),char(77),char(97),char(116),char(99),char(104),char(0),char(109),char(95),char(114),char(105),char(103),char(105),char(100),char(67), -char(111),char(110),char(116),char(97),char(99),char(116),char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(107),char(105),char(110), -char(101),char(116),char(105),char(99),char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115),char(0), -char(109),char(95),char(115),char(111),char(102),char(116),char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(72),char(97),char(114),char(100),char(110),char(101),char(115), -char(115),char(0),char(109),char(95),char(97),char(110),char(99),char(104),char(111),char(114),char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115),char(0),char(109), -char(95),char(115),char(111),char(102),char(116),char(82),char(105),char(103),char(105),char(100),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(72),char(97),char(114), -char(100),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(75),char(105),char(110),char(101),char(116),char(105),char(99),char(67), -char(108),char(117),char(115),char(116),char(101),char(114),char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(115),char(111),char(102), -char(116),char(83),char(111),char(102),char(116),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115), -char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(82),char(105),char(103),char(105),char(100),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(73), -char(109),char(112),char(117),char(108),char(115),char(101),char(83),char(112),char(108),char(105),char(116),char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(75),char(105), -char(110),char(101),char(116),char(105),char(99),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(83), -char(112),char(108),char(105),char(116),char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(83),char(111),char(102),char(116),char(67),char(108),char(117),char(115),char(116), -char(101),char(114),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(83),char(112),char(108),char(105),char(116),char(0),char(109),char(95),char(109),char(97),char(120), -char(86),char(111),char(108),char(117),char(109),char(101),char(0),char(109),char(95),char(116),char(105),char(109),char(101),char(83),char(99),char(97),char(108),char(101),char(0),char(109), -char(95),char(118),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(73),char(116),char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(115),char(0), -char(109),char(95),char(112),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(73),char(116),char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(115), -char(0),char(109),char(95),char(100),char(114),char(105),char(102),char(116),char(73),char(116),char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(115),char(0),char(109), -char(95),char(99),char(108),char(117),char(115),char(116),char(101),char(114),char(73),char(116),char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(115),char(0),char(109), -char(95),char(114),char(111),char(116),char(0),char(109),char(95),char(115),char(99),char(97),char(108),char(101),char(0),char(109),char(95),char(97),char(113),char(113),char(0),char(109), -char(95),char(99),char(111),char(109),char(0),char(42),char(109),char(95),char(112),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(115),char(0),char(42),char(109), -char(95),char(119),char(101),char(105),char(103),char(104),char(116),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(80),char(111),char(115),char(105),char(116),char(105), -char(111),char(110),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(87),char(101),char(105),char(103),char(116),char(115),char(0),char(109),char(95),char(98),char(118), -char(111),char(108),char(117),char(109),char(101),char(0),char(109),char(95),char(98),char(102),char(114),char(97),char(109),char(101),char(0),char(109),char(95),char(102),char(114),char(97), -char(109),char(101),char(120),char(102),char(111),char(114),char(109),char(0),char(109),char(95),char(108),char(111),char(99),char(105),char(105),char(0),char(109),char(95),char(105),char(110), -char(118),char(119),char(105),char(0),char(109),char(95),char(118),char(105),char(109),char(112),char(117),char(108),char(115),char(101),char(115),char(91),char(50),char(93),char(0),char(109), -char(95),char(100),char(105),char(109),char(112),char(117),char(108),char(115),char(101),char(115),char(91),char(50),char(93),char(0),char(109),char(95),char(108),char(118),char(0),char(109), -char(95),char(97),char(118),char(0),char(42),char(109),char(95),char(102),char(114),char(97),char(109),char(101),char(114),char(101),char(102),char(115),char(0),char(42),char(109),char(95), -char(110),char(111),char(100),char(101),char(73),char(110),char(100),char(105),char(99),char(101),char(115),char(0),char(42),char(109),char(95),char(109),char(97),char(115),char(115),char(101), -char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(70),char(114),char(97),char(109),char(101),char(82),char(101),char(102),char(115),char(0),char(109),char(95),char(110), -char(117),char(109),char(78),char(111),char(100),char(101),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(77),char(97),char(115),char(115),char(101),char(115),char(0), -char(109),char(95),char(105),char(100),char(109),char(97),char(115),char(115),char(0),char(109),char(95),char(105),char(109),char(97),char(115),char(115),char(0),char(109),char(95),char(110), -char(118),char(105),char(109),char(112),char(117),char(108),char(115),char(101),char(115),char(0),char(109),char(95),char(110),char(100),char(105),char(109),char(112),char(117),char(108),char(115), -char(101),char(115),char(0),char(109),char(95),char(110),char(100),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(108),char(100),char(97),char(109), -char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(97),char(100),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(109),char(97), -char(116),char(99),char(104),char(105),char(110),char(103),char(0),char(109),char(95),char(109),char(97),char(120),char(83),char(101),char(108),char(102),char(67),char(111),char(108),char(108), -char(105),char(115),char(105),char(111),char(110),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(0),char(109),char(95),char(115),char(101),char(108),char(102),char(67), -char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(70),char(97),char(99),char(116),char(111), -char(114),char(0),char(109),char(95),char(99),char(111),char(110),char(116),char(97),char(105),char(110),char(115),char(65),char(110),char(99),char(104),char(111),char(114),char(0),char(109), -char(95),char(99),char(111),char(108),char(108),char(105),char(100),char(101),char(0),char(109),char(95),char(99),char(108),char(117),char(115),char(116),char(101),char(114),char(73),char(110), -char(100),char(101),char(120),char(0),char(42),char(109),char(95),char(98),char(111),char(100),char(121),char(65),char(0),char(42),char(109),char(95),char(98),char(111),char(100),char(121), -char(66),char(0),char(109),char(95),char(114),char(101),char(102),char(115),char(91),char(50),char(93),char(0),char(109),char(95),char(99),char(102),char(109),char(0),char(109),char(95), -char(115),char(112),char(108),char(105),char(116),char(0),char(109),char(95),char(100),char(101),char(108),char(101),char(116),char(101),char(0),char(109),char(95),char(114),char(101),char(108), -char(80),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(91),char(50),char(93),char(0),char(109),char(95),char(98),char(111),char(100),char(121),char(65),char(116), -char(121),char(112),char(101),char(0),char(109),char(95),char(98),char(111),char(100),char(121),char(66),char(116),char(121),char(112),char(101),char(0),char(109),char(95),char(106),char(111), -char(105),char(110),char(116),char(84),char(121),char(112),char(101),char(0),char(42),char(109),char(95),char(112),char(111),char(115),char(101),char(0),char(42),char(42),char(109),char(95), -char(109),char(97),char(116),char(101),char(114),char(105),char(97),char(108),char(115),char(0),char(42),char(109),char(95),char(110),char(111),char(100),char(101),char(115),char(0),char(42), -char(109),char(95),char(108),char(105),char(110),char(107),char(115),char(0),char(42),char(109),char(95),char(102),char(97),char(99),char(101),char(115),char(0),char(42),char(109),char(95), -char(116),char(101),char(116),char(114),char(97),char(104),char(101),char(100),char(114),char(97),char(0),char(42),char(109),char(95),char(97),char(110),char(99),char(104),char(111),char(114), -char(115),char(0),char(42),char(109),char(95),char(99),char(108),char(117),char(115),char(116),char(101),char(114),char(115),char(0),char(42),char(109),char(95),char(106),char(111),char(105), -char(110),char(116),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(77),char(97),char(116),char(101),char(114),char(105),char(97),char(108),char(115),char(0),char(109), -char(95),char(110),char(117),char(109),char(76),char(105),char(110),char(107),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(70),char(97),char(99),char(101),char(115), -char(0),char(109),char(95),char(110),char(117),char(109),char(84),char(101),char(116),char(114),char(97),char(104),char(101),char(100),char(114),char(97),char(0),char(109),char(95),char(110), -char(117),char(109),char(65),char(110),char(99),char(104),char(111),char(114),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(67),char(108),char(117),char(115),char(116), -char(101),char(114),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(74),char(111),char(105),char(110),char(116),char(115),char(0),char(109),char(95),char(99),char(111), -char(110),char(102),char(105),char(103),char(0),char(109),char(95),char(122),char(101),char(114),char(111),char(82),char(111),char(116),char(80),char(97),char(114),char(101),char(110),char(116), -char(84),char(111),char(84),char(104),char(105),char(115),char(0),char(109),char(95),char(112),char(97),char(114),char(101),char(110),char(116),char(67),char(111),char(109),char(84),char(111), -char(84),char(104),char(105),char(115),char(67),char(111),char(109),char(79),char(102),char(102),char(115),char(101),char(116),char(0),char(109),char(95),char(116),char(104),char(105),char(115), -char(80),char(105),char(118),char(111),char(116),char(84),char(111),char(84),char(104),char(105),char(115),char(67),char(111),char(109),char(79),char(102),char(102),char(115),char(101),char(116), -char(0),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(65),char(120),char(105),char(115),char(84),char(111),char(112),char(91),char(54),char(93),char(0),char(109), -char(95),char(106),char(111),char(105),char(110),char(116),char(65),char(120),char(105),char(115),char(66),char(111),char(116),char(116),char(111),char(109),char(91),char(54),char(93),char(0), -char(109),char(95),char(108),char(105),char(110),char(107),char(73),char(110),char(101),char(114),char(116),char(105),char(97),char(0),char(109),char(95),char(108),char(105),char(110),char(107), -char(77),char(97),char(115),char(115),char(0),char(109),char(95),char(112),char(97),char(114),char(101),char(110),char(116),char(73),char(110),char(100),char(101),char(120),char(0),char(109), -char(95),char(100),char(111),char(102),char(67),char(111),char(117),char(110),char(116),char(0),char(109),char(95),char(112),char(111),char(115),char(86),char(97),char(114),char(67),char(111), -char(117),char(110),char(116),char(0),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(80),char(111),char(115),char(91),char(55),char(93),char(0),char(109),char(95), -char(106),char(111),char(105),char(110),char(116),char(86),char(101),char(108),char(91),char(54),char(93),char(0),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(84), -char(111),char(114),char(113),char(117),char(101),char(91),char(54),char(93),char(0),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(68),char(97),char(109),char(112), -char(105),char(110),char(103),char(0),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(70),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(0), -char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(76),char(111),char(119),char(101),char(114),char(76),char(105),char(109),char(105),char(116),char(0),char(109),char(95), -char(106),char(111),char(105),char(110),char(116),char(85),char(112),char(112),char(101),char(114),char(76),char(105),char(109),char(105),char(116),char(0),char(109),char(95),char(106),char(111), -char(105),char(110),char(116),char(77),char(97),char(120),char(70),char(111),char(114),char(99),char(101),char(0),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(77), -char(97),char(120),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(42),char(109),char(95),char(108),char(105),char(110),char(107),char(78),char(97), -char(109),char(101),char(0),char(42),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(78),char(97),char(109),char(101),char(0),char(42),char(109),char(95),char(108), -char(105),char(110),char(107),char(67),char(111),char(108),char(108),char(105),char(100),char(101),char(114),char(0),char(42),char(109),char(95),char(112),char(97),char(100),char(100),char(105), -char(110),char(103),char(80),char(116),char(114),char(0),char(109),char(95),char(98),char(97),char(115),char(101),char(87),char(111),char(114),char(108),char(100),char(84),char(114),char(97), -char(110),char(115),char(102),char(111),char(114),char(109),char(0),char(109),char(95),char(98),char(97),char(115),char(101),char(73),char(110),char(101),char(114),char(116),char(105),char(97), -char(0),char(109),char(95),char(98),char(97),char(115),char(101),char(77),char(97),char(115),char(115),char(0),char(42),char(109),char(95),char(98),char(97),char(115),char(101),char(78), -char(97),char(109),char(101),char(0),char(42),char(109),char(95),char(98),char(97),char(115),char(101),char(67),char(111),char(108),char(108),char(105),char(100),char(101),char(114),char(0), -char(84),char(89),char(80),char(69),char(95),char(0),char(0),char(0),char(99),char(104),char(97),char(114),char(0),char(117),char(99),char(104),char(97),char(114),char(0),char(115), -char(104),char(111),char(114),char(116),char(0),char(117),char(115),char(104),char(111),char(114),char(116),char(0),char(105),char(110),char(116),char(0),char(108),char(111),char(110),char(103), -char(0),char(117),char(108),char(111),char(110),char(103),char(0),char(102),char(108),char(111),char(97),char(116),char(0),char(100),char(111),char(117),char(98),char(108),char(101),char(0), -char(118),char(111),char(105),char(100),char(0),char(80),char(111),char(105),char(110),char(116),char(101),char(114),char(65),char(114),char(114),char(97),char(121),char(0),char(98),char(116), -char(80),char(104),char(121),char(115),char(105),char(99),char(115),char(83),char(121),char(115),char(116),char(101),char(109),char(0),char(76),char(105),char(115),char(116),char(66),char(97), -char(115),char(101),char(0),char(98),char(116),char(86),char(101),char(99),char(116),char(111),char(114),char(51),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116), -char(97),char(0),char(98),char(116),char(86),char(101),char(99),char(116),char(111),char(114),char(51),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116), -char(97),char(0),char(98),char(116),char(81),char(117),char(97),char(116),char(101),char(114),char(110),char(105),char(111),char(110),char(70),char(108),char(111),char(97),char(116),char(68), -char(97),char(116),char(97),char(0),char(98),char(116),char(81),char(117),char(97),char(116),char(101),char(114),char(110),char(105),char(111),char(110),char(68),char(111),char(117),char(98), -char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(77),char(97),char(116),char(114),char(105),char(120),char(51),char(120),char(51),char(70),char(108), -char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(77),char(97),char(116),char(114),char(105),char(120),char(51),char(120),char(51),char(68), -char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(84),char(114),char(97),char(110),char(115),char(102),char(111),char(114), -char(109),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(84),char(114),char(97),char(110),char(115),char(102),char(111), -char(114),char(109),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(66),char(118),char(104),char(83),char(117), -char(98),char(116),char(114),char(101),char(101),char(73),char(110),char(102),char(111),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(79),char(112),char(116),char(105), -char(109),char(105),char(122),char(101),char(100),char(66),char(118),char(104),char(78),char(111),char(100),char(101),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116), +char(109),char(75),char(101),char(121),char(115),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99),char(104),char(101),char(76),char(111), +char(99),char(97),char(108),char(80),char(111),char(105),char(110),char(116),char(65),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116), +char(67),char(97),char(99),char(104),char(101),char(76),char(111),char(99),char(97),char(108),char(80),char(111),char(105),char(110),char(116),char(66),char(91),char(52),char(93),char(0), +char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99),char(104),char(101),char(80),char(111),char(115),char(105),char(116),char(105),char(111),char(110), +char(87),char(111),char(114),char(108),char(100),char(79),char(110),char(65),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67), +char(97),char(99),char(104),char(101),char(80),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(87),char(111),char(114),char(108),char(100),char(79),char(110),char(66), +char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99),char(104),char(101),char(78),char(111),char(114),char(109), +char(97),char(108),char(87),char(111),char(114),char(108),char(100),char(79),char(110),char(66),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110), +char(116),char(67),char(97),char(99),char(104),char(101),char(76),char(97),char(116),char(101),char(114),char(97),char(108),char(70),char(114),char(105),char(99),char(116),char(105),char(111), +char(110),char(68),char(105),char(114),char(49),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99),char(104), +char(101),char(76),char(97),char(116),char(101),char(114),char(97),char(108),char(70),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(68),char(105),char(114),char(50), +char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99),char(104),char(101),char(68),char(105),char(115),char(116), +char(97),char(110),char(99),char(101),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99),char(104),char(101), +char(65),char(112),char(112),char(108),char(105),char(101),char(100),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(91),char(52),char(93),char(0),char(109),char(95), +char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99),char(104),char(101),char(67),char(111),char(109),char(98),char(105),char(110),char(101),char(100),char(70),char(114), +char(105),char(99),char(116),char(105),char(111),char(110),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99), +char(104),char(101),char(67),char(111),char(109),char(98),char(105),char(110),char(101),char(100),char(82),char(111),char(108),char(108),char(105),char(110),char(103),char(70),char(114),char(105), +char(99),char(116),char(105),char(111),char(110),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99),char(104), +char(101),char(67),char(111),char(109),char(98),char(105),char(110),char(101),char(100),char(83),char(112),char(105),char(110),char(110),char(105),char(110),char(103),char(70),char(114),char(105), +char(99),char(116),char(105),char(111),char(110),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99),char(104), +char(101),char(67),char(111),char(109),char(98),char(105),char(110),char(101),char(100),char(82),char(101),char(115),char(116),char(105),char(116),char(117),char(116),char(105),char(111),char(110), +char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99),char(104),char(101),char(80),char(97),char(114),char(116), +char(73),char(100),char(48),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99),char(104),char(101),char(80), +char(97),char(114),char(116),char(73),char(100),char(49),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99), +char(104),char(101),char(73),char(110),char(100),char(101),char(120),char(48),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67), +char(97),char(99),char(104),char(101),char(73),char(110),char(100),char(101),char(120),char(49),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110), +char(116),char(67),char(97),char(99),char(104),char(101),char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(80),char(111),char(105),char(110),char(116),char(70),char(108), +char(97),char(103),char(115),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99),char(104),char(101),char(65), +char(112),char(112),char(108),char(105),char(101),char(100),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(76),char(97),char(116),char(101),char(114),char(97),char(108), +char(49),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99),char(104),char(101),char(65),char(112),char(112), +char(108),char(105),char(101),char(100),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(76),char(97),char(116),char(101),char(114),char(97),char(108),char(50),char(91), +char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99),char(104),char(101),char(67),char(111),char(110),char(116),char(97), +char(99),char(116),char(77),char(111),char(116),char(105),char(111),char(110),char(49),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116), +char(67),char(97),char(99),char(104),char(101),char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(77),char(111),char(116),char(105),char(111),char(110),char(50),char(91), +char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99),char(104),char(101),char(67),char(111),char(110),char(116),char(97), +char(99),char(116),char(67),char(70),char(77),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99),char(104), +char(101),char(67),char(111),char(109),char(98),char(105),char(110),char(101),char(100),char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(83),char(116),char(105),char(102), +char(102),char(110),char(101),char(115),char(115),char(49),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99), +char(104),char(101),char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(69),char(82),char(80),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111), +char(105),char(110),char(116),char(67),char(97),char(99),char(104),char(101),char(67),char(111),char(109),char(98),char(105),char(110),char(101),char(100),char(67),char(111),char(110),char(116), +char(97),char(99),char(116),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(49),char(91),char(52),char(93),char(0),char(109),char(95),char(112),char(111),char(105), +char(110),char(116),char(67),char(97),char(99),char(104),char(101),char(70),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(67),char(70),char(77),char(91),char(52), +char(93),char(0),char(109),char(95),char(112),char(111),char(105),char(110),char(116),char(67),char(97),char(99),char(104),char(101),char(76),char(105),char(102),char(101),char(84),char(105), +char(109),char(101),char(91),char(52),char(93),char(0),char(109),char(95),char(110),char(117),char(109),char(67),char(97),char(99),char(104),char(101),char(100),char(80),char(111),char(105), +char(110),char(116),char(115),char(0),char(109),char(95),char(99),char(111),char(109),char(112),char(97),char(110),char(105),char(111),char(110),char(73),char(100),char(65),char(0),char(109), +char(95),char(99),char(111),char(109),char(112),char(97),char(110),char(105),char(111),char(110),char(73),char(100),char(66),char(0),char(109),char(95),char(105),char(110),char(100),char(101), +char(120),char(49),char(97),char(0),char(109),char(95),char(111),char(98),char(106),char(101),char(99),char(116),char(84),char(121),char(112),char(101),char(0),char(109),char(95),char(99), +char(111),char(110),char(116),char(97),char(99),char(116),char(66),char(114),char(101),char(97),char(107),char(105),char(110),char(103),char(84),char(104),char(114),char(101),char(115),char(104), +char(111),char(108),char(100),char(0),char(109),char(95),char(99),char(111),char(110),char(116),char(97),char(99),char(116),char(80),char(114),char(111),char(99),char(101),char(115),char(115), +char(105),char(110),char(103),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(42),char(109),char(95),char(98),char(111),char(100),char(121), +char(48),char(0),char(42),char(109),char(95),char(98),char(111),char(100),char(121),char(49),char(0),char(109),char(95),char(103),char(105),char(109),char(112),char(97),char(99),char(116), +char(83),char(117),char(98),char(84),char(121),char(112),char(101),char(0),char(42),char(109),char(95),char(117),char(110),char(115),char(99),char(97),char(108),char(101),char(100),char(80), +char(111),char(105),char(110),char(116),char(115),char(70),char(108),char(111),char(97),char(116),char(80),char(116),char(114),char(0),char(42),char(109),char(95),char(117),char(110),char(115), +char(99),char(97),char(108),char(101),char(100),char(80),char(111),char(105),char(110),char(116),char(115),char(68),char(111),char(117),char(98),char(108),char(101),char(80),char(116),char(114), +char(0),char(109),char(95),char(110),char(117),char(109),char(85),char(110),char(115),char(99),char(97),char(108),char(101),char(100),char(80),char(111),char(105),char(110),char(116),char(115), +char(0),char(109),char(95),char(112),char(97),char(100),char(100),char(105),char(110),char(103),char(51),char(91),char(52),char(93),char(0),char(42),char(109),char(95),char(98),char(114), +char(111),char(97),char(100),char(112),char(104),char(97),char(115),char(101),char(72),char(97),char(110),char(100),char(108),char(101),char(0),char(42),char(109),char(95),char(99),char(111), +char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(83),char(104),char(97),char(112),char(101),char(0),char(42),char(109),char(95),char(114),char(111),char(111),char(116), +char(67),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(83),char(104),char(97),char(112),char(101),char(0),char(109),char(95),char(119),char(111),char(114), +char(108),char(100),char(84),char(114),char(97),char(110),char(115),char(102),char(111),char(114),char(109),char(0),char(109),char(95),char(105),char(110),char(116),char(101),char(114),char(112), +char(111),char(108),char(97),char(116),char(105),char(111),char(110),char(87),char(111),char(114),char(108),char(100),char(84),char(114),char(97),char(110),char(115),char(102),char(111),char(114), +char(109),char(0),char(109),char(95),char(105),char(110),char(116),char(101),char(114),char(112),char(111),char(108),char(97),char(116),char(105),char(111),char(110),char(76),char(105),char(110), +char(101),char(97),char(114),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(105),char(110),char(116),char(101),char(114),char(112), +char(111),char(108),char(97),char(116),char(105),char(111),char(110),char(65),char(110),char(103),char(117),char(108),char(97),char(114),char(86),char(101),char(108),char(111),char(99),char(105), +char(116),char(121),char(0),char(109),char(95),char(97),char(110),char(105),char(115),char(111),char(116),char(114),char(111),char(112),char(105),char(99),char(70),char(114),char(105),char(99), +char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(100),char(101),char(97),char(99),char(116),char(105),char(118),char(97),char(116),char(105),char(111),char(110),char(84), +char(105),char(109),char(101),char(0),char(109),char(95),char(102),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(114),char(111),char(108), +char(108),char(105),char(110),char(103),char(70),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(99),char(111),char(110),char(116),char(97), +char(99),char(116),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(99),char(111),char(110),char(116),char(97),char(99),char(116),char(83), +char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(114),char(101),char(115),char(116),char(105),char(116),char(117),char(116),char(105), +char(111),char(110),char(0),char(109),char(95),char(104),char(105),char(116),char(70),char(114),char(97),char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(99), +char(99),char(100),char(83),char(119),char(101),char(112),char(116),char(83),char(112),char(104),char(101),char(114),char(101),char(82),char(97),char(100),char(105),char(117),char(115),char(0), +char(109),char(95),char(99),char(99),char(100),char(77),char(111),char(116),char(105),char(111),char(110),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100), +char(0),char(109),char(95),char(104),char(97),char(115),char(65),char(110),char(105),char(115),char(111),char(116),char(114),char(111),char(112),char(105),char(99),char(70),char(114),char(105), +char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(99),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(70),char(108),char(97), +char(103),char(115),char(0),char(109),char(95),char(105),char(115),char(108),char(97),char(110),char(100),char(84),char(97),char(103),char(49),char(0),char(109),char(95),char(99),char(111), +char(109),char(112),char(97),char(110),char(105),char(111),char(110),char(73),char(100),char(0),char(109),char(95),char(97),char(99),char(116),char(105),char(118),char(97),char(116),char(105), +char(111),char(110),char(83),char(116),char(97),char(116),char(101),char(49),char(0),char(109),char(95),char(105),char(110),char(116),char(101),char(114),char(110),char(97),char(108),char(84), +char(121),char(112),char(101),char(0),char(109),char(95),char(99),char(104),char(101),char(99),char(107),char(67),char(111),char(108),char(108),char(105),char(100),char(101),char(87),char(105), +char(116),char(104),char(0),char(109),char(95),char(99),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(70),char(105),char(108),char(116),char(101),char(114), +char(71),char(114),char(111),char(117),char(112),char(0),char(109),char(95),char(99),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(70),char(105),char(108), +char(116),char(101),char(114),char(77),char(97),char(115),char(107),char(0),char(109),char(95),char(117),char(110),char(105),char(113),char(117),char(101),char(73),char(100),char(0),char(109), +char(95),char(116),char(97),char(117),char(0),char(109),char(95),char(100),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(116),char(105),char(109), +char(101),char(83),char(116),char(101),char(112),char(0),char(109),char(95),char(109),char(97),char(120),char(69),char(114),char(114),char(111),char(114),char(82),char(101),char(100),char(117), +char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(115),char(111),char(114),char(0),char(109),char(95),char(101),char(114),char(112),char(0),char(109),char(95), +char(101),char(114),char(112),char(50),char(0),char(109),char(95),char(103),char(108),char(111),char(98),char(97),char(108),char(67),char(102),char(109),char(0),char(109),char(95),char(115), +char(112),char(108),char(105),char(116),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(80),char(101),char(110),char(101),char(116),char(114),char(97),char(116),char(105), +char(111),char(110),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(115),char(112),char(108),char(105),char(116),char(73), +char(109),char(112),char(117),char(108),char(115),char(101),char(84),char(117),char(114),char(110),char(69),char(114),char(112),char(0),char(109),char(95),char(108),char(105),char(110),char(101), +char(97),char(114),char(83),char(108),char(111),char(112),char(0),char(109),char(95),char(119),char(97),char(114),char(109),char(115),char(116),char(97),char(114),char(116),char(105),char(110), +char(103),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(109),char(97),char(120),char(71),char(121),char(114),char(111),char(115),char(99),char(111), +char(112),char(105),char(99),char(70),char(111),char(114),char(99),char(101),char(0),char(109),char(95),char(115),char(105),char(110),char(103),char(108),char(101),char(65),char(120),char(105), +char(115),char(82),char(111),char(108),char(108),char(105),char(110),char(103),char(70),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(84),char(104),char(114),char(101), +char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(110),char(117),char(109),char(73),char(116),char(101),char(114),char(97),char(116),char(105),char(111),char(110), +char(115),char(0),char(109),char(95),char(115),char(111),char(108),char(118),char(101),char(114),char(77),char(111),char(100),char(101),char(0),char(109),char(95),char(114),char(101),char(115), +char(116),char(105),char(110),char(103),char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(82),char(101),char(115),char(116),char(105),char(116),char(117),char(116),char(105), +char(111),char(110),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(109),char(105),char(110),char(105),char(109),char(117), +char(109),char(83),char(111),char(108),char(118),char(101),char(114),char(66),char(97),char(116),char(99),char(104),char(83),char(105),char(122),char(101),char(0),char(109),char(95),char(115), +char(112),char(108),char(105),char(116),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(0),char(109),char(95),char(115),char(111),char(108),char(118),char(101),char(114), +char(73),char(110),char(102),char(111),char(0),char(109),char(95),char(103),char(114),char(97),char(118),char(105),char(116),char(121),char(0),char(109),char(95),char(99),char(111),char(108), +char(108),char(105),char(115),char(105),char(111),char(110),char(79),char(98),char(106),char(101),char(99),char(116),char(68),char(97),char(116),char(97),char(0),char(109),char(95),char(105), +char(110),char(118),char(73),char(110),char(101),char(114),char(116),char(105),char(97),char(84),char(101),char(110),char(115),char(111),char(114),char(87),char(111),char(114),char(108),char(100), +char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95), +char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(97),char(110), +char(103),char(117),char(108),char(97),char(114),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114), +char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(103),char(114),char(97),char(118),char(105),char(116),char(121),char(95),char(97),char(99),char(99), +char(101),char(108),char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(105),char(110),char(118),char(73),char(110),char(101),char(114),char(116), +char(105),char(97),char(76),char(111),char(99),char(97),char(108),char(0),char(109),char(95),char(116),char(111),char(116),char(97),char(108),char(70),char(111),char(114),char(99),char(101), +char(0),char(109),char(95),char(116),char(111),char(116),char(97),char(108),char(84),char(111),char(114),char(113),char(117),char(101),char(0),char(109),char(95),char(105),char(110),char(118), +char(101),char(114),char(115),char(101),char(77),char(97),char(115),char(115),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(68),char(97),char(109), +char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(68),char(97),char(109),char(112),char(105),char(110), +char(103),char(0),char(109),char(95),char(97),char(100),char(100),char(105),char(116),char(105),char(111),char(110),char(97),char(108),char(68),char(97),char(109),char(112),char(105),char(110), +char(103),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(97),char(100),char(100),char(105),char(116),char(105),char(111),char(110),char(97),char(108), +char(76),char(105),char(110),char(101),char(97),char(114),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(84),char(104),char(114),char(101),char(115),char(104),char(111), +char(108),char(100),char(83),char(113),char(114),char(0),char(109),char(95),char(97),char(100),char(100),char(105),char(116),char(105),char(111),char(110),char(97),char(108),char(65),char(110), +char(103),char(117),char(108),char(97),char(114),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108), +char(100),char(83),char(113),char(114),char(0),char(109),char(95),char(97),char(100),char(100),char(105),char(116),char(105),char(111),char(110),char(97),char(108),char(65),char(110),char(103), +char(117),char(108),char(97),char(114),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95), +char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(108),char(101),char(101),char(112),char(105),char(110),char(103),char(84),char(104),char(114),char(101),char(115),char(104), +char(111),char(108),char(100),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(83),char(108),char(101),char(101),char(112),char(105),char(110), +char(103),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(97),char(100),char(100),char(105),char(116),char(105),char(111), +char(110),char(97),char(108),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(110),char(117),char(109),char(67),char(111),char(110),char(115), +char(116),char(114),char(97),char(105),char(110),char(116),char(82),char(111),char(119),char(115),char(0),char(110),char(117),char(98),char(0),char(42),char(109),char(95),char(114),char(98), +char(65),char(0),char(42),char(109),char(95),char(114),char(98),char(66),char(0),char(109),char(95),char(117),char(115),char(101),char(114),char(67),char(111),char(110),char(115),char(116), +char(114),char(97),char(105),char(110),char(116),char(84),char(121),char(112),char(101),char(0),char(109),char(95),char(117),char(115),char(101),char(114),char(67),char(111),char(110),char(115), +char(116),char(114),char(97),char(105),char(110),char(116),char(73),char(100),char(0),char(109),char(95),char(110),char(101),char(101),char(100),char(115),char(70),char(101),char(101),char(100), +char(98),char(97),char(99),char(107),char(0),char(109),char(95),char(97),char(112),char(112),char(108),char(105),char(101),char(100),char(73),char(109),char(112),char(117),char(108),char(115), +char(101),char(0),char(109),char(95),char(100),char(98),char(103),char(68),char(114),char(97),char(119),char(83),char(105),char(122),char(101),char(0),char(109),char(95),char(100),char(105), +char(115),char(97),char(98),char(108),char(101),char(67),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(115),char(66),char(101),char(116),char(119),char(101), +char(101),char(110),char(76),char(105),char(110),char(107),char(101),char(100),char(66),char(111),char(100),char(105),char(101),char(115),char(0),char(109),char(95),char(111),char(118),char(101), +char(114),char(114),char(105),char(100),char(101),char(78),char(117),char(109),char(83),char(111),char(108),char(118),char(101),char(114),char(73),char(116),char(101),char(114),char(97),char(116), +char(105),char(111),char(110),char(115),char(0),char(109),char(95),char(98),char(114),char(101),char(97),char(107),char(105),char(110),char(103),char(73),char(109),char(112),char(117),char(108), +char(115),char(101),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(105),char(115),char(69),char(110),char(97),char(98), +char(108),char(101),char(100),char(0),char(112),char(97),char(100),char(100),char(105),char(110),char(103),char(91),char(52),char(93),char(0),char(109),char(95),char(116),char(121),char(112), +char(101),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(109),char(95),char(112),char(105), +char(118),char(111),char(116),char(73),char(110),char(65),char(0),char(109),char(95),char(112),char(105),char(118),char(111),char(116),char(73),char(110),char(66),char(0),char(109),char(95), +char(114),char(98),char(65),char(70),char(114),char(97),char(109),char(101),char(0),char(109),char(95),char(114),char(98),char(66),char(70),char(114),char(97),char(109),char(101),char(0), +char(109),char(95),char(117),char(115),char(101),char(82),char(101),char(102),char(101),char(114),char(101),char(110),char(99),char(101),char(70),char(114),char(97),char(109),char(101),char(65), +char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(79),char(110),char(108),char(121),char(0),char(109),char(95),char(101),char(110),char(97), +char(98),char(108),char(101),char(65),char(110),char(103),char(117),char(108),char(97),char(114),char(77),char(111),char(116),char(111),char(114),char(0),char(109),char(95),char(109),char(111), +char(116),char(111),char(114),char(84),char(97),char(114),char(103),char(101),char(116),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95), +char(109),char(97),char(120),char(77),char(111),char(116),char(111),char(114),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(0),char(109),char(95),char(108),char(111), +char(119),char(101),char(114),char(76),char(105),char(109),char(105),char(116),char(0),char(109),char(95),char(117),char(112),char(112),char(101),char(114),char(76),char(105),char(109),char(105), +char(116),char(0),char(109),char(95),char(108),char(105),char(109),char(105),char(116),char(83),char(111),char(102),char(116),char(110),char(101),char(115),char(115),char(0),char(109),char(95), +char(98),char(105),char(97),char(115),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(114),char(101),char(108),char(97),char(120),char(97),char(116), +char(105),char(111),char(110),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(112),char(97),char(100),char(100),char(105),char(110),char(103),char(49), +char(91),char(52),char(93),char(0),char(109),char(95),char(115),char(119),char(105),char(110),char(103),char(83),char(112),char(97),char(110),char(49),char(0),char(109),char(95),char(115), +char(119),char(105),char(110),char(103),char(83),char(112),char(97),char(110),char(50),char(0),char(109),char(95),char(116),char(119),char(105),char(115),char(116),char(83),char(112),char(97), +char(110),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(85),char(112),char(112),char(101),char(114),char(76),char(105),char(109),char(105),char(116), +char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(76),char(111),char(119),char(101),char(114),char(76),char(105),char(109),char(105),char(116),char(0), +char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(85),char(112),char(112),char(101),char(114),char(76),char(105),char(109),char(105),char(116),char(0), +char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(76),char(111),char(119),char(101),char(114),char(76),char(105),char(109),char(105),char(116),char(0), +char(109),char(95),char(117),char(115),char(101),char(76),char(105),char(110),char(101),char(97),char(114),char(82),char(101),char(102),char(101),char(114),char(101),char(110),char(99),char(101), +char(70),char(114),char(97),char(109),char(101),char(65),char(0),char(109),char(95),char(117),char(115),char(101),char(79),char(102),char(102),char(115),char(101),char(116),char(70),char(111), +char(114),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(70),char(114),char(97),char(109),char(101),char(0),char(109),char(95),char(54), +char(100),char(111),char(102),char(68),char(97),char(116),char(97),char(0),char(109),char(95),char(115),char(112),char(114),char(105),char(110),char(103),char(69),char(110),char(97),char(98), +char(108),char(101),char(100),char(91),char(54),char(93),char(0),char(109),char(95),char(101),char(113),char(117),char(105),char(108),char(105),char(98),char(114),char(105),char(117),char(109), +char(80),char(111),char(105),char(110),char(116),char(91),char(54),char(93),char(0),char(109),char(95),char(115),char(112),char(114),char(105),char(110),char(103),char(83),char(116),char(105), +char(102),char(102),char(110),char(101),char(115),char(115),char(91),char(54),char(93),char(0),char(109),char(95),char(115),char(112),char(114),char(105),char(110),char(103),char(68),char(97), +char(109),char(112),char(105),char(110),char(103),char(91),char(54),char(93),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(66),char(111),char(117), +char(110),char(99),char(101),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(116),char(111),char(112),char(69),char(82),char(80),char(0), +char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(116),char(111),char(112),char(67),char(70),char(77),char(0),char(109),char(95),char(108),char(105), +char(110),char(101),char(97),char(114),char(77),char(111),char(116),char(111),char(114),char(69),char(82),char(80),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97), +char(114),char(77),char(111),char(116),char(111),char(114),char(67),char(70),char(77),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(84),char(97), +char(114),char(103),char(101),char(116),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97), +char(114),char(77),char(97),char(120),char(77),char(111),char(116),char(111),char(114),char(70),char(111),char(114),char(99),char(101),char(0),char(109),char(95),char(108),char(105),char(110), +char(101),char(97),char(114),char(83),char(101),char(114),char(118),char(111),char(84),char(97),char(114),char(103),char(101),char(116),char(0),char(109),char(95),char(108),char(105),char(110), +char(101),char(97),char(114),char(83),char(112),char(114),char(105),char(110),char(103),char(83),char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(0),char(109), +char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(112),char(114),char(105),char(110),char(103),char(68),char(97),char(109),char(112),char(105),char(110),char(103), +char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(69),char(113),char(117),char(105),char(108),char(105),char(98),char(114),char(105),char(117),char(109), +char(80),char(111),char(105),char(110),char(116),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(69),char(110),char(97),char(98),char(108),char(101), +char(77),char(111),char(116),char(111),char(114),char(91),char(52),char(93),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(101),char(114), +char(118),char(111),char(77),char(111),char(116),char(111),char(114),char(91),char(52),char(93),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(69), +char(110),char(97),char(98),char(108),char(101),char(83),char(112),char(114),char(105),char(110),char(103),char(91),char(52),char(93),char(0),char(109),char(95),char(108),char(105),char(110), +char(101),char(97),char(114),char(83),char(112),char(114),char(105),char(110),char(103),char(83),char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(76),char(105), +char(109),char(105),char(116),char(101),char(100),char(91),char(52),char(93),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(112),char(114), +char(105),char(110),char(103),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(76),char(105),char(109),char(105),char(116),char(101),char(100),char(91),char(52),char(93), +char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(66),char(111),char(117),char(110),char(99),char(101),char(0),char(109),char(95),char(97), +char(110),char(103),char(117),char(108),char(97),char(114),char(83),char(116),char(111),char(112),char(69),char(82),char(80),char(0),char(109),char(95),char(97),char(110),char(103),char(117), +char(108),char(97),char(114),char(83),char(116),char(111),char(112),char(67),char(70),char(77),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114), +char(77),char(111),char(116),char(111),char(114),char(69),char(82),char(80),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(77),char(111), +char(116),char(111),char(114),char(67),char(70),char(77),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(84),char(97),char(114),char(103), +char(101),char(116),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114), +char(77),char(97),char(120),char(77),char(111),char(116),char(111),char(114),char(70),char(111),char(114),char(99),char(101),char(0),char(109),char(95),char(97),char(110),char(103),char(117), +char(108),char(97),char(114),char(83),char(101),char(114),char(118),char(111),char(84),char(97),char(114),char(103),char(101),char(116),char(0),char(109),char(95),char(97),char(110),char(103), +char(117),char(108),char(97),char(114),char(83),char(112),char(114),char(105),char(110),char(103),char(83),char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(0), +char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(83),char(112),char(114),char(105),char(110),char(103),char(68),char(97),char(109),char(112),char(105), +char(110),char(103),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(69),char(113),char(117),char(105),char(108),char(105),char(98),char(114), +char(105),char(117),char(109),char(80),char(111),char(105),char(110),char(116),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(69),char(110), +char(97),char(98),char(108),char(101),char(77),char(111),char(116),char(111),char(114),char(91),char(52),char(93),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108), +char(97),char(114),char(83),char(101),char(114),char(118),char(111),char(77),char(111),char(116),char(111),char(114),char(91),char(52),char(93),char(0),char(109),char(95),char(97),char(110), +char(103),char(117),char(108),char(97),char(114),char(69),char(110),char(97),char(98),char(108),char(101),char(83),char(112),char(114),char(105),char(110),char(103),char(91),char(52),char(93), +char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(83),char(112),char(114),char(105),char(110),char(103),char(83),char(116),char(105),char(102), +char(102),char(110),char(101),char(115),char(115),char(76),char(105),char(109),char(105),char(116),char(101),char(100),char(91),char(52),char(93),char(0),char(109),char(95),char(97),char(110), +char(103),char(117),char(108),char(97),char(114),char(83),char(112),char(114),char(105),char(110),char(103),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(76),char(105), +char(109),char(105),char(116),char(101),char(100),char(91),char(52),char(93),char(0),char(109),char(95),char(114),char(111),char(116),char(97),char(116),char(101),char(79),char(114),char(100), +char(101),char(114),char(0),char(109),char(95),char(97),char(120),char(105),char(115),char(73),char(110),char(65),char(0),char(109),char(95),char(97),char(120),char(105),char(115),char(73), +char(110),char(66),char(0),char(109),char(95),char(114),char(97),char(116),char(105),char(111),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83), +char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(83),char(116), +char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(118),char(111),char(108),char(117),char(109),char(101),char(83),char(116),char(105),char(102), +char(102),char(110),char(101),char(115),char(115),char(0),char(42),char(109),char(95),char(109),char(97),char(116),char(101),char(114),char(105),char(97),char(108),char(0),char(109),char(95), +char(112),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(112),char(114),char(101),char(118),char(105),char(111),char(117),char(115),char(80), +char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(118),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109), +char(95),char(97),char(99),char(99),char(117),char(109),char(117),char(108),char(97),char(116),char(101),char(100),char(70),char(111),char(114),char(99),char(101),char(0),char(109),char(95), +char(110),char(111),char(114),char(109),char(97),char(108),char(0),char(109),char(95),char(97),char(114),char(101),char(97),char(0),char(109),char(95),char(97),char(116),char(116),char(97), +char(99),char(104),char(0),char(109),char(95),char(110),char(111),char(100),char(101),char(73),char(110),char(100),char(105),char(99),char(101),char(115),char(91),char(50),char(93),char(0), +char(109),char(95),char(114),char(101),char(115),char(116),char(76),char(101),char(110),char(103),char(116),char(104),char(0),char(109),char(95),char(98),char(98),char(101),char(110),char(100), +char(105),char(110),char(103),char(0),char(109),char(95),char(110),char(111),char(100),char(101),char(73),char(110),char(100),char(105),char(99),char(101),char(115),char(91),char(51),char(93), +char(0),char(109),char(95),char(114),char(101),char(115),char(116),char(65),char(114),char(101),char(97),char(0),char(109),char(95),char(99),char(48),char(91),char(52),char(93),char(0), +char(109),char(95),char(110),char(111),char(100),char(101),char(73),char(110),char(100),char(105),char(99),char(101),char(115),char(91),char(52),char(93),char(0),char(109),char(95),char(114), +char(101),char(115),char(116),char(86),char(111),char(108),char(117),char(109),char(101),char(0),char(109),char(95),char(99),char(49),char(0),char(109),char(95),char(99),char(50),char(0), +char(109),char(95),char(99),char(48),char(0),char(109),char(95),char(108),char(111),char(99),char(97),char(108),char(70),char(114),char(97),char(109),char(101),char(0),char(42),char(109), +char(95),char(114),char(105),char(103),char(105),char(100),char(66),char(111),char(100),char(121),char(0),char(109),char(95),char(110),char(111),char(100),char(101),char(73),char(110),char(100), +char(101),char(120),char(0),char(109),char(95),char(97),char(101),char(114),char(111),char(77),char(111),char(100),char(101),char(108),char(0),char(109),char(95),char(98),char(97),char(117), +char(109),char(103),char(97),char(114),char(116),char(101),char(0),char(109),char(95),char(100),char(114),char(97),char(103),char(0),char(109),char(95),char(108),char(105),char(102),char(116), +char(0),char(109),char(95),char(112),char(114),char(101),char(115),char(115),char(117),char(114),char(101),char(0),char(109),char(95),char(118),char(111),char(108),char(117),char(109),char(101), +char(0),char(109),char(95),char(100),char(121),char(110),char(97),char(109),char(105),char(99),char(70),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(0),char(109), +char(95),char(112),char(111),char(115),char(101),char(77),char(97),char(116),char(99),char(104),char(0),char(109),char(95),char(114),char(105),char(103),char(105),char(100),char(67),char(111), +char(110),char(116),char(97),char(99),char(116),char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(107),char(105),char(110),char(101), +char(116),char(105),char(99),char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115),char(0),char(109), +char(95),char(115),char(111),char(102),char(116),char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115), +char(0),char(109),char(95),char(97),char(110),char(99),char(104),char(111),char(114),char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115),char(0),char(109),char(95), +char(115),char(111),char(102),char(116),char(82),char(105),char(103),char(105),char(100),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(72),char(97),char(114),char(100), +char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(75),char(105),char(110),char(101),char(116),char(105),char(99),char(67),char(108), +char(117),char(115),char(116),char(101),char(114),char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(115),char(111),char(102),char(116), +char(83),char(111),char(102),char(116),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115),char(0), +char(109),char(95),char(115),char(111),char(102),char(116),char(82),char(105),char(103),char(105),char(100),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(73),char(109), +char(112),char(117),char(108),char(115),char(101),char(83),char(112),char(108),char(105),char(116),char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(75),char(105),char(110), +char(101),char(116),char(105),char(99),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(83),char(112), +char(108),char(105),char(116),char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(83),char(111),char(102),char(116),char(67),char(108),char(117),char(115),char(116),char(101), +char(114),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(83),char(112),char(108),char(105),char(116),char(0),char(109),char(95),char(109),char(97),char(120),char(86), +char(111),char(108),char(117),char(109),char(101),char(0),char(109),char(95),char(116),char(105),char(109),char(101),char(83),char(99),char(97),char(108),char(101),char(0),char(109),char(95), +char(118),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(73),char(116),char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(115),char(0),char(109), +char(95),char(112),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(73),char(116),char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(115),char(0), +char(109),char(95),char(100),char(114),char(105),char(102),char(116),char(73),char(116),char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95), +char(99),char(108),char(117),char(115),char(116),char(101),char(114),char(73),char(116),char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95), +char(114),char(111),char(116),char(0),char(109),char(95),char(115),char(99),char(97),char(108),char(101),char(0),char(109),char(95),char(97),char(113),char(113),char(0),char(109),char(95), +char(99),char(111),char(109),char(0),char(42),char(109),char(95),char(112),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(115),char(0),char(42),char(109),char(95), +char(119),char(101),char(105),char(103),char(104),char(116),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(80),char(111),char(115),char(105),char(116),char(105),char(111), +char(110),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(87),char(101),char(105),char(103),char(116),char(115),char(0),char(109),char(95),char(98),char(118),char(111), +char(108),char(117),char(109),char(101),char(0),char(109),char(95),char(98),char(102),char(114),char(97),char(109),char(101),char(0),char(109),char(95),char(102),char(114),char(97),char(109), +char(101),char(120),char(102),char(111),char(114),char(109),char(0),char(109),char(95),char(108),char(111),char(99),char(105),char(105),char(0),char(109),char(95),char(105),char(110),char(118), +char(119),char(105),char(0),char(109),char(95),char(118),char(105),char(109),char(112),char(117),char(108),char(115),char(101),char(115),char(91),char(50),char(93),char(0),char(109),char(95), +char(100),char(105),char(109),char(112),char(117),char(108),char(115),char(101),char(115),char(91),char(50),char(93),char(0),char(109),char(95),char(108),char(118),char(0),char(109),char(95), +char(97),char(118),char(0),char(42),char(109),char(95),char(102),char(114),char(97),char(109),char(101),char(114),char(101),char(102),char(115),char(0),char(42),char(109),char(95),char(110), +char(111),char(100),char(101),char(73),char(110),char(100),char(105),char(99),char(101),char(115),char(0),char(42),char(109),char(95),char(109),char(97),char(115),char(115),char(101),char(115), +char(0),char(109),char(95),char(110),char(117),char(109),char(70),char(114),char(97),char(109),char(101),char(82),char(101),char(102),char(115),char(0),char(109),char(95),char(110),char(117), +char(109),char(78),char(111),char(100),char(101),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(77),char(97),char(115),char(115),char(101),char(115),char(0),char(109), +char(95),char(105),char(100),char(109),char(97),char(115),char(115),char(0),char(109),char(95),char(105),char(109),char(97),char(115),char(115),char(0),char(109),char(95),char(110),char(118), +char(105),char(109),char(112),char(117),char(108),char(115),char(101),char(115),char(0),char(109),char(95),char(110),char(100),char(105),char(109),char(112),char(117),char(108),char(115),char(101), +char(115),char(0),char(109),char(95),char(110),char(100),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(108),char(100),char(97),char(109),char(112), +char(105),char(110),char(103),char(0),char(109),char(95),char(97),char(100),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(109),char(97),char(116), +char(99),char(104),char(105),char(110),char(103),char(0),char(109),char(95),char(109),char(97),char(120),char(83),char(101),char(108),char(102),char(67),char(111),char(108),char(108),char(105), +char(115),char(105),char(111),char(110),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(0),char(109),char(95),char(115),char(101),char(108),char(102),char(67),char(111), +char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(70),char(97),char(99),char(116),char(111),char(114), +char(0),char(109),char(95),char(99),char(111),char(110),char(116),char(97),char(105),char(110),char(115),char(65),char(110),char(99),char(104),char(111),char(114),char(0),char(109),char(95), +char(99),char(111),char(108),char(108),char(105),char(100),char(101),char(0),char(109),char(95),char(99),char(108),char(117),char(115),char(116),char(101),char(114),char(73),char(110),char(100), +char(101),char(120),char(0),char(42),char(109),char(95),char(98),char(111),char(100),char(121),char(65),char(0),char(42),char(109),char(95),char(98),char(111),char(100),char(121),char(66), +char(0),char(109),char(95),char(114),char(101),char(102),char(115),char(91),char(50),char(93),char(0),char(109),char(95),char(99),char(102),char(109),char(0),char(109),char(95),char(115), +char(112),char(108),char(105),char(116),char(0),char(109),char(95),char(100),char(101),char(108),char(101),char(116),char(101),char(0),char(109),char(95),char(114),char(101),char(108),char(80), +char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(91),char(50),char(93),char(0),char(109),char(95),char(98),char(111),char(100),char(121),char(65),char(116),char(121), +char(112),char(101),char(0),char(109),char(95),char(98),char(111),char(100),char(121),char(66),char(116),char(121),char(112),char(101),char(0),char(109),char(95),char(106),char(111),char(105), +char(110),char(116),char(84),char(121),char(112),char(101),char(0),char(42),char(109),char(95),char(112),char(111),char(115),char(101),char(0),char(42),char(42),char(109),char(95),char(109), +char(97),char(116),char(101),char(114),char(105),char(97),char(108),char(115),char(0),char(42),char(109),char(95),char(110),char(111),char(100),char(101),char(115),char(0),char(42),char(109), +char(95),char(108),char(105),char(110),char(107),char(115),char(0),char(42),char(109),char(95),char(102),char(97),char(99),char(101),char(115),char(0),char(42),char(109),char(95),char(116), +char(101),char(116),char(114),char(97),char(104),char(101),char(100),char(114),char(97),char(0),char(42),char(109),char(95),char(97),char(110),char(99),char(104),char(111),char(114),char(115), +char(0),char(42),char(109),char(95),char(99),char(108),char(117),char(115),char(116),char(101),char(114),char(115),char(0),char(42),char(109),char(95),char(106),char(111),char(105),char(110), +char(116),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(77),char(97),char(116),char(101),char(114),char(105),char(97),char(108),char(115),char(0),char(109),char(95), +char(110),char(117),char(109),char(76),char(105),char(110),char(107),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(70),char(97),char(99),char(101),char(115),char(0), +char(109),char(95),char(110),char(117),char(109),char(84),char(101),char(116),char(114),char(97),char(104),char(101),char(100),char(114),char(97),char(0),char(109),char(95),char(110),char(117), +char(109),char(65),char(110),char(99),char(104),char(111),char(114),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(67),char(108),char(117),char(115),char(116),char(101), +char(114),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(74),char(111),char(105),char(110),char(116),char(115),char(0),char(109),char(95),char(99),char(111),char(110), +char(102),char(105),char(103),char(0),char(109),char(95),char(122),char(101),char(114),char(111),char(82),char(111),char(116),char(80),char(97),char(114),char(101),char(110),char(116),char(84), +char(111),char(84),char(104),char(105),char(115),char(0),char(109),char(95),char(112),char(97),char(114),char(101),char(110),char(116),char(67),char(111),char(109),char(84),char(111),char(84), +char(104),char(105),char(115),char(80),char(105),char(118),char(111),char(116),char(79),char(102),char(102),char(115),char(101),char(116),char(0),char(109),char(95),char(116),char(104),char(105), +char(115),char(80),char(105),char(118),char(111),char(116),char(84),char(111),char(84),char(104),char(105),char(115),char(67),char(111),char(109),char(79),char(102),char(102),char(115),char(101), +char(116),char(0),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(65),char(120),char(105),char(115),char(84),char(111),char(112),char(91),char(54),char(93),char(0), +char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(65),char(120),char(105),char(115),char(66),char(111),char(116),char(116),char(111),char(109),char(91),char(54),char(93), +char(0),char(109),char(95),char(108),char(105),char(110),char(107),char(73),char(110),char(101),char(114),char(116),char(105),char(97),char(0),char(109),char(95),char(97),char(98),char(115), +char(70),char(114),char(97),char(109),char(101),char(84),char(111),char(116),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(84),char(111),char(112),char(0), +char(109),char(95),char(97),char(98),char(115),char(70),char(114),char(97),char(109),char(101),char(84),char(111),char(116),char(86),char(101),char(108),char(111),char(99),char(105),char(116), +char(121),char(66),char(111),char(116),char(116),char(111),char(109),char(0),char(109),char(95),char(97),char(98),char(115),char(70),char(114),char(97),char(109),char(101),char(76),char(111), +char(99),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(84),char(111),char(112),char(0),char(109),char(95),char(97),char(98),char(115),char(70),char(114), +char(97),char(109),char(101),char(76),char(111),char(99),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(66),char(111),char(116),char(116),char(111),char(109), +char(0),char(109),char(95),char(108),char(105),char(110),char(107),char(77),char(97),char(115),char(115),char(0),char(109),char(95),char(112),char(97),char(114),char(101),char(110),char(116), +char(73),char(110),char(100),char(101),char(120),char(0),char(109),char(95),char(100),char(111),char(102),char(67),char(111),char(117),char(110),char(116),char(0),char(109),char(95),char(112), +char(111),char(115),char(86),char(97),char(114),char(67),char(111),char(117),char(110),char(116),char(0),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(80),char(111), +char(115),char(91),char(55),char(93),char(0),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(86),char(101),char(108),char(91),char(54),char(93),char(0),char(109), +char(95),char(106),char(111),char(105),char(110),char(116),char(84),char(111),char(114),char(113),char(117),char(101),char(91),char(54),char(93),char(0),char(109),char(95),char(106),char(111), +char(105),char(110),char(116),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(70),char(114), +char(105),char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(76),char(111),char(119),char(101),char(114),char(76), +char(105),char(109),char(105),char(116),char(0),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(85),char(112),char(112),char(101),char(114),char(76),char(105),char(109), +char(105),char(116),char(0),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(77),char(97),char(120),char(70),char(111),char(114),char(99),char(101),char(0),char(109), +char(95),char(106),char(111),char(105),char(110),char(116),char(77),char(97),char(120),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(42),char(109), +char(95),char(108),char(105),char(110),char(107),char(78),char(97),char(109),char(101),char(0),char(42),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(78),char(97), +char(109),char(101),char(0),char(42),char(109),char(95),char(108),char(105),char(110),char(107),char(67),char(111),char(108),char(108),char(105),char(100),char(101),char(114),char(0),char(42), +char(109),char(95),char(112),char(97),char(100),char(100),char(105),char(110),char(103),char(80),char(116),char(114),char(0),char(109),char(95),char(98),char(97),char(115),char(101),char(87), +char(111),char(114),char(108),char(100),char(80),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(98),char(97),char(115),char(101),char(87), +char(111),char(114),char(108),char(100),char(79),char(114),char(105),char(101),char(110),char(116),char(97),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(98),char(97), +char(115),char(101),char(76),char(105),char(110),char(101),char(97),char(114),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(98), +char(97),char(115),char(101),char(65),char(110),char(103),char(117),char(108),char(97),char(114),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109), +char(95),char(98),char(97),char(115),char(101),char(73),char(110),char(101),char(114),char(116),char(105),char(97),char(0),char(109),char(95),char(98),char(97),char(115),char(101),char(77), +char(97),char(115),char(115),char(0),char(42),char(109),char(95),char(98),char(97),char(115),char(101),char(78),char(97),char(109),char(101),char(0),char(42),char(109),char(95),char(98), +char(97),char(115),char(101),char(67),char(111),char(108),char(108),char(105),char(100),char(101),char(114),char(0),char(109),char(95),char(99),char(111),char(108),char(79),char(98),char(106), +char(68),char(97),char(116),char(97),char(0),char(42),char(109),char(95),char(109),char(117),char(108),char(116),char(105),char(66),char(111),char(100),char(121),char(0),char(109),char(95), +char(108),char(105),char(110),char(107),char(0),char(0),char(0),char(0),char(84),char(89),char(80),char(69),char(99),char(0),char(0),char(0),char(99),char(104),char(97),char(114), +char(0),char(117),char(99),char(104),char(97),char(114),char(0),char(115),char(104),char(111),char(114),char(116),char(0),char(117),char(115),char(104),char(111),char(114),char(116),char(0), +char(105),char(110),char(116),char(0),char(108),char(111),char(110),char(103),char(0),char(117),char(108),char(111),char(110),char(103),char(0),char(102),char(108),char(111),char(97),char(116), +char(0),char(100),char(111),char(117),char(98),char(108),char(101),char(0),char(118),char(111),char(105),char(100),char(0),char(80),char(111),char(105),char(110),char(116),char(101),char(114), +char(65),char(114),char(114),char(97),char(121),char(0),char(98),char(116),char(80),char(104),char(121),char(115),char(105),char(99),char(115),char(83),char(121),char(115),char(116),char(101), +char(109),char(0),char(76),char(105),char(115),char(116),char(66),char(97),char(115),char(101),char(0),char(98),char(116),char(86),char(101),char(99),char(116),char(111),char(114),char(51), +char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(86),char(101),char(99),char(116),char(111),char(114),char(51),char(68), +char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(81),char(117),char(97),char(116),char(101),char(114),char(110),char(105), +char(111),char(110),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(81),char(117),char(97),char(116),char(101),char(114), +char(110),char(105),char(111),char(110),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(77),char(97),char(116), +char(114),char(105),char(120),char(51),char(120),char(51),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(77),char(97), +char(116),char(114),char(105),char(120),char(51),char(120),char(51),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116), +char(84),char(114),char(97),char(110),char(115),char(102),char(111),char(114),char(109),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98), +char(116),char(84),char(114),char(97),char(110),char(115),char(102),char(111),char(114),char(109),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97), +char(0),char(98),char(116),char(66),char(118),char(104),char(83),char(117),char(98),char(116),char(114),char(101),char(101),char(73),char(110),char(102),char(111),char(68),char(97),char(116), char(97),char(0),char(98),char(116),char(79),char(112),char(116),char(105),char(109),char(105),char(122),char(101),char(100),char(66),char(118),char(104),char(78),char(111),char(100),char(101), -char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(81),char(117),char(97),char(110),char(116),char(105),char(122), -char(101),char(100),char(66),char(118),char(104),char(78),char(111),char(100),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(81),char(117),char(97),char(110), -char(116),char(105),char(122),char(101),char(100),char(66),char(118),char(104),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116), -char(81),char(117),char(97),char(110),char(116),char(105),char(122),char(101),char(100),char(66),char(118),char(104),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97), -char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(83),char(104),char(97),char(112),char(101),char(68), -char(97),char(116),char(97),char(0),char(98),char(116),char(83),char(116),char(97),char(116),char(105),char(99),char(80),char(108),char(97),char(110),char(101),char(83),char(104),char(97), -char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(110),char(118),char(101),char(120),char(73),char(110),char(116),char(101),char(114), -char(110),char(97),char(108),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(80),char(111),char(115),char(105),char(116), -char(105),char(111),char(110),char(65),char(110),char(100),char(82),char(97),char(100),char(105),char(117),char(115),char(0),char(98),char(116),char(77),char(117),char(108),char(116),char(105), -char(83),char(112),char(104),char(101),char(114),char(101),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(73),char(110), -char(116),char(73),char(110),char(100),char(101),char(120),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(83),char(104),char(111),char(114),char(116),char(73),char(110), -char(116),char(73),char(110),char(100),char(101),char(120),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(83),char(104),char(111),char(114),char(116),char(73),char(110), -char(116),char(73),char(110),char(100),char(101),char(120),char(84),char(114),char(105),char(112),char(108),char(101),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116), -char(67),char(104),char(97),char(114),char(73),char(110),char(100),char(101),char(120),char(84),char(114),char(105),char(112),char(108),char(101),char(116),char(68),char(97),char(116),char(97), -char(0),char(98),char(116),char(77),char(101),char(115),char(104),char(80),char(97),char(114),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(83),char(116), -char(114),char(105),char(100),char(105),char(110),char(103),char(77),char(101),char(115),char(104),char(73),char(110),char(116),char(101),char(114),char(102),char(97),char(99),char(101),char(68), -char(97),char(116),char(97),char(0),char(98),char(116),char(84),char(114),char(105),char(97),char(110),char(103),char(108),char(101),char(77),char(101),char(115),char(104),char(83),char(104), +char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(79),char(112),char(116),char(105),char(109),char(105),char(122),char(101), +char(100),char(66),char(118),char(104),char(78),char(111),char(100),char(101),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98), +char(116),char(81),char(117),char(97),char(110),char(116),char(105),char(122),char(101),char(100),char(66),char(118),char(104),char(78),char(111),char(100),char(101),char(68),char(97),char(116), +char(97),char(0),char(98),char(116),char(81),char(117),char(97),char(110),char(116),char(105),char(122),char(101),char(100),char(66),char(118),char(104),char(70),char(108),char(111),char(97), +char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(81),char(117),char(97),char(110),char(116),char(105),char(122),char(101),char(100),char(66),char(118),char(104), +char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(108),char(108),char(105),char(115),char(105), +char(111),char(110),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(83),char(116),char(97),char(116),char(105),char(99), +char(80),char(108),char(97),char(110),char(101),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(110), +char(118),char(101),char(120),char(73),char(110),char(116),char(101),char(114),char(110),char(97),char(108),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97), +char(0),char(98),char(116),char(80),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(65),char(110),char(100),char(82),char(97),char(100),char(105),char(117),char(115), +char(0),char(98),char(116),char(77),char(117),char(108),char(116),char(105),char(83),char(112),char(104),char(101),char(114),char(101),char(83),char(104),char(97),char(112),char(101),char(68), +char(97),char(116),char(97),char(0),char(98),char(116),char(73),char(110),char(116),char(73),char(110),char(100),char(101),char(120),char(68),char(97),char(116),char(97),char(0),char(98), +char(116),char(83),char(104),char(111),char(114),char(116),char(73),char(110),char(116),char(73),char(110),char(100),char(101),char(120),char(68),char(97),char(116),char(97),char(0),char(98), +char(116),char(83),char(104),char(111),char(114),char(116),char(73),char(110),char(116),char(73),char(110),char(100),char(101),char(120),char(84),char(114),char(105),char(112),char(108),char(101), +char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(104),char(97),char(114),char(73),char(110),char(100),char(101),char(120),char(84),char(114),char(105), +char(112),char(108),char(101),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(77),char(101),char(115),char(104),char(80),char(97),char(114),char(116),char(68), +char(97),char(116),char(97),char(0),char(98),char(116),char(83),char(116),char(114),char(105),char(100),char(105),char(110),char(103),char(77),char(101),char(115),char(104),char(73),char(110), +char(116),char(101),char(114),char(102),char(97),char(99),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(84),char(114),char(105),char(97),char(110),char(103), +char(108),char(101),char(77),char(101),char(115),char(104),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(84),char(114), +char(105),char(97),char(110),char(103),char(108),char(101),char(73),char(110),char(102),char(111),char(77),char(97),char(112),char(68),char(97),char(116),char(97),char(0),char(98),char(116), +char(83),char(99),char(97),char(108),char(101),char(100),char(84),char(114),char(105),char(97),char(110),char(103),char(108),char(101),char(77),char(101),char(115),char(104),char(83),char(104), +char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(109),char(112),char(111),char(117),char(110),char(100),char(83),char(104), +char(97),char(112),char(101),char(67),char(104),char(105),char(108),char(100),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(109),char(112),char(111), +char(117),char(110),char(100),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(121),char(108),char(105),char(110), +char(100),char(101),char(114),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(110),char(101),char(83), +char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(97),char(112),char(115),char(117),char(108),char(101),char(83),char(104), char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(84),char(114),char(105),char(97),char(110),char(103),char(108),char(101),char(73),char(110), -char(102),char(111),char(77),char(97),char(112),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(83),char(99),char(97),char(108),char(101),char(100),char(84),char(114), -char(105),char(97),char(110),char(103),char(108),char(101),char(77),char(101),char(115),char(104),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0), -char(98),char(116),char(67),char(111),char(109),char(112),char(111),char(117),char(110),char(100),char(83),char(104),char(97),char(112),char(101),char(67),char(104),char(105),char(108),char(100), -char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(109),char(112),char(111),char(117),char(110),char(100),char(83),char(104),char(97),char(112),char(101), -char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(121),char(108),char(105),char(110),char(100),char(101),char(114),char(83),char(104),char(97),char(112),char(101), -char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(110),char(101),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97), -char(0),char(98),char(116),char(67),char(97),char(112),char(115),char(117),char(108),char(101),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0), -char(98),char(116),char(84),char(114),char(105),char(97),char(110),char(103),char(108),char(101),char(73),char(110),char(102),char(111),char(68),char(97),char(116),char(97),char(0),char(98), -char(116),char(71),char(73),char(109),char(112),char(97),char(99),char(116),char(77),char(101),char(115),char(104),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116), -char(97),char(0),char(98),char(116),char(67),char(111),char(110),char(118),char(101),char(120),char(72),char(117),char(108),char(108),char(83),char(104),char(97),char(112),char(101),char(68), -char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(79),char(98),char(106),char(101),char(99), -char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(108),char(108),char(105),char(115), -char(105),char(111),char(110),char(79),char(98),char(106),char(101),char(99),char(116),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98), -char(116),char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(83),char(111),char(108),char(118),char(101),char(114),char(73),char(110),char(102),char(111),char(68),char(111), -char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(83),char(111), -char(108),char(118),char(101),char(114),char(73),char(110),char(102),char(111),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116), -char(68),char(121),char(110),char(97),char(109),char(105),char(99),char(115),char(87),char(111),char(114),char(108),char(100),char(68),char(111),char(117),char(98),char(108),char(101),char(68), -char(97),char(116),char(97),char(0),char(98),char(116),char(68),char(121),char(110),char(97),char(109),char(105),char(99),char(115),char(87),char(111),char(114),char(108),char(100),char(70), -char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(82),char(105),char(103),char(105),char(100),char(66),char(111),char(100),char(121), -char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(82),char(105),char(103),char(105),char(100),char(66),char(111),char(100), -char(121),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(110),char(115),char(116),char(114), -char(97),char(105),char(110),char(116),char(73),char(110),char(102),char(111),char(49),char(0),char(98),char(116),char(84),char(121),char(112),char(101),char(100),char(67),char(111),char(110), -char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(84), -char(121),char(112),char(101),char(100),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(98), -char(116),char(82),char(105),char(103),char(105),char(100),char(66),char(111),char(100),char(121),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(84),char(121),char(112), -char(101),char(100),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97), -char(116),char(97),char(0),char(98),char(116),char(80),char(111),char(105),char(110),char(116),char(50),char(80),char(111),char(105),char(110),char(116),char(67),char(111),char(110),char(115), -char(116),char(114),char(97),char(105),char(110),char(116),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(80),char(111), -char(105),char(110),char(116),char(50),char(80),char(111),char(105),char(110),char(116),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68), -char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(50),char(0),char(98),char(116),char(80),char(111),char(105),char(110),char(116),char(50),char(80), -char(111),char(105),char(110),char(116),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101), -char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(72),char(105),char(110),char(103),char(101),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105), -char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(72),char(105),char(110),char(103),char(101), -char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0), -char(98),char(116),char(72),char(105),char(110),char(103),char(101),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117), -char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(50),char(0),char(98),char(116),char(67),char(111),char(110),char(101),char(84),char(119),char(105),char(115),char(116), -char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97), -char(0),char(98),char(116),char(67),char(111),char(110),char(101),char(84),char(119),char(105),char(115),char(116),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105), -char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(71),char(101),char(110),char(101),char(114),char(105),char(99),char(54),char(68),char(111),char(102), -char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(71),char(101),char(110), -char(101),char(114),char(105),char(99),char(54),char(68),char(111),char(102),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111), -char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(50),char(0),char(98),char(116),char(71),char(101),char(110),char(101),char(114),char(105),char(99),char(54), -char(68),char(111),char(102),char(83),char(112),char(114),char(105),char(110),char(103),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68), -char(97),char(116),char(97),char(0),char(98),char(116),char(71),char(101),char(110),char(101),char(114),char(105),char(99),char(54),char(68),char(111),char(102),char(83),char(112),char(114), -char(105),char(110),char(103),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68), -char(97),char(116),char(97),char(50),char(0),char(98),char(116),char(71),char(101),char(110),char(101),char(114),char(105),char(99),char(54),char(68),char(111),char(102),char(83),char(112), -char(114),char(105),char(110),char(103),char(50),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0), -char(98),char(116),char(71),char(101),char(110),char(101),char(114),char(105),char(99),char(54),char(68),char(111),char(102),char(83),char(112),char(114),char(105),char(110),char(103),char(50), -char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97), -char(50),char(0),char(98),char(116),char(83),char(108),char(105),char(100),char(101),char(114),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116), -char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(83),char(108),char(105),char(100),char(101),char(114),char(67),char(111),char(110),char(115),char(116),char(114),char(97), -char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(71),char(101),char(97),char(114), -char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0), -char(98),char(116),char(71),char(101),char(97),char(114),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98), -char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(77),char(97),char(116),char(101),char(114), -char(105),char(97),char(108),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(78),char(111),char(100),char(101), -char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(76),char(105),char(110),char(107),char(68),char(97),char(116), -char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(70),char(97),char(99),char(101),char(68),char(97),char(116),char(97),char(0),char(83), -char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(84),char(101),char(116),char(114),char(97),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102), -char(116),char(82),char(105),char(103),char(105),char(100),char(65),char(110),char(99),char(104),char(111),char(114),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102), -char(116),char(66),char(111),char(100),char(121),char(67),char(111),char(110),char(102),char(105),char(103),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116), -char(66),char(111),char(100),char(121),char(80),char(111),char(115),char(101),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100), -char(121),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(83),char(111),char(102),char(116),char(66), -char(111),char(100),char(121),char(74),char(111),char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(83),char(111),char(102),char(116),char(66), -char(111),char(100),char(121),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(77),char(117),char(108),char(116),char(105), -char(66),char(111),char(100),char(121),char(76),char(105),char(110),char(107),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98), -char(116),char(77),char(117),char(108),char(116),char(105),char(66),char(111),char(100),char(121),char(76),char(105),char(110),char(107),char(70),char(108),char(111),char(97),char(116),char(68), -char(97),char(116),char(97),char(0),char(98),char(116),char(77),char(117),char(108),char(116),char(105),char(66),char(111),char(100),char(121),char(68),char(111),char(117),char(98),char(108), -char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(77),char(117),char(108),char(116),char(105),char(66),char(111),char(100),char(121),char(70),char(108),char(111), -char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(0),char(84),char(76),char(69),char(78),char(1),char(0),char(1),char(0),char(2),char(0),char(2),char(0), -char(4),char(0),char(4),char(0),char(4),char(0),char(4),char(0),char(8),char(0),char(0),char(0),char(16),char(0),char(48),char(0),char(16),char(0),char(16),char(0), -char(32),char(0),char(16),char(0),char(32),char(0),char(48),char(0),char(96),char(0),char(64),char(0),char(-128),char(0),char(20),char(0),char(48),char(0),char(80),char(0), -char(16),char(0),char(96),char(0),char(-112),char(0),char(16),char(0),char(56),char(0),char(56),char(0),char(20),char(0),char(72),char(0),char(4),char(0),char(4),char(0), -char(8),char(0),char(4),char(0),char(56),char(0),char(32),char(0),char(80),char(0),char(72),char(0),char(96),char(0),char(80),char(0),char(32),char(0),char(64),char(0), -char(64),char(0),char(64),char(0),char(16),char(0),char(72),char(0),char(80),char(0),char(-16),char(1),char(24),char(1),char(-104),char(0),char(88),char(0),char(-72),char(0), -char(104),char(0),char(0),char(2),char(-64),char(3),char(8),char(0),char(64),char(0),char(64),char(0),char(0),char(0),char(80),char(0),char(96),char(0),char(-112),char(0), +char(102),char(111),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(80),char(101),char(114),char(115),char(105),char(115),char(116),char(101),char(110),char(116),char(77), +char(97),char(110),char(105),char(102),char(111),char(108),char(100),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116), +char(67),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(79),char(98),char(106),char(101),char(99),char(116),char(68),char(111),char(117),char(98),char(108), +char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(80),char(101),char(114),char(115),char(105),char(115),char(116),char(101),char(110),char(116),char(77),char(97), +char(110),char(105),char(102),char(111),char(108),char(100),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111), +char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(79),char(98),char(106),char(101),char(99),char(116),char(70),char(108),char(111),char(97),char(116),char(68),char(97), +char(116),char(97),char(0),char(98),char(116),char(71),char(73),char(109),char(112),char(97),char(99),char(116),char(77),char(101),char(115),char(104),char(83),char(104),char(97),char(112), +char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(110),char(118),char(101),char(120),char(72),char(117),char(108),char(108),char(83),char(104), +char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(83),char(111),char(108), +char(118),char(101),char(114),char(73),char(110),char(102),char(111),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116), +char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(83),char(111),char(108),char(118),char(101),char(114),char(73),char(110),char(102),char(111),char(70),char(108),char(111), +char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(68),char(121),char(110),char(97),char(109),char(105),char(99),char(115),char(87),char(111),char(114), +char(108),char(100),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(68),char(121),char(110),char(97),char(109), +char(105),char(99),char(115),char(87),char(111),char(114),char(108),char(100),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116), +char(82),char(105),char(103),char(105),char(100),char(66),char(111),char(100),char(121),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98), +char(116),char(82),char(105),char(103),char(105),char(100),char(66),char(111),char(100),char(121),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97), +char(0),char(98),char(116),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(73),char(110),char(102),char(111),char(49),char(0),char(98), +char(116),char(84),char(121),char(112),char(101),char(100),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(70),char(108),char(111),char(97), +char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(84),char(121),char(112),char(101),char(100),char(67),char(111),char(110),char(115),char(116),char(114),char(97), +char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(82),char(105),char(103),char(105),char(100),char(66),char(111),char(100),char(121),char(68), +char(97),char(116),char(97),char(0),char(98),char(116),char(84),char(121),char(112),char(101),char(100),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110), +char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(80),char(111),char(105),char(110),char(116),char(50), +char(80),char(111),char(105),char(110),char(116),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(70),char(108),char(111),char(97),char(116), +char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(80),char(111),char(105),char(110),char(116),char(50),char(80),char(111),char(105),char(110),char(116),char(67),char(111), +char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(50),char(0), +char(98),char(116),char(80),char(111),char(105),char(110),char(116),char(50),char(80),char(111),char(105),char(110),char(116),char(67),char(111),char(110),char(115),char(116),char(114),char(97), +char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(72),char(105),char(110),char(103), +char(101),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116), +char(97),char(0),char(98),char(116),char(72),char(105),char(110),char(103),char(101),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(70), +char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(72),char(105),char(110),char(103),char(101),char(67),char(111),char(110),char(115), +char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(50),char(0),char(98),char(116), +char(67),char(111),char(110),char(101),char(84),char(119),char(105),char(115),char(116),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68), +char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(110),char(101),char(84),char(119),char(105),char(115), +char(116),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(71),char(101), +char(110),char(101),char(114),char(105),char(99),char(54),char(68),char(111),char(102),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68), +char(97),char(116),char(97),char(0),char(98),char(116),char(71),char(101),char(110),char(101),char(114),char(105),char(99),char(54),char(68),char(111),char(102),char(67),char(111),char(110), +char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(50),char(0),char(98), +char(116),char(71),char(101),char(110),char(101),char(114),char(105),char(99),char(54),char(68),char(111),char(102),char(83),char(112),char(114),char(105),char(110),char(103),char(67),char(111), +char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(71),char(101),char(110),char(101),char(114), +char(105),char(99),char(54),char(68),char(111),char(102),char(83),char(112),char(114),char(105),char(110),char(103),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105), +char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(50),char(0),char(98),char(116),char(71),char(101),char(110),char(101), +char(114),char(105),char(99),char(54),char(68),char(111),char(102),char(83),char(112),char(114),char(105),char(110),char(103),char(50),char(67),char(111),char(110),char(115),char(116),char(114), +char(97),char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(71),char(101),char(110),char(101),char(114),char(105),char(99),char(54),char(68), +char(111),char(102),char(83),char(112),char(114),char(105),char(110),char(103),char(50),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68), +char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(50),char(0),char(98),char(116),char(83),char(108),char(105),char(100),char(101),char(114),char(67), +char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(83),char(108),char(105),char(100), +char(101),char(114),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97), +char(116),char(97),char(0),char(98),char(116),char(71),char(101),char(97),char(114),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(70), +char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(71),char(101),char(97),char(114),char(67),char(111),char(110),char(115),char(116), +char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116), +char(66),char(111),char(100),char(121),char(77),char(97),char(116),char(101),char(114),char(105),char(97),char(108),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102), +char(116),char(66),char(111),char(100),char(121),char(78),char(111),char(100),char(101),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111), +char(100),char(121),char(76),char(105),char(110),char(107),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(70), +char(97),char(99),char(101),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(84),char(101),char(116),char(114), +char(97),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(82),char(105),char(103),char(105),char(100),char(65),char(110),char(99),char(104),char(111), +char(114),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(67),char(111),char(110),char(102),char(105),char(103), +char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(80),char(111),char(115),char(101),char(68),char(97),char(116), +char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(68),char(97),char(116), +char(97),char(0),char(98),char(116),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(74),char(111),char(105),char(110),char(116),char(68),char(97),char(116), +char(97),char(0),char(98),char(116),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116), +char(97),char(0),char(98),char(116),char(77),char(117),char(108),char(116),char(105),char(66),char(111),char(100),char(121),char(76),char(105),char(110),char(107),char(68),char(111),char(117), +char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(77),char(117),char(108),char(116),char(105),char(66),char(111),char(100),char(121),char(76), +char(105),char(110),char(107),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(77),char(117),char(108),char(116),char(105), +char(66),char(111),char(100),char(121),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(77),char(117),char(108), +char(116),char(105),char(66),char(111),char(100),char(121),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(77),char(117), +char(108),char(116),char(105),char(66),char(111),char(100),char(121),char(76),char(105),char(110),char(107),char(67),char(111),char(108),char(108),char(105),char(100),char(101),char(114),char(70), +char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(77),char(117),char(108),char(116),char(105),char(66),char(111),char(100),char(121), +char(76),char(105),char(110),char(107),char(67),char(111),char(108),char(108),char(105),char(100),char(101),char(114),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97), +char(116),char(97),char(0),char(0),char(84),char(76),char(69),char(78),char(1),char(0),char(1),char(0),char(2),char(0),char(2),char(0),char(4),char(0),char(4),char(0), +char(4),char(0),char(4),char(0),char(8),char(0),char(0),char(0),char(16),char(0),char(48),char(0),char(16),char(0),char(16),char(0),char(32),char(0),char(16),char(0), +char(32),char(0),char(48),char(0),char(96),char(0),char(64),char(0),char(-128),char(0),char(20),char(0),char(48),char(0),char(80),char(0),char(16),char(0),char(96),char(0), +char(-112),char(0),char(16),char(0),char(56),char(0),char(56),char(0),char(20),char(0),char(72),char(0),char(4),char(0),char(4),char(0),char(8),char(0),char(4),char(0), +char(56),char(0),char(32),char(0),char(80),char(0),char(72),char(0),char(96),char(0),char(80),char(0),char(32),char(0),char(64),char(0),char(64),char(0),char(64),char(0), +char(16),char(0),char(-8),char(5),char(-8),char(1),char(64),char(3),char(32),char(1),char(72),char(0),char(80),char(0),char(-104),char(0),char(88),char(0),char(-72),char(0), +char(104),char(0),char(8),char(2),char(-56),char(3),char(8),char(0),char(64),char(0),char(64),char(0),char(0),char(0),char(80),char(0),char(96),char(0),char(-112),char(0), char(-128),char(0),char(104),char(1),char(-24),char(0),char(-104),char(1),char(-120),char(1),char(-32),char(0),char(8),char(1),char(-40),char(1),char(104),char(1),char(-128),char(2), char(-112),char(2),char(-64),char(4),char(-40),char(0),char(120),char(1),char(104),char(0),char(-104),char(0),char(16),char(0),char(104),char(0),char(24),char(0),char(40),char(0), -char(104),char(0),char(96),char(0),char(104),char(0),char(-56),char(0),char(104),char(1),char(112),char(0),char(-24),char(1),char(0),char(3),char(-104),char(1),char(-48),char(0), -char(112),char(0),char(0),char(0),char(83),char(84),char(82),char(67),char(84),char(0),char(0),char(0),char(10),char(0),char(3),char(0),char(4),char(0),char(0),char(0), -char(4),char(0),char(1),char(0),char(9),char(0),char(2),char(0),char(11),char(0),char(3),char(0),char(10),char(0),char(3),char(0),char(10),char(0),char(4),char(0), -char(10),char(0),char(5),char(0),char(12),char(0),char(2),char(0),char(9),char(0),char(6),char(0),char(9),char(0),char(7),char(0),char(13),char(0),char(1),char(0), -char(7),char(0),char(8),char(0),char(14),char(0),char(1),char(0),char(8),char(0),char(8),char(0),char(15),char(0),char(1),char(0),char(7),char(0),char(8),char(0), -char(16),char(0),char(1),char(0),char(8),char(0),char(8),char(0),char(17),char(0),char(1),char(0),char(13),char(0),char(9),char(0),char(18),char(0),char(1),char(0), -char(14),char(0),char(9),char(0),char(19),char(0),char(2),char(0),char(17),char(0),char(10),char(0),char(13),char(0),char(11),char(0),char(20),char(0),char(2),char(0), -char(18),char(0),char(10),char(0),char(14),char(0),char(11),char(0),char(21),char(0),char(4),char(0),char(4),char(0),char(12),char(0),char(4),char(0),char(13),char(0), -char(2),char(0),char(14),char(0),char(2),char(0),char(15),char(0),char(22),char(0),char(6),char(0),char(13),char(0),char(16),char(0),char(13),char(0),char(17),char(0), -char(4),char(0),char(18),char(0),char(4),char(0),char(19),char(0),char(4),char(0),char(20),char(0),char(0),char(0),char(21),char(0),char(23),char(0),char(6),char(0), -char(14),char(0),char(16),char(0),char(14),char(0),char(17),char(0),char(4),char(0),char(18),char(0),char(4),char(0),char(19),char(0),char(4),char(0),char(20),char(0), -char(0),char(0),char(21),char(0),char(24),char(0),char(3),char(0),char(2),char(0),char(14),char(0),char(2),char(0),char(15),char(0),char(4),char(0),char(22),char(0), -char(25),char(0),char(12),char(0),char(13),char(0),char(23),char(0),char(13),char(0),char(24),char(0),char(13),char(0),char(25),char(0),char(4),char(0),char(26),char(0), -char(4),char(0),char(27),char(0),char(4),char(0),char(28),char(0),char(4),char(0),char(29),char(0),char(22),char(0),char(30),char(0),char(24),char(0),char(31),char(0), -char(21),char(0),char(32),char(0),char(4),char(0),char(33),char(0),char(4),char(0),char(34),char(0),char(26),char(0),char(12),char(0),char(14),char(0),char(23),char(0), -char(14),char(0),char(24),char(0),char(14),char(0),char(25),char(0),char(4),char(0),char(26),char(0),char(4),char(0),char(27),char(0),char(4),char(0),char(28),char(0), -char(4),char(0),char(29),char(0),char(23),char(0),char(30),char(0),char(24),char(0),char(31),char(0),char(4),char(0),char(33),char(0),char(4),char(0),char(34),char(0), -char(21),char(0),char(32),char(0),char(27),char(0),char(3),char(0),char(0),char(0),char(35),char(0),char(4),char(0),char(36),char(0),char(0),char(0),char(37),char(0), -char(28),char(0),char(5),char(0),char(27),char(0),char(38),char(0),char(13),char(0),char(39),char(0),char(13),char(0),char(40),char(0),char(7),char(0),char(41),char(0), -char(0),char(0),char(21),char(0),char(29),char(0),char(5),char(0),char(27),char(0),char(38),char(0),char(13),char(0),char(39),char(0),char(13),char(0),char(42),char(0), -char(7),char(0),char(43),char(0),char(4),char(0),char(44),char(0),char(30),char(0),char(2),char(0),char(13),char(0),char(45),char(0),char(7),char(0),char(46),char(0), -char(31),char(0),char(4),char(0),char(29),char(0),char(47),char(0),char(30),char(0),char(48),char(0),char(4),char(0),char(49),char(0),char(0),char(0),char(37),char(0), -char(32),char(0),char(1),char(0),char(4),char(0),char(50),char(0),char(33),char(0),char(2),char(0),char(2),char(0),char(50),char(0),char(0),char(0),char(51),char(0), -char(34),char(0),char(2),char(0),char(2),char(0),char(52),char(0),char(0),char(0),char(51),char(0),char(35),char(0),char(2),char(0),char(0),char(0),char(52),char(0), -char(0),char(0),char(53),char(0),char(36),char(0),char(8),char(0),char(13),char(0),char(54),char(0),char(14),char(0),char(55),char(0),char(32),char(0),char(56),char(0), -char(34),char(0),char(57),char(0),char(35),char(0),char(58),char(0),char(33),char(0),char(59),char(0),char(4),char(0),char(60),char(0),char(4),char(0),char(61),char(0), -char(37),char(0),char(4),char(0),char(36),char(0),char(62),char(0),char(13),char(0),char(63),char(0),char(4),char(0),char(64),char(0),char(0),char(0),char(37),char(0), -char(38),char(0),char(7),char(0),char(27),char(0),char(38),char(0),char(37),char(0),char(65),char(0),char(25),char(0),char(66),char(0),char(26),char(0),char(67),char(0), -char(39),char(0),char(68),char(0),char(7),char(0),char(43),char(0),char(0),char(0),char(69),char(0),char(40),char(0),char(2),char(0),char(38),char(0),char(70),char(0), -char(13),char(0),char(39),char(0),char(41),char(0),char(4),char(0),char(19),char(0),char(71),char(0),char(27),char(0),char(72),char(0),char(4),char(0),char(73),char(0), -char(7),char(0),char(74),char(0),char(42),char(0),char(4),char(0),char(27),char(0),char(38),char(0),char(41),char(0),char(75),char(0),char(4),char(0),char(76),char(0), -char(7),char(0),char(43),char(0),char(43),char(0),char(3),char(0),char(29),char(0),char(47),char(0),char(4),char(0),char(77),char(0),char(0),char(0),char(37),char(0), -char(44),char(0),char(3),char(0),char(29),char(0),char(47),char(0),char(4),char(0),char(78),char(0),char(0),char(0),char(37),char(0),char(45),char(0),char(3),char(0), -char(29),char(0),char(47),char(0),char(4),char(0),char(77),char(0),char(0),char(0),char(37),char(0),char(46),char(0),char(4),char(0),char(4),char(0),char(79),char(0), -char(7),char(0),char(80),char(0),char(7),char(0),char(81),char(0),char(7),char(0),char(82),char(0),char(39),char(0),char(14),char(0),char(4),char(0),char(83),char(0), -char(4),char(0),char(84),char(0),char(46),char(0),char(85),char(0),char(4),char(0),char(86),char(0),char(7),char(0),char(87),char(0),char(7),char(0),char(88),char(0), -char(7),char(0),char(89),char(0),char(7),char(0),char(90),char(0),char(7),char(0),char(91),char(0),char(4),char(0),char(92),char(0),char(4),char(0),char(93),char(0), -char(4),char(0),char(94),char(0),char(4),char(0),char(95),char(0),char(0),char(0),char(37),char(0),char(47),char(0),char(5),char(0),char(27),char(0),char(38),char(0), -char(37),char(0),char(65),char(0),char(13),char(0),char(39),char(0),char(7),char(0),char(43),char(0),char(4),char(0),char(96),char(0),char(48),char(0),char(5),char(0), -char(29),char(0),char(47),char(0),char(13),char(0),char(97),char(0),char(14),char(0),char(98),char(0),char(4),char(0),char(99),char(0),char(0),char(0),char(100),char(0), -char(49),char(0),char(27),char(0),char(9),char(0),char(101),char(0),char(9),char(0),char(102),char(0),char(27),char(0),char(103),char(0),char(0),char(0),char(35),char(0), -char(20),char(0),char(104),char(0),char(20),char(0),char(105),char(0),char(14),char(0),char(106),char(0),char(14),char(0),char(107),char(0),char(14),char(0),char(108),char(0), -char(8),char(0),char(109),char(0),char(8),char(0),char(110),char(0),char(8),char(0),char(111),char(0),char(8),char(0),char(112),char(0),char(8),char(0),char(113),char(0), -char(8),char(0),char(114),char(0),char(8),char(0),char(115),char(0),char(8),char(0),char(116),char(0),char(8),char(0),char(117),char(0),char(8),char(0),char(118),char(0), -char(4),char(0),char(119),char(0),char(4),char(0),char(120),char(0),char(4),char(0),char(121),char(0),char(4),char(0),char(122),char(0),char(4),char(0),char(123),char(0), -char(4),char(0),char(124),char(0),char(4),char(0),char(125),char(0),char(0),char(0),char(37),char(0),char(50),char(0),char(27),char(0),char(9),char(0),char(101),char(0), -char(9),char(0),char(102),char(0),char(27),char(0),char(103),char(0),char(0),char(0),char(35),char(0),char(19),char(0),char(104),char(0),char(19),char(0),char(105),char(0), -char(13),char(0),char(106),char(0),char(13),char(0),char(107),char(0),char(13),char(0),char(108),char(0),char(7),char(0),char(109),char(0),char(7),char(0),char(110),char(0), -char(7),char(0),char(111),char(0),char(7),char(0),char(112),char(0),char(7),char(0),char(113),char(0),char(7),char(0),char(114),char(0),char(7),char(0),char(115),char(0), -char(7),char(0),char(116),char(0),char(7),char(0),char(117),char(0),char(7),char(0),char(118),char(0),char(4),char(0),char(119),char(0),char(4),char(0),char(120),char(0), -char(4),char(0),char(121),char(0),char(4),char(0),char(122),char(0),char(4),char(0),char(123),char(0),char(4),char(0),char(124),char(0),char(4),char(0),char(125),char(0), -char(0),char(0),char(37),char(0),char(51),char(0),char(22),char(0),char(8),char(0),char(126),char(0),char(8),char(0),char(127),char(0),char(8),char(0),char(111),char(0), -char(8),char(0),char(-128),char(0),char(8),char(0),char(115),char(0),char(8),char(0),char(-127),char(0),char(8),char(0),char(-126),char(0),char(8),char(0),char(-125),char(0), -char(8),char(0),char(-124),char(0),char(8),char(0),char(-123),char(0),char(8),char(0),char(-122),char(0),char(8),char(0),char(-121),char(0),char(8),char(0),char(-120),char(0), -char(8),char(0),char(-119),char(0),char(8),char(0),char(-118),char(0),char(8),char(0),char(-117),char(0),char(4),char(0),char(-116),char(0),char(4),char(0),char(-115),char(0), -char(4),char(0),char(-114),char(0),char(4),char(0),char(-113),char(0),char(4),char(0),char(-112),char(0),char(0),char(0),char(37),char(0),char(52),char(0),char(22),char(0), -char(7),char(0),char(126),char(0),char(7),char(0),char(127),char(0),char(7),char(0),char(111),char(0),char(7),char(0),char(-128),char(0),char(7),char(0),char(115),char(0), -char(7),char(0),char(-127),char(0),char(7),char(0),char(-126),char(0),char(7),char(0),char(-125),char(0),char(7),char(0),char(-124),char(0),char(7),char(0),char(-123),char(0), -char(7),char(0),char(-122),char(0),char(7),char(0),char(-121),char(0),char(7),char(0),char(-120),char(0),char(7),char(0),char(-119),char(0),char(7),char(0),char(-118),char(0), -char(7),char(0),char(-117),char(0),char(4),char(0),char(-116),char(0),char(4),char(0),char(-115),char(0),char(4),char(0),char(-114),char(0),char(4),char(0),char(-113),char(0), -char(4),char(0),char(-112),char(0),char(0),char(0),char(37),char(0),char(53),char(0),char(2),char(0),char(51),char(0),char(-111),char(0),char(14),char(0),char(-110),char(0), -char(54),char(0),char(2),char(0),char(52),char(0),char(-111),char(0),char(13),char(0),char(-110),char(0),char(55),char(0),char(21),char(0),char(50),char(0),char(-109),char(0), -char(17),char(0),char(-108),char(0),char(13),char(0),char(-107),char(0),char(13),char(0),char(-106),char(0),char(13),char(0),char(-105),char(0),char(13),char(0),char(-104),char(0), -char(13),char(0),char(-110),char(0),char(13),char(0),char(-103),char(0),char(13),char(0),char(-102),char(0),char(13),char(0),char(-101),char(0),char(13),char(0),char(-100),char(0), -char(7),char(0),char(-99),char(0),char(7),char(0),char(-98),char(0),char(7),char(0),char(-97),char(0),char(7),char(0),char(-96),char(0),char(7),char(0),char(-95),char(0), -char(7),char(0),char(-94),char(0),char(7),char(0),char(-93),char(0),char(7),char(0),char(-92),char(0),char(7),char(0),char(-91),char(0),char(4),char(0),char(-90),char(0), -char(56),char(0),char(22),char(0),char(49),char(0),char(-109),char(0),char(18),char(0),char(-108),char(0),char(14),char(0),char(-107),char(0),char(14),char(0),char(-106),char(0), -char(14),char(0),char(-105),char(0),char(14),char(0),char(-104),char(0),char(14),char(0),char(-110),char(0),char(14),char(0),char(-103),char(0),char(14),char(0),char(-102),char(0), -char(14),char(0),char(-101),char(0),char(14),char(0),char(-100),char(0),char(8),char(0),char(-99),char(0),char(8),char(0),char(-98),char(0),char(8),char(0),char(-97),char(0), -char(8),char(0),char(-96),char(0),char(8),char(0),char(-95),char(0),char(8),char(0),char(-94),char(0),char(8),char(0),char(-93),char(0),char(8),char(0),char(-92),char(0), -char(8),char(0),char(-91),char(0),char(4),char(0),char(-90),char(0),char(0),char(0),char(37),char(0),char(57),char(0),char(2),char(0),char(4),char(0),char(-89),char(0), -char(4),char(0),char(-88),char(0),char(58),char(0),char(13),char(0),char(55),char(0),char(-87),char(0),char(55),char(0),char(-86),char(0),char(0),char(0),char(35),char(0), -char(4),char(0),char(-85),char(0),char(4),char(0),char(-84),char(0),char(4),char(0),char(-83),char(0),char(4),char(0),char(-82),char(0),char(7),char(0),char(-81),char(0), -char(7),char(0),char(-80),char(0),char(4),char(0),char(-79),char(0),char(4),char(0),char(-78),char(0),char(7),char(0),char(-77),char(0),char(4),char(0),char(-76),char(0), -char(59),char(0),char(13),char(0),char(60),char(0),char(-87),char(0),char(60),char(0),char(-86),char(0),char(0),char(0),char(35),char(0),char(4),char(0),char(-85),char(0), -char(4),char(0),char(-84),char(0),char(4),char(0),char(-83),char(0),char(4),char(0),char(-82),char(0),char(7),char(0),char(-81),char(0),char(7),char(0),char(-80),char(0), -char(4),char(0),char(-79),char(0),char(4),char(0),char(-78),char(0),char(7),char(0),char(-77),char(0),char(4),char(0),char(-76),char(0),char(61),char(0),char(14),char(0), -char(56),char(0),char(-87),char(0),char(56),char(0),char(-86),char(0),char(0),char(0),char(35),char(0),char(4),char(0),char(-85),char(0),char(4),char(0),char(-84),char(0), -char(4),char(0),char(-83),char(0),char(4),char(0),char(-82),char(0),char(8),char(0),char(-81),char(0),char(8),char(0),char(-80),char(0),char(4),char(0),char(-79),char(0), -char(4),char(0),char(-78),char(0),char(8),char(0),char(-77),char(0),char(4),char(0),char(-76),char(0),char(0),char(0),char(-75),char(0),char(62),char(0),char(3),char(0), -char(59),char(0),char(-74),char(0),char(13),char(0),char(-73),char(0),char(13),char(0),char(-72),char(0),char(63),char(0),char(3),char(0),char(61),char(0),char(-74),char(0), -char(14),char(0),char(-73),char(0),char(14),char(0),char(-72),char(0),char(64),char(0),char(3),char(0),char(59),char(0),char(-74),char(0),char(14),char(0),char(-73),char(0), -char(14),char(0),char(-72),char(0),char(65),char(0),char(13),char(0),char(59),char(0),char(-74),char(0),char(20),char(0),char(-71),char(0),char(20),char(0),char(-70),char(0), -char(4),char(0),char(-69),char(0),char(4),char(0),char(-68),char(0),char(4),char(0),char(-67),char(0),char(7),char(0),char(-66),char(0),char(7),char(0),char(-65),char(0), -char(7),char(0),char(-64),char(0),char(7),char(0),char(-63),char(0),char(7),char(0),char(-62),char(0),char(7),char(0),char(-61),char(0),char(7),char(0),char(-60),char(0), -char(66),char(0),char(13),char(0),char(59),char(0),char(-74),char(0),char(19),char(0),char(-71),char(0),char(19),char(0),char(-70),char(0),char(4),char(0),char(-69),char(0), -char(4),char(0),char(-68),char(0),char(4),char(0),char(-67),char(0),char(7),char(0),char(-66),char(0),char(7),char(0),char(-65),char(0),char(7),char(0),char(-64),char(0), -char(7),char(0),char(-63),char(0),char(7),char(0),char(-62),char(0),char(7),char(0),char(-61),char(0),char(7),char(0),char(-60),char(0),char(67),char(0),char(14),char(0), -char(61),char(0),char(-74),char(0),char(20),char(0),char(-71),char(0),char(20),char(0),char(-70),char(0),char(4),char(0),char(-69),char(0),char(4),char(0),char(-68),char(0), -char(4),char(0),char(-67),char(0),char(8),char(0),char(-66),char(0),char(8),char(0),char(-65),char(0),char(8),char(0),char(-64),char(0),char(8),char(0),char(-63),char(0), -char(8),char(0),char(-62),char(0),char(8),char(0),char(-61),char(0),char(8),char(0),char(-60),char(0),char(0),char(0),char(-59),char(0),char(68),char(0),char(10),char(0), -char(61),char(0),char(-74),char(0),char(20),char(0),char(-71),char(0),char(20),char(0),char(-70),char(0),char(8),char(0),char(-58),char(0),char(8),char(0),char(-57),char(0), -char(8),char(0),char(-56),char(0),char(8),char(0),char(-62),char(0),char(8),char(0),char(-61),char(0),char(8),char(0),char(-60),char(0),char(8),char(0),char(127),char(0), -char(69),char(0),char(11),char(0),char(59),char(0),char(-74),char(0),char(19),char(0),char(-71),char(0),char(19),char(0),char(-70),char(0),char(7),char(0),char(-58),char(0), -char(7),char(0),char(-57),char(0),char(7),char(0),char(-56),char(0),char(7),char(0),char(-62),char(0),char(7),char(0),char(-61),char(0),char(7),char(0),char(-60),char(0), -char(7),char(0),char(127),char(0),char(0),char(0),char(21),char(0),char(70),char(0),char(9),char(0),char(59),char(0),char(-74),char(0),char(19),char(0),char(-71),char(0), -char(19),char(0),char(-70),char(0),char(13),char(0),char(-55),char(0),char(13),char(0),char(-54),char(0),char(13),char(0),char(-53),char(0),char(13),char(0),char(-52),char(0), -char(4),char(0),char(-51),char(0),char(4),char(0),char(-50),char(0),char(71),char(0),char(9),char(0),char(61),char(0),char(-74),char(0),char(20),char(0),char(-71),char(0), -char(20),char(0),char(-70),char(0),char(14),char(0),char(-55),char(0),char(14),char(0),char(-54),char(0),char(14),char(0),char(-53),char(0),char(14),char(0),char(-52),char(0), -char(4),char(0),char(-51),char(0),char(4),char(0),char(-50),char(0),char(72),char(0),char(5),char(0),char(70),char(0),char(-49),char(0),char(4),char(0),char(-48),char(0), -char(7),char(0),char(-47),char(0),char(7),char(0),char(-46),char(0),char(7),char(0),char(-45),char(0),char(73),char(0),char(5),char(0),char(71),char(0),char(-49),char(0), -char(4),char(0),char(-48),char(0),char(8),char(0),char(-47),char(0),char(8),char(0),char(-46),char(0),char(8),char(0),char(-45),char(0),char(74),char(0),char(41),char(0), -char(59),char(0),char(-74),char(0),char(19),char(0),char(-71),char(0),char(19),char(0),char(-70),char(0),char(13),char(0),char(-55),char(0),char(13),char(0),char(-54),char(0), -char(13),char(0),char(-44),char(0),char(13),char(0),char(-43),char(0),char(13),char(0),char(-42),char(0),char(13),char(0),char(-41),char(0),char(13),char(0),char(-40),char(0), -char(13),char(0),char(-39),char(0),char(13),char(0),char(-38),char(0),char(13),char(0),char(-37),char(0),char(13),char(0),char(-36),char(0),char(13),char(0),char(-35),char(0), -char(13),char(0),char(-34),char(0),char(0),char(0),char(-33),char(0),char(0),char(0),char(-32),char(0),char(0),char(0),char(-31),char(0),char(0),char(0),char(-30),char(0), -char(0),char(0),char(-29),char(0),char(0),char(0),char(-59),char(0),char(13),char(0),char(-53),char(0),char(13),char(0),char(-52),char(0),char(13),char(0),char(-28),char(0), -char(13),char(0),char(-27),char(0),char(13),char(0),char(-26),char(0),char(13),char(0),char(-25),char(0),char(13),char(0),char(-24),char(0),char(13),char(0),char(-23),char(0), -char(13),char(0),char(-22),char(0),char(13),char(0),char(-21),char(0),char(13),char(0),char(-20),char(0),char(13),char(0),char(-19),char(0),char(13),char(0),char(-18),char(0), -char(0),char(0),char(-17),char(0),char(0),char(0),char(-16),char(0),char(0),char(0),char(-15),char(0),char(0),char(0),char(-14),char(0),char(0),char(0),char(-13),char(0), -char(4),char(0),char(-12),char(0),char(75),char(0),char(41),char(0),char(61),char(0),char(-74),char(0),char(20),char(0),char(-71),char(0),char(20),char(0),char(-70),char(0), -char(14),char(0),char(-55),char(0),char(14),char(0),char(-54),char(0),char(14),char(0),char(-44),char(0),char(14),char(0),char(-43),char(0),char(14),char(0),char(-42),char(0), -char(14),char(0),char(-41),char(0),char(14),char(0),char(-40),char(0),char(14),char(0),char(-39),char(0),char(14),char(0),char(-38),char(0),char(14),char(0),char(-37),char(0), -char(14),char(0),char(-36),char(0),char(14),char(0),char(-35),char(0),char(14),char(0),char(-34),char(0),char(0),char(0),char(-33),char(0),char(0),char(0),char(-32),char(0), -char(0),char(0),char(-31),char(0),char(0),char(0),char(-30),char(0),char(0),char(0),char(-29),char(0),char(0),char(0),char(-59),char(0),char(14),char(0),char(-53),char(0), -char(14),char(0),char(-52),char(0),char(14),char(0),char(-28),char(0),char(14),char(0),char(-27),char(0),char(14),char(0),char(-26),char(0),char(14),char(0),char(-25),char(0), -char(14),char(0),char(-24),char(0),char(14),char(0),char(-23),char(0),char(14),char(0),char(-22),char(0),char(14),char(0),char(-21),char(0),char(14),char(0),char(-20),char(0), -char(14),char(0),char(-19),char(0),char(14),char(0),char(-18),char(0),char(0),char(0),char(-17),char(0),char(0),char(0),char(-16),char(0),char(0),char(0),char(-15),char(0), -char(0),char(0),char(-14),char(0),char(0),char(0),char(-13),char(0),char(4),char(0),char(-12),char(0),char(76),char(0),char(9),char(0),char(59),char(0),char(-74),char(0), -char(19),char(0),char(-71),char(0),char(19),char(0),char(-70),char(0),char(7),char(0),char(-55),char(0),char(7),char(0),char(-54),char(0),char(7),char(0),char(-53),char(0), -char(7),char(0),char(-52),char(0),char(4),char(0),char(-51),char(0),char(4),char(0),char(-50),char(0),char(77),char(0),char(9),char(0),char(61),char(0),char(-74),char(0), -char(20),char(0),char(-71),char(0),char(20),char(0),char(-70),char(0),char(8),char(0),char(-55),char(0),char(8),char(0),char(-54),char(0),char(8),char(0),char(-53),char(0), -char(8),char(0),char(-52),char(0),char(4),char(0),char(-51),char(0),char(4),char(0),char(-50),char(0),char(78),char(0),char(5),char(0),char(58),char(0),char(-74),char(0), -char(13),char(0),char(-11),char(0),char(13),char(0),char(-10),char(0),char(7),char(0),char(-9),char(0),char(0),char(0),char(37),char(0),char(79),char(0),char(4),char(0), -char(61),char(0),char(-74),char(0),char(14),char(0),char(-11),char(0),char(14),char(0),char(-10),char(0),char(8),char(0),char(-9),char(0),char(80),char(0),char(4),char(0), -char(7),char(0),char(-8),char(0),char(7),char(0),char(-7),char(0),char(7),char(0),char(-6),char(0),char(4),char(0),char(79),char(0),char(81),char(0),char(10),char(0), -char(80),char(0),char(-5),char(0),char(13),char(0),char(-4),char(0),char(13),char(0),char(-3),char(0),char(13),char(0),char(-2),char(0),char(13),char(0),char(-1),char(0), -char(13),char(0),char(0),char(1),char(7),char(0),char(-99),char(0),char(7),char(0),char(1),char(1),char(4),char(0),char(2),char(1),char(4),char(0),char(53),char(0), -char(82),char(0),char(4),char(0),char(80),char(0),char(-5),char(0),char(4),char(0),char(3),char(1),char(7),char(0),char(4),char(1),char(4),char(0),char(5),char(1), -char(83),char(0),char(4),char(0),char(13),char(0),char(0),char(1),char(80),char(0),char(-5),char(0),char(4),char(0),char(6),char(1),char(7),char(0),char(7),char(1), -char(84),char(0),char(7),char(0),char(13),char(0),char(8),char(1),char(80),char(0),char(-5),char(0),char(4),char(0),char(9),char(1),char(7),char(0),char(10),char(1), -char(7),char(0),char(11),char(1),char(7),char(0),char(12),char(1),char(4),char(0),char(53),char(0),char(85),char(0),char(6),char(0),char(17),char(0),char(13),char(1), -char(13),char(0),char(11),char(1),char(13),char(0),char(14),char(1),char(60),char(0),char(15),char(1),char(4),char(0),char(16),char(1),char(7),char(0),char(12),char(1), -char(86),char(0),char(26),char(0),char(4),char(0),char(17),char(1),char(7),char(0),char(18),char(1),char(7),char(0),char(127),char(0),char(7),char(0),char(19),char(1), -char(7),char(0),char(20),char(1),char(7),char(0),char(21),char(1),char(7),char(0),char(22),char(1),char(7),char(0),char(23),char(1),char(7),char(0),char(24),char(1), -char(7),char(0),char(25),char(1),char(7),char(0),char(26),char(1),char(7),char(0),char(27),char(1),char(7),char(0),char(28),char(1),char(7),char(0),char(29),char(1), -char(7),char(0),char(30),char(1),char(7),char(0),char(31),char(1),char(7),char(0),char(32),char(1),char(7),char(0),char(33),char(1),char(7),char(0),char(34),char(1), -char(7),char(0),char(35),char(1),char(7),char(0),char(36),char(1),char(4),char(0),char(37),char(1),char(4),char(0),char(38),char(1),char(4),char(0),char(39),char(1), -char(4),char(0),char(40),char(1),char(4),char(0),char(120),char(0),char(87),char(0),char(12),char(0),char(17),char(0),char(41),char(1),char(17),char(0),char(42),char(1), -char(17),char(0),char(43),char(1),char(13),char(0),char(44),char(1),char(13),char(0),char(45),char(1),char(7),char(0),char(46),char(1),char(4),char(0),char(47),char(1), -char(4),char(0),char(48),char(1),char(4),char(0),char(49),char(1),char(4),char(0),char(50),char(1),char(7),char(0),char(10),char(1),char(4),char(0),char(53),char(0), -char(88),char(0),char(27),char(0),char(19),char(0),char(51),char(1),char(17),char(0),char(52),char(1),char(17),char(0),char(53),char(1),char(13),char(0),char(44),char(1), -char(13),char(0),char(54),char(1),char(13),char(0),char(55),char(1),char(13),char(0),char(56),char(1),char(13),char(0),char(57),char(1),char(13),char(0),char(58),char(1), -char(4),char(0),char(59),char(1),char(7),char(0),char(60),char(1),char(4),char(0),char(61),char(1),char(4),char(0),char(62),char(1),char(4),char(0),char(63),char(1), -char(7),char(0),char(64),char(1),char(7),char(0),char(65),char(1),char(4),char(0),char(66),char(1),char(4),char(0),char(67),char(1),char(7),char(0),char(68),char(1), -char(7),char(0),char(69),char(1),char(7),char(0),char(70),char(1),char(7),char(0),char(71),char(1),char(7),char(0),char(72),char(1),char(7),char(0),char(73),char(1), -char(4),char(0),char(74),char(1),char(4),char(0),char(75),char(1),char(4),char(0),char(76),char(1),char(89),char(0),char(12),char(0),char(9),char(0),char(77),char(1), -char(9),char(0),char(78),char(1),char(13),char(0),char(79),char(1),char(7),char(0),char(80),char(1),char(7),char(0),char(-125),char(0),char(7),char(0),char(81),char(1), -char(4),char(0),char(82),char(1),char(13),char(0),char(83),char(1),char(4),char(0),char(84),char(1),char(4),char(0),char(85),char(1),char(4),char(0),char(86),char(1), -char(4),char(0),char(53),char(0),char(90),char(0),char(19),char(0),char(50),char(0),char(-109),char(0),char(87),char(0),char(87),char(1),char(80),char(0),char(88),char(1), -char(81),char(0),char(89),char(1),char(82),char(0),char(90),char(1),char(83),char(0),char(91),char(1),char(84),char(0),char(92),char(1),char(85),char(0),char(93),char(1), -char(88),char(0),char(94),char(1),char(89),char(0),char(95),char(1),char(4),char(0),char(96),char(1),char(4),char(0),char(62),char(1),char(4),char(0),char(97),char(1), -char(4),char(0),char(98),char(1),char(4),char(0),char(99),char(1),char(4),char(0),char(100),char(1),char(4),char(0),char(101),char(1),char(4),char(0),char(102),char(1), -char(86),char(0),char(103),char(1),char(91),char(0),char(24),char(0),char(16),char(0),char(104),char(1),char(14),char(0),char(105),char(1),char(14),char(0),char(106),char(1), -char(14),char(0),char(107),char(1),char(14),char(0),char(108),char(1),char(14),char(0),char(109),char(1),char(8),char(0),char(110),char(1),char(4),char(0),char(111),char(1), -char(4),char(0),char(86),char(1),char(4),char(0),char(112),char(1),char(4),char(0),char(113),char(1),char(8),char(0),char(114),char(1),char(8),char(0),char(115),char(1), -char(8),char(0),char(116),char(1),char(8),char(0),char(117),char(1),char(8),char(0),char(118),char(1),char(8),char(0),char(119),char(1),char(8),char(0),char(120),char(1), -char(8),char(0),char(121),char(1),char(8),char(0),char(122),char(1),char(0),char(0),char(123),char(1),char(0),char(0),char(124),char(1),char(49),char(0),char(125),char(1), -char(0),char(0),char(126),char(1),char(92),char(0),char(24),char(0),char(15),char(0),char(104),char(1),char(13),char(0),char(105),char(1),char(13),char(0),char(106),char(1), -char(13),char(0),char(107),char(1),char(13),char(0),char(108),char(1),char(13),char(0),char(109),char(1),char(4),char(0),char(112),char(1),char(7),char(0),char(110),char(1), -char(4),char(0),char(111),char(1),char(4),char(0),char(86),char(1),char(7),char(0),char(114),char(1),char(7),char(0),char(115),char(1),char(7),char(0),char(116),char(1), -char(4),char(0),char(113),char(1),char(7),char(0),char(117),char(1),char(7),char(0),char(118),char(1),char(7),char(0),char(119),char(1),char(7),char(0),char(120),char(1), -char(7),char(0),char(121),char(1),char(7),char(0),char(122),char(1),char(0),char(0),char(123),char(1),char(0),char(0),char(124),char(1),char(50),char(0),char(125),char(1), -char(0),char(0),char(126),char(1),char(93),char(0),char(9),char(0),char(20),char(0),char(127),char(1),char(14),char(0),char(-128),char(1),char(8),char(0),char(-127),char(1), -char(0),char(0),char(-126),char(1),char(91),char(0),char(90),char(1),char(49),char(0),char(-125),char(1),char(0),char(0),char(126),char(1),char(4),char(0),char(97),char(1), -char(0),char(0),char(37),char(0),char(94),char(0),char(7),char(0),char(0),char(0),char(-126),char(1),char(92),char(0),char(90),char(1),char(50),char(0),char(-125),char(1), -char(19),char(0),char(127),char(1),char(13),char(0),char(-128),char(1),char(7),char(0),char(-127),char(1),char(4),char(0),char(97),char(1),}; +char(104),char(0),char(96),char(0),char(104),char(0),char(-56),char(0),char(104),char(1),char(112),char(0),char(-16),char(1),char(-128),char(3),char(-40),char(1),char(-56),char(0), +char(112),char(0),char(48),char(1),char(8),char(2),char(0),char(0),char(83),char(84),char(82),char(67),char(88),char(0),char(0),char(0),char(10),char(0),char(3),char(0), +char(4),char(0),char(0),char(0),char(4),char(0),char(1),char(0),char(9),char(0),char(2),char(0),char(11),char(0),char(3),char(0),char(10),char(0),char(3),char(0), +char(10),char(0),char(4),char(0),char(10),char(0),char(5),char(0),char(12),char(0),char(2),char(0),char(9),char(0),char(6),char(0),char(9),char(0),char(7),char(0), +char(13),char(0),char(1),char(0),char(7),char(0),char(8),char(0),char(14),char(0),char(1),char(0),char(8),char(0),char(8),char(0),char(15),char(0),char(1),char(0), +char(7),char(0),char(8),char(0),char(16),char(0),char(1),char(0),char(8),char(0),char(8),char(0),char(17),char(0),char(1),char(0),char(13),char(0),char(9),char(0), +char(18),char(0),char(1),char(0),char(14),char(0),char(9),char(0),char(19),char(0),char(2),char(0),char(17),char(0),char(10),char(0),char(13),char(0),char(11),char(0), +char(20),char(0),char(2),char(0),char(18),char(0),char(10),char(0),char(14),char(0),char(11),char(0),char(21),char(0),char(4),char(0),char(4),char(0),char(12),char(0), +char(4),char(0),char(13),char(0),char(2),char(0),char(14),char(0),char(2),char(0),char(15),char(0),char(22),char(0),char(6),char(0),char(13),char(0),char(16),char(0), +char(13),char(0),char(17),char(0),char(4),char(0),char(18),char(0),char(4),char(0),char(19),char(0),char(4),char(0),char(20),char(0),char(0),char(0),char(21),char(0), +char(23),char(0),char(6),char(0),char(14),char(0),char(16),char(0),char(14),char(0),char(17),char(0),char(4),char(0),char(18),char(0),char(4),char(0),char(19),char(0), +char(4),char(0),char(20),char(0),char(0),char(0),char(21),char(0),char(24),char(0),char(3),char(0),char(2),char(0),char(14),char(0),char(2),char(0),char(15),char(0), +char(4),char(0),char(22),char(0),char(25),char(0),char(12),char(0),char(13),char(0),char(23),char(0),char(13),char(0),char(24),char(0),char(13),char(0),char(25),char(0), +char(4),char(0),char(26),char(0),char(4),char(0),char(27),char(0),char(4),char(0),char(28),char(0),char(4),char(0),char(29),char(0),char(22),char(0),char(30),char(0), +char(24),char(0),char(31),char(0),char(21),char(0),char(32),char(0),char(4),char(0),char(33),char(0),char(4),char(0),char(34),char(0),char(26),char(0),char(12),char(0), +char(14),char(0),char(23),char(0),char(14),char(0),char(24),char(0),char(14),char(0),char(25),char(0),char(4),char(0),char(26),char(0),char(4),char(0),char(27),char(0), +char(4),char(0),char(28),char(0),char(4),char(0),char(29),char(0),char(23),char(0),char(30),char(0),char(24),char(0),char(31),char(0),char(4),char(0),char(33),char(0), +char(4),char(0),char(34),char(0),char(21),char(0),char(32),char(0),char(27),char(0),char(3),char(0),char(0),char(0),char(35),char(0),char(4),char(0),char(36),char(0), +char(0),char(0),char(37),char(0),char(28),char(0),char(5),char(0),char(27),char(0),char(38),char(0),char(13),char(0),char(39),char(0),char(13),char(0),char(40),char(0), +char(7),char(0),char(41),char(0),char(0),char(0),char(21),char(0),char(29),char(0),char(5),char(0),char(27),char(0),char(38),char(0),char(13),char(0),char(39),char(0), +char(13),char(0),char(42),char(0),char(7),char(0),char(43),char(0),char(4),char(0),char(44),char(0),char(30),char(0),char(2),char(0),char(13),char(0),char(45),char(0), +char(7),char(0),char(46),char(0),char(31),char(0),char(4),char(0),char(29),char(0),char(47),char(0),char(30),char(0),char(48),char(0),char(4),char(0),char(49),char(0), +char(0),char(0),char(37),char(0),char(32),char(0),char(1),char(0),char(4),char(0),char(50),char(0),char(33),char(0),char(2),char(0),char(2),char(0),char(50),char(0), +char(0),char(0),char(51),char(0),char(34),char(0),char(2),char(0),char(2),char(0),char(52),char(0),char(0),char(0),char(51),char(0),char(35),char(0),char(2),char(0), +char(0),char(0),char(52),char(0),char(0),char(0),char(53),char(0),char(36),char(0),char(8),char(0),char(13),char(0),char(54),char(0),char(14),char(0),char(55),char(0), +char(32),char(0),char(56),char(0),char(34),char(0),char(57),char(0),char(35),char(0),char(58),char(0),char(33),char(0),char(59),char(0),char(4),char(0),char(60),char(0), +char(4),char(0),char(61),char(0),char(37),char(0),char(4),char(0),char(36),char(0),char(62),char(0),char(13),char(0),char(63),char(0),char(4),char(0),char(64),char(0), +char(0),char(0),char(37),char(0),char(38),char(0),char(7),char(0),char(27),char(0),char(38),char(0),char(37),char(0),char(65),char(0),char(25),char(0),char(66),char(0), +char(26),char(0),char(67),char(0),char(39),char(0),char(68),char(0),char(7),char(0),char(43),char(0),char(0),char(0),char(69),char(0),char(40),char(0),char(2),char(0), +char(38),char(0),char(70),char(0),char(13),char(0),char(39),char(0),char(41),char(0),char(4),char(0),char(19),char(0),char(71),char(0),char(27),char(0),char(72),char(0), +char(4),char(0),char(73),char(0),char(7),char(0),char(74),char(0),char(42),char(0),char(4),char(0),char(27),char(0),char(38),char(0),char(41),char(0),char(75),char(0), +char(4),char(0),char(76),char(0),char(7),char(0),char(43),char(0),char(43),char(0),char(3),char(0),char(29),char(0),char(47),char(0),char(4),char(0),char(77),char(0), +char(0),char(0),char(37),char(0),char(44),char(0),char(3),char(0),char(29),char(0),char(47),char(0),char(4),char(0),char(78),char(0),char(0),char(0),char(37),char(0), +char(45),char(0),char(3),char(0),char(29),char(0),char(47),char(0),char(4),char(0),char(77),char(0),char(0),char(0),char(37),char(0),char(46),char(0),char(4),char(0), +char(4),char(0),char(79),char(0),char(7),char(0),char(80),char(0),char(7),char(0),char(81),char(0),char(7),char(0),char(82),char(0),char(39),char(0),char(14),char(0), +char(4),char(0),char(83),char(0),char(4),char(0),char(84),char(0),char(46),char(0),char(85),char(0),char(4),char(0),char(86),char(0),char(7),char(0),char(87),char(0), +char(7),char(0),char(88),char(0),char(7),char(0),char(89),char(0),char(7),char(0),char(90),char(0),char(7),char(0),char(91),char(0),char(4),char(0),char(92),char(0), +char(4),char(0),char(93),char(0),char(4),char(0),char(94),char(0),char(4),char(0),char(95),char(0),char(0),char(0),char(37),char(0),char(47),char(0),char(38),char(0), +char(14),char(0),char(96),char(0),char(14),char(0),char(97),char(0),char(14),char(0),char(98),char(0),char(14),char(0),char(99),char(0),char(14),char(0),char(100),char(0), +char(14),char(0),char(101),char(0),char(14),char(0),char(102),char(0),char(8),char(0),char(103),char(0),char(8),char(0),char(104),char(0),char(8),char(0),char(105),char(0), +char(8),char(0),char(106),char(0),char(8),char(0),char(107),char(0),char(8),char(0),char(108),char(0),char(4),char(0),char(109),char(0),char(4),char(0),char(110),char(0), +char(4),char(0),char(111),char(0),char(4),char(0),char(112),char(0),char(4),char(0),char(113),char(0),char(8),char(0),char(114),char(0),char(8),char(0),char(115),char(0), +char(8),char(0),char(116),char(0),char(8),char(0),char(117),char(0),char(8),char(0),char(118),char(0),char(8),char(0),char(119),char(0),char(8),char(0),char(120),char(0), +char(8),char(0),char(121),char(0),char(8),char(0),char(122),char(0),char(4),char(0),char(123),char(0),char(4),char(0),char(124),char(0),char(4),char(0),char(125),char(0), +char(4),char(0),char(126),char(0),char(4),char(0),char(127),char(0),char(4),char(0),char(-128),char(0),char(8),char(0),char(-127),char(0),char(8),char(0),char(-126),char(0), +char(4),char(0),char(44),char(0),char(48),char(0),char(-125),char(0),char(48),char(0),char(-124),char(0),char(49),char(0),char(38),char(0),char(13),char(0),char(96),char(0), +char(13),char(0),char(97),char(0),char(13),char(0),char(98),char(0),char(13),char(0),char(99),char(0),char(13),char(0),char(100),char(0),char(13),char(0),char(101),char(0), +char(13),char(0),char(102),char(0),char(7),char(0),char(103),char(0),char(7),char(0),char(104),char(0),char(7),char(0),char(105),char(0),char(7),char(0),char(106),char(0), +char(7),char(0),char(107),char(0),char(7),char(0),char(108),char(0),char(4),char(0),char(109),char(0),char(4),char(0),char(110),char(0),char(4),char(0),char(111),char(0), +char(4),char(0),char(112),char(0),char(4),char(0),char(113),char(0),char(7),char(0),char(114),char(0),char(7),char(0),char(115),char(0),char(7),char(0),char(116),char(0), +char(7),char(0),char(117),char(0),char(7),char(0),char(118),char(0),char(7),char(0),char(119),char(0),char(7),char(0),char(120),char(0),char(7),char(0),char(121),char(0), +char(7),char(0),char(122),char(0),char(4),char(0),char(123),char(0),char(4),char(0),char(124),char(0),char(4),char(0),char(125),char(0),char(4),char(0),char(126),char(0), +char(4),char(0),char(127),char(0),char(4),char(0),char(-128),char(0),char(7),char(0),char(-127),char(0),char(7),char(0),char(-126),char(0),char(4),char(0),char(44),char(0), +char(50),char(0),char(-125),char(0),char(50),char(0),char(-124),char(0),char(51),char(0),char(5),char(0),char(27),char(0),char(38),char(0),char(37),char(0),char(65),char(0), +char(13),char(0),char(39),char(0),char(7),char(0),char(43),char(0),char(4),char(0),char(-123),char(0),char(52),char(0),char(5),char(0),char(29),char(0),char(47),char(0), +char(13),char(0),char(-122),char(0),char(14),char(0),char(-121),char(0),char(4),char(0),char(-120),char(0),char(0),char(0),char(-119),char(0),char(48),char(0),char(29),char(0), +char(9),char(0),char(-118),char(0),char(9),char(0),char(-117),char(0),char(27),char(0),char(-116),char(0),char(0),char(0),char(35),char(0),char(20),char(0),char(-115),char(0), +char(20),char(0),char(-114),char(0),char(14),char(0),char(-113),char(0),char(14),char(0),char(-112),char(0),char(14),char(0),char(-111),char(0),char(8),char(0),char(-126),char(0), +char(8),char(0),char(-110),char(0),char(8),char(0),char(-109),char(0),char(8),char(0),char(-108),char(0),char(8),char(0),char(-107),char(0),char(8),char(0),char(-106),char(0), +char(8),char(0),char(-105),char(0),char(8),char(0),char(-104),char(0),char(8),char(0),char(-103),char(0),char(8),char(0),char(-102),char(0),char(4),char(0),char(-101),char(0), +char(4),char(0),char(-100),char(0),char(4),char(0),char(-99),char(0),char(4),char(0),char(-98),char(0),char(4),char(0),char(-97),char(0),char(4),char(0),char(-96),char(0), +char(4),char(0),char(-95),char(0),char(4),char(0),char(-94),char(0),char(4),char(0),char(-93),char(0),char(4),char(0),char(-92),char(0),char(50),char(0),char(29),char(0), +char(9),char(0),char(-118),char(0),char(9),char(0),char(-117),char(0),char(27),char(0),char(-116),char(0),char(0),char(0),char(35),char(0),char(19),char(0),char(-115),char(0), +char(19),char(0),char(-114),char(0),char(13),char(0),char(-113),char(0),char(13),char(0),char(-112),char(0),char(13),char(0),char(-111),char(0),char(7),char(0),char(-126),char(0), +char(7),char(0),char(-110),char(0),char(7),char(0),char(-109),char(0),char(7),char(0),char(-108),char(0),char(7),char(0),char(-107),char(0),char(7),char(0),char(-106),char(0), +char(7),char(0),char(-105),char(0),char(7),char(0),char(-104),char(0),char(7),char(0),char(-103),char(0),char(7),char(0),char(-102),char(0),char(4),char(0),char(-101),char(0), +char(4),char(0),char(-100),char(0),char(4),char(0),char(-99),char(0),char(4),char(0),char(-98),char(0),char(4),char(0),char(-97),char(0),char(4),char(0),char(-96),char(0), +char(4),char(0),char(-95),char(0),char(4),char(0),char(-94),char(0),char(4),char(0),char(-93),char(0),char(4),char(0),char(-92),char(0),char(53),char(0),char(22),char(0), +char(8),char(0),char(-91),char(0),char(8),char(0),char(-90),char(0),char(8),char(0),char(-109),char(0),char(8),char(0),char(-89),char(0),char(8),char(0),char(-105),char(0), +char(8),char(0),char(-88),char(0),char(8),char(0),char(-87),char(0),char(8),char(0),char(-86),char(0),char(8),char(0),char(-85),char(0),char(8),char(0),char(-84),char(0), +char(8),char(0),char(-83),char(0),char(8),char(0),char(-82),char(0),char(8),char(0),char(-81),char(0),char(8),char(0),char(-80),char(0),char(8),char(0),char(-79),char(0), +char(8),char(0),char(-78),char(0),char(4),char(0),char(-77),char(0),char(4),char(0),char(-76),char(0),char(4),char(0),char(-75),char(0),char(4),char(0),char(-74),char(0), +char(4),char(0),char(-73),char(0),char(0),char(0),char(37),char(0),char(54),char(0),char(22),char(0),char(7),char(0),char(-91),char(0),char(7),char(0),char(-90),char(0), +char(7),char(0),char(-109),char(0),char(7),char(0),char(-89),char(0),char(7),char(0),char(-105),char(0),char(7),char(0),char(-88),char(0),char(7),char(0),char(-87),char(0), +char(7),char(0),char(-86),char(0),char(7),char(0),char(-85),char(0),char(7),char(0),char(-84),char(0),char(7),char(0),char(-83),char(0),char(7),char(0),char(-82),char(0), +char(7),char(0),char(-81),char(0),char(7),char(0),char(-80),char(0),char(7),char(0),char(-79),char(0),char(7),char(0),char(-78),char(0),char(4),char(0),char(-77),char(0), +char(4),char(0),char(-76),char(0),char(4),char(0),char(-75),char(0),char(4),char(0),char(-74),char(0),char(4),char(0),char(-73),char(0),char(0),char(0),char(37),char(0), +char(55),char(0),char(2),char(0),char(53),char(0),char(-72),char(0),char(14),char(0),char(-71),char(0),char(56),char(0),char(2),char(0),char(54),char(0),char(-72),char(0), +char(13),char(0),char(-71),char(0),char(57),char(0),char(21),char(0),char(50),char(0),char(-70),char(0),char(17),char(0),char(-69),char(0),char(13),char(0),char(-68),char(0), +char(13),char(0),char(-67),char(0),char(13),char(0),char(-66),char(0),char(13),char(0),char(-65),char(0),char(13),char(0),char(-71),char(0),char(13),char(0),char(-64),char(0), +char(13),char(0),char(-63),char(0),char(13),char(0),char(-62),char(0),char(13),char(0),char(-61),char(0),char(7),char(0),char(-60),char(0),char(7),char(0),char(-59),char(0), +char(7),char(0),char(-58),char(0),char(7),char(0),char(-57),char(0),char(7),char(0),char(-56),char(0),char(7),char(0),char(-55),char(0),char(7),char(0),char(-54),char(0), +char(7),char(0),char(-53),char(0),char(7),char(0),char(-52),char(0),char(4),char(0),char(-51),char(0),char(58),char(0),char(22),char(0),char(48),char(0),char(-70),char(0), +char(18),char(0),char(-69),char(0),char(14),char(0),char(-68),char(0),char(14),char(0),char(-67),char(0),char(14),char(0),char(-66),char(0),char(14),char(0),char(-65),char(0), +char(14),char(0),char(-71),char(0),char(14),char(0),char(-64),char(0),char(14),char(0),char(-63),char(0),char(14),char(0),char(-62),char(0),char(14),char(0),char(-61),char(0), +char(8),char(0),char(-60),char(0),char(8),char(0),char(-59),char(0),char(8),char(0),char(-58),char(0),char(8),char(0),char(-57),char(0),char(8),char(0),char(-56),char(0), +char(8),char(0),char(-55),char(0),char(8),char(0),char(-54),char(0),char(8),char(0),char(-53),char(0),char(8),char(0),char(-52),char(0),char(4),char(0),char(-51),char(0), +char(0),char(0),char(37),char(0),char(59),char(0),char(2),char(0),char(4),char(0),char(-50),char(0),char(4),char(0),char(-49),char(0),char(60),char(0),char(13),char(0), +char(57),char(0),char(-48),char(0),char(57),char(0),char(-47),char(0),char(0),char(0),char(35),char(0),char(4),char(0),char(-128),char(0),char(4),char(0),char(-46),char(0), +char(4),char(0),char(-45),char(0),char(4),char(0),char(-44),char(0),char(7),char(0),char(-43),char(0),char(7),char(0),char(-42),char(0),char(4),char(0),char(-41),char(0), +char(4),char(0),char(-40),char(0),char(7),char(0),char(-39),char(0),char(4),char(0),char(-38),char(0),char(61),char(0),char(13),char(0),char(62),char(0),char(-48),char(0), +char(62),char(0),char(-47),char(0),char(0),char(0),char(35),char(0),char(4),char(0),char(-128),char(0),char(4),char(0),char(-46),char(0),char(4),char(0),char(-45),char(0), +char(4),char(0),char(-44),char(0),char(7),char(0),char(-43),char(0),char(7),char(0),char(-42),char(0),char(4),char(0),char(-41),char(0),char(4),char(0),char(-40),char(0), +char(7),char(0),char(-39),char(0),char(4),char(0),char(-38),char(0),char(63),char(0),char(14),char(0),char(58),char(0),char(-48),char(0),char(58),char(0),char(-47),char(0), +char(0),char(0),char(35),char(0),char(4),char(0),char(-128),char(0),char(4),char(0),char(-46),char(0),char(4),char(0),char(-45),char(0),char(4),char(0),char(-44),char(0), +char(8),char(0),char(-43),char(0),char(8),char(0),char(-42),char(0),char(4),char(0),char(-41),char(0),char(4),char(0),char(-40),char(0),char(8),char(0),char(-39),char(0), +char(4),char(0),char(-38),char(0),char(0),char(0),char(-37),char(0),char(64),char(0),char(3),char(0),char(61),char(0),char(-36),char(0),char(13),char(0),char(-35),char(0), +char(13),char(0),char(-34),char(0),char(65),char(0),char(3),char(0),char(63),char(0),char(-36),char(0),char(14),char(0),char(-35),char(0),char(14),char(0),char(-34),char(0), +char(66),char(0),char(3),char(0),char(61),char(0),char(-36),char(0),char(14),char(0),char(-35),char(0),char(14),char(0),char(-34),char(0),char(67),char(0),char(13),char(0), +char(61),char(0),char(-36),char(0),char(20),char(0),char(-33),char(0),char(20),char(0),char(-32),char(0),char(4),char(0),char(-31),char(0),char(4),char(0),char(-30),char(0), +char(4),char(0),char(-29),char(0),char(7),char(0),char(-28),char(0),char(7),char(0),char(-27),char(0),char(7),char(0),char(-26),char(0),char(7),char(0),char(-25),char(0), +char(7),char(0),char(-24),char(0),char(7),char(0),char(-23),char(0),char(7),char(0),char(-22),char(0),char(68),char(0),char(13),char(0),char(61),char(0),char(-36),char(0), +char(19),char(0),char(-33),char(0),char(19),char(0),char(-32),char(0),char(4),char(0),char(-31),char(0),char(4),char(0),char(-30),char(0),char(4),char(0),char(-29),char(0), +char(7),char(0),char(-28),char(0),char(7),char(0),char(-27),char(0),char(7),char(0),char(-26),char(0),char(7),char(0),char(-25),char(0),char(7),char(0),char(-24),char(0), +char(7),char(0),char(-23),char(0),char(7),char(0),char(-22),char(0),char(69),char(0),char(14),char(0),char(63),char(0),char(-36),char(0),char(20),char(0),char(-33),char(0), +char(20),char(0),char(-32),char(0),char(4),char(0),char(-31),char(0),char(4),char(0),char(-30),char(0),char(4),char(0),char(-29),char(0),char(8),char(0),char(-28),char(0), +char(8),char(0),char(-27),char(0),char(8),char(0),char(-26),char(0),char(8),char(0),char(-25),char(0),char(8),char(0),char(-24),char(0),char(8),char(0),char(-23),char(0), +char(8),char(0),char(-22),char(0),char(0),char(0),char(-21),char(0),char(70),char(0),char(10),char(0),char(63),char(0),char(-36),char(0),char(20),char(0),char(-33),char(0), +char(20),char(0),char(-32),char(0),char(8),char(0),char(-20),char(0),char(8),char(0),char(-19),char(0),char(8),char(0),char(-18),char(0),char(8),char(0),char(-24),char(0), +char(8),char(0),char(-23),char(0),char(8),char(0),char(-22),char(0),char(8),char(0),char(-90),char(0),char(71),char(0),char(11),char(0),char(61),char(0),char(-36),char(0), +char(19),char(0),char(-33),char(0),char(19),char(0),char(-32),char(0),char(7),char(0),char(-20),char(0),char(7),char(0),char(-19),char(0),char(7),char(0),char(-18),char(0), +char(7),char(0),char(-24),char(0),char(7),char(0),char(-23),char(0),char(7),char(0),char(-22),char(0),char(7),char(0),char(-90),char(0),char(0),char(0),char(21),char(0), +char(72),char(0),char(9),char(0),char(61),char(0),char(-36),char(0),char(19),char(0),char(-33),char(0),char(19),char(0),char(-32),char(0),char(13),char(0),char(-17),char(0), +char(13),char(0),char(-16),char(0),char(13),char(0),char(-15),char(0),char(13),char(0),char(-14),char(0),char(4),char(0),char(-13),char(0),char(4),char(0),char(-12),char(0), +char(73),char(0),char(9),char(0),char(63),char(0),char(-36),char(0),char(20),char(0),char(-33),char(0),char(20),char(0),char(-32),char(0),char(14),char(0),char(-17),char(0), +char(14),char(0),char(-16),char(0),char(14),char(0),char(-15),char(0),char(14),char(0),char(-14),char(0),char(4),char(0),char(-13),char(0),char(4),char(0),char(-12),char(0), +char(74),char(0),char(5),char(0),char(72),char(0),char(-11),char(0),char(4),char(0),char(-10),char(0),char(7),char(0),char(-9),char(0),char(7),char(0),char(-8),char(0), +char(7),char(0),char(-7),char(0),char(75),char(0),char(5),char(0),char(73),char(0),char(-11),char(0),char(4),char(0),char(-10),char(0),char(8),char(0),char(-9),char(0), +char(8),char(0),char(-8),char(0),char(8),char(0),char(-7),char(0),char(76),char(0),char(41),char(0),char(61),char(0),char(-36),char(0),char(19),char(0),char(-33),char(0), +char(19),char(0),char(-32),char(0),char(13),char(0),char(-17),char(0),char(13),char(0),char(-16),char(0),char(13),char(0),char(-6),char(0),char(13),char(0),char(-5),char(0), +char(13),char(0),char(-4),char(0),char(13),char(0),char(-3),char(0),char(13),char(0),char(-2),char(0),char(13),char(0),char(-1),char(0),char(13),char(0),char(0),char(1), +char(13),char(0),char(1),char(1),char(13),char(0),char(2),char(1),char(13),char(0),char(3),char(1),char(13),char(0),char(4),char(1),char(0),char(0),char(5),char(1), +char(0),char(0),char(6),char(1),char(0),char(0),char(7),char(1),char(0),char(0),char(8),char(1),char(0),char(0),char(9),char(1),char(0),char(0),char(-21),char(0), +char(13),char(0),char(-15),char(0),char(13),char(0),char(-14),char(0),char(13),char(0),char(10),char(1),char(13),char(0),char(11),char(1),char(13),char(0),char(12),char(1), +char(13),char(0),char(13),char(1),char(13),char(0),char(14),char(1),char(13),char(0),char(15),char(1),char(13),char(0),char(16),char(1),char(13),char(0),char(17),char(1), +char(13),char(0),char(18),char(1),char(13),char(0),char(19),char(1),char(13),char(0),char(20),char(1),char(0),char(0),char(21),char(1),char(0),char(0),char(22),char(1), +char(0),char(0),char(23),char(1),char(0),char(0),char(24),char(1),char(0),char(0),char(25),char(1),char(4),char(0),char(26),char(1),char(77),char(0),char(41),char(0), +char(63),char(0),char(-36),char(0),char(20),char(0),char(-33),char(0),char(20),char(0),char(-32),char(0),char(14),char(0),char(-17),char(0),char(14),char(0),char(-16),char(0), +char(14),char(0),char(-6),char(0),char(14),char(0),char(-5),char(0),char(14),char(0),char(-4),char(0),char(14),char(0),char(-3),char(0),char(14),char(0),char(-2),char(0), +char(14),char(0),char(-1),char(0),char(14),char(0),char(0),char(1),char(14),char(0),char(1),char(1),char(14),char(0),char(2),char(1),char(14),char(0),char(3),char(1), +char(14),char(0),char(4),char(1),char(0),char(0),char(5),char(1),char(0),char(0),char(6),char(1),char(0),char(0),char(7),char(1),char(0),char(0),char(8),char(1), +char(0),char(0),char(9),char(1),char(0),char(0),char(-21),char(0),char(14),char(0),char(-15),char(0),char(14),char(0),char(-14),char(0),char(14),char(0),char(10),char(1), +char(14),char(0),char(11),char(1),char(14),char(0),char(12),char(1),char(14),char(0),char(13),char(1),char(14),char(0),char(14),char(1),char(14),char(0),char(15),char(1), +char(14),char(0),char(16),char(1),char(14),char(0),char(17),char(1),char(14),char(0),char(18),char(1),char(14),char(0),char(19),char(1),char(14),char(0),char(20),char(1), +char(0),char(0),char(21),char(1),char(0),char(0),char(22),char(1),char(0),char(0),char(23),char(1),char(0),char(0),char(24),char(1),char(0),char(0),char(25),char(1), +char(4),char(0),char(26),char(1),char(78),char(0),char(9),char(0),char(61),char(0),char(-36),char(0),char(19),char(0),char(-33),char(0),char(19),char(0),char(-32),char(0), +char(7),char(0),char(-17),char(0),char(7),char(0),char(-16),char(0),char(7),char(0),char(-15),char(0),char(7),char(0),char(-14),char(0),char(4),char(0),char(-13),char(0), +char(4),char(0),char(-12),char(0),char(79),char(0),char(9),char(0),char(63),char(0),char(-36),char(0),char(20),char(0),char(-33),char(0),char(20),char(0),char(-32),char(0), +char(8),char(0),char(-17),char(0),char(8),char(0),char(-16),char(0),char(8),char(0),char(-15),char(0),char(8),char(0),char(-14),char(0),char(4),char(0),char(-13),char(0), +char(4),char(0),char(-12),char(0),char(80),char(0),char(5),char(0),char(60),char(0),char(-36),char(0),char(13),char(0),char(27),char(1),char(13),char(0),char(28),char(1), +char(7),char(0),char(29),char(1),char(0),char(0),char(37),char(0),char(81),char(0),char(4),char(0),char(63),char(0),char(-36),char(0),char(14),char(0),char(27),char(1), +char(14),char(0),char(28),char(1),char(8),char(0),char(29),char(1),char(82),char(0),char(4),char(0),char(7),char(0),char(30),char(1),char(7),char(0),char(31),char(1), +char(7),char(0),char(32),char(1),char(4),char(0),char(79),char(0),char(83),char(0),char(10),char(0),char(82),char(0),char(33),char(1),char(13),char(0),char(34),char(1), +char(13),char(0),char(35),char(1),char(13),char(0),char(36),char(1),char(13),char(0),char(37),char(1),char(13),char(0),char(38),char(1),char(7),char(0),char(-60),char(0), +char(7),char(0),char(39),char(1),char(4),char(0),char(40),char(1),char(4),char(0),char(53),char(0),char(84),char(0),char(4),char(0),char(82),char(0),char(33),char(1), +char(4),char(0),char(41),char(1),char(7),char(0),char(42),char(1),char(4),char(0),char(43),char(1),char(85),char(0),char(4),char(0),char(13),char(0),char(38),char(1), +char(82),char(0),char(33),char(1),char(4),char(0),char(44),char(1),char(7),char(0),char(45),char(1),char(86),char(0),char(7),char(0),char(13),char(0),char(46),char(1), +char(82),char(0),char(33),char(1),char(4),char(0),char(47),char(1),char(7),char(0),char(48),char(1),char(7),char(0),char(49),char(1),char(7),char(0),char(50),char(1), +char(4),char(0),char(53),char(0),char(87),char(0),char(6),char(0),char(17),char(0),char(51),char(1),char(13),char(0),char(49),char(1),char(13),char(0),char(52),char(1), +char(62),char(0),char(53),char(1),char(4),char(0),char(54),char(1),char(7),char(0),char(50),char(1),char(88),char(0),char(26),char(0),char(4),char(0),char(55),char(1), +char(7),char(0),char(56),char(1),char(7),char(0),char(-90),char(0),char(7),char(0),char(57),char(1),char(7),char(0),char(58),char(1),char(7),char(0),char(59),char(1), +char(7),char(0),char(60),char(1),char(7),char(0),char(61),char(1),char(7),char(0),char(62),char(1),char(7),char(0),char(63),char(1),char(7),char(0),char(64),char(1), +char(7),char(0),char(65),char(1),char(7),char(0),char(66),char(1),char(7),char(0),char(67),char(1),char(7),char(0),char(68),char(1),char(7),char(0),char(69),char(1), +char(7),char(0),char(70),char(1),char(7),char(0),char(71),char(1),char(7),char(0),char(72),char(1),char(7),char(0),char(73),char(1),char(7),char(0),char(74),char(1), +char(4),char(0),char(75),char(1),char(4),char(0),char(76),char(1),char(4),char(0),char(77),char(1),char(4),char(0),char(78),char(1),char(4),char(0),char(-100),char(0), +char(89),char(0),char(12),char(0),char(17),char(0),char(79),char(1),char(17),char(0),char(80),char(1),char(17),char(0),char(81),char(1),char(13),char(0),char(82),char(1), +char(13),char(0),char(83),char(1),char(7),char(0),char(84),char(1),char(4),char(0),char(85),char(1),char(4),char(0),char(86),char(1),char(4),char(0),char(87),char(1), +char(4),char(0),char(88),char(1),char(7),char(0),char(48),char(1),char(4),char(0),char(53),char(0),char(90),char(0),char(27),char(0),char(19),char(0),char(89),char(1), +char(17),char(0),char(90),char(1),char(17),char(0),char(91),char(1),char(13),char(0),char(82),char(1),char(13),char(0),char(92),char(1),char(13),char(0),char(93),char(1), +char(13),char(0),char(94),char(1),char(13),char(0),char(95),char(1),char(13),char(0),char(96),char(1),char(4),char(0),char(97),char(1),char(7),char(0),char(98),char(1), +char(4),char(0),char(99),char(1),char(4),char(0),char(100),char(1),char(4),char(0),char(101),char(1),char(7),char(0),char(102),char(1),char(7),char(0),char(103),char(1), +char(4),char(0),char(104),char(1),char(4),char(0),char(105),char(1),char(7),char(0),char(106),char(1),char(7),char(0),char(107),char(1),char(7),char(0),char(108),char(1), +char(7),char(0),char(109),char(1),char(7),char(0),char(110),char(1),char(7),char(0),char(111),char(1),char(4),char(0),char(112),char(1),char(4),char(0),char(113),char(1), +char(4),char(0),char(114),char(1),char(91),char(0),char(12),char(0),char(9),char(0),char(115),char(1),char(9),char(0),char(116),char(1),char(13),char(0),char(117),char(1), +char(7),char(0),char(118),char(1),char(7),char(0),char(-86),char(0),char(7),char(0),char(119),char(1),char(4),char(0),char(120),char(1),char(13),char(0),char(121),char(1), +char(4),char(0),char(122),char(1),char(4),char(0),char(123),char(1),char(4),char(0),char(124),char(1),char(4),char(0),char(53),char(0),char(92),char(0),char(19),char(0), +char(50),char(0),char(-70),char(0),char(89),char(0),char(125),char(1),char(82),char(0),char(126),char(1),char(83),char(0),char(127),char(1),char(84),char(0),char(-128),char(1), +char(85),char(0),char(-127),char(1),char(86),char(0),char(-126),char(1),char(87),char(0),char(-125),char(1),char(90),char(0),char(-124),char(1),char(91),char(0),char(-123),char(1), +char(4),char(0),char(-122),char(1),char(4),char(0),char(100),char(1),char(4),char(0),char(-121),char(1),char(4),char(0),char(-120),char(1),char(4),char(0),char(-119),char(1), +char(4),char(0),char(-118),char(1),char(4),char(0),char(-117),char(1),char(4),char(0),char(-116),char(1),char(88),char(0),char(-115),char(1),char(93),char(0),char(28),char(0), +char(16),char(0),char(-114),char(1),char(14),char(0),char(-113),char(1),char(14),char(0),char(-112),char(1),char(14),char(0),char(-111),char(1),char(14),char(0),char(-110),char(1), +char(14),char(0),char(-109),char(1),char(14),char(0),char(-108),char(1),char(14),char(0),char(-107),char(1),char(14),char(0),char(-106),char(1),char(14),char(0),char(-105),char(1), +char(8),char(0),char(-104),char(1),char(4),char(0),char(-103),char(1),char(4),char(0),char(124),char(1),char(4),char(0),char(-102),char(1),char(4),char(0),char(-101),char(1), +char(8),char(0),char(-100),char(1),char(8),char(0),char(-99),char(1),char(8),char(0),char(-98),char(1),char(8),char(0),char(-97),char(1),char(8),char(0),char(-96),char(1), +char(8),char(0),char(-95),char(1),char(8),char(0),char(-94),char(1),char(8),char(0),char(-93),char(1),char(8),char(0),char(-92),char(1),char(0),char(0),char(-91),char(1), +char(0),char(0),char(-90),char(1),char(48),char(0),char(-89),char(1),char(0),char(0),char(-88),char(1),char(94),char(0),char(28),char(0),char(15),char(0),char(-114),char(1), +char(13),char(0),char(-113),char(1),char(13),char(0),char(-112),char(1),char(13),char(0),char(-111),char(1),char(13),char(0),char(-110),char(1),char(13),char(0),char(-109),char(1), +char(13),char(0),char(-108),char(1),char(13),char(0),char(-107),char(1),char(13),char(0),char(-106),char(1),char(13),char(0),char(-105),char(1),char(4),char(0),char(-102),char(1), +char(7),char(0),char(-104),char(1),char(4),char(0),char(-103),char(1),char(4),char(0),char(124),char(1),char(7),char(0),char(-100),char(1),char(7),char(0),char(-99),char(1), +char(7),char(0),char(-98),char(1),char(4),char(0),char(-101),char(1),char(7),char(0),char(-97),char(1),char(7),char(0),char(-96),char(1),char(7),char(0),char(-95),char(1), +char(7),char(0),char(-94),char(1),char(7),char(0),char(-93),char(1),char(7),char(0),char(-92),char(1),char(0),char(0),char(-91),char(1),char(0),char(0),char(-90),char(1), +char(50),char(0),char(-89),char(1),char(0),char(0),char(-88),char(1),char(95),char(0),char(11),char(0),char(14),char(0),char(-87),char(1),char(16),char(0),char(-86),char(1), +char(14),char(0),char(-85),char(1),char(14),char(0),char(-84),char(1),char(14),char(0),char(-83),char(1),char(8),char(0),char(-82),char(1),char(4),char(0),char(-121),char(1), +char(0),char(0),char(37),char(0),char(0),char(0),char(-81),char(1),char(93),char(0),char(-128),char(1),char(48),char(0),char(-80),char(1),char(96),char(0),char(10),char(0), +char(13),char(0),char(-87),char(1),char(15),char(0),char(-86),char(1),char(13),char(0),char(-85),char(1),char(13),char(0),char(-84),char(1),char(13),char(0),char(-83),char(1), +char(7),char(0),char(-82),char(1),char(4),char(0),char(-121),char(1),char(0),char(0),char(-81),char(1),char(94),char(0),char(-128),char(1),char(50),char(0),char(-80),char(1), +char(97),char(0),char(4),char(0),char(50),char(0),char(-79),char(1),char(96),char(0),char(-78),char(1),char(4),char(0),char(-77),char(1),char(0),char(0),char(37),char(0), +char(98),char(0),char(4),char(0),char(48),char(0),char(-79),char(1),char(95),char(0),char(-78),char(1),char(4),char(0),char(-77),char(1),char(0),char(0),char(37),char(0), +}; int sBulletDNAlen64= sizeof(sBulletDNAstr64); diff --git a/thirdparty/bullet/LinearMath/btThreads.cpp b/thirdparty/bullet/LinearMath/btThreads.cpp index 59a7ea36e9..c037626ffb 100644 --- a/thirdparty/bullet/LinearMath/btThreads.cpp +++ b/thirdparty/bullet/LinearMath/btThreads.cpp @@ -453,6 +453,33 @@ void btParallelFor( int iBegin, int iEnd, int grainSize, const btIParallelForBod #endif// #if BT_THREADSAFE } +btScalar btParallelSum( int iBegin, int iEnd, int grainSize, const btIParallelSumBody& body ) +{ +#if BT_THREADSAFE + +#if BT_DETECT_BAD_THREAD_INDEX + if ( !btThreadsAreRunning() ) + { + // clear out thread ids + for ( int i = 0; i < BT_MAX_THREAD_COUNT; ++i ) + { + gDebugThreadIds[ i ] = kInvalidThreadId; + } + } +#endif // #if BT_DETECT_BAD_THREAD_INDEX + + btAssert( gBtTaskScheduler != NULL ); // call btSetTaskScheduler() with a valid task scheduler first! + return gBtTaskScheduler->parallelSum( iBegin, iEnd, grainSize, body ); + +#else // #if BT_THREADSAFE + + // non-parallel version of btParallelSum + btAssert( !"called btParallelFor in non-threadsafe build. enable BT_THREADSAFE" ); + return body.sumLoop( iBegin, iEnd ); + +#endif //#else // #if BT_THREADSAFE +} + /// /// btTaskSchedulerSequential -- non-threaded implementation of task scheduler @@ -470,6 +497,11 @@ public: BT_PROFILE( "parallelFor_sequential" ); body.forLoop( iBegin, iEnd ); } + virtual btScalar parallelSum( int iBegin, int iEnd, int grainSize, const btIParallelSumBody& body ) BT_OVERRIDE + { + BT_PROFILE( "parallelSum_sequential" ); + return body.sumLoop( iBegin, iEnd ); + } }; @@ -514,11 +546,25 @@ public: #pragma omp parallel for schedule( static, 1 ) for ( int i = iBegin; i < iEnd; i += grainSize ) { - BT_PROFILE( "OpenMP_job" ); + BT_PROFILE( "OpenMP_forJob" ); body.forLoop( i, ( std::min )( i + grainSize, iEnd ) ); } btPopThreadsAreRunning(); } + virtual btScalar parallelSum( int iBegin, int iEnd, int grainSize, const btIParallelSumBody& body ) BT_OVERRIDE + { + BT_PROFILE( "parallelFor_OpenMP" ); + btPushThreadsAreRunning(); + btScalar sum = btScalar( 0 ); +#pragma omp parallel for schedule( static, 1 ) reduction(+:sum) + for ( int i = iBegin; i < iEnd; i += grainSize ) + { + BT_PROFILE( "OpenMP_sumJob" ); + sum += body.sumLoop( i, ( std::min )( i + grainSize, iEnd ) ); + } + btPopThreadsAreRunning(); + return sum; + } }; #endif // #if BT_USE_OPENMP && BT_THREADSAFE @@ -571,22 +617,21 @@ public: btResetThreadIndexCounter(); } } - struct BodyAdapter + struct ForBodyAdapter { const btIParallelForBody* mBody; + ForBodyAdapter( const btIParallelForBody* body ) : mBody( body ) {} void operator()( const tbb::blocked_range<int>& range ) const { - BT_PROFILE( "TBB_job" ); + BT_PROFILE( "TBB_forJob" ); mBody->forLoop( range.begin(), range.end() ); } }; virtual void parallelFor( int iBegin, int iEnd, int grainSize, const btIParallelForBody& body ) BT_OVERRIDE { BT_PROFILE( "parallelFor_TBB" ); - // TBB dispatch - BodyAdapter tbbBody; - tbbBody.mBody = &body; + ForBodyAdapter tbbBody( &body ); btPushThreadsAreRunning(); tbb::parallel_for( tbb::blocked_range<int>( iBegin, iEnd, grainSize ), tbbBody, @@ -594,6 +639,29 @@ public: ); btPopThreadsAreRunning(); } + struct SumBodyAdapter + { + const btIParallelSumBody* mBody; + btScalar mSum; + + SumBodyAdapter( const btIParallelSumBody* body ) : mBody( body ), mSum( btScalar( 0 ) ) {} + SumBodyAdapter( const SumBodyAdapter& src, tbb::split ) : mBody( src.mBody ), mSum( btScalar( 0 ) ) {} + void join( const SumBodyAdapter& src ) { mSum += src.mSum; } + void operator()( const tbb::blocked_range<int>& range ) + { + BT_PROFILE( "TBB_sumJob" ); + mSum += mBody->sumLoop( range.begin(), range.end() ); + } + }; + virtual btScalar parallelSum( int iBegin, int iEnd, int grainSize, const btIParallelSumBody& body ) BT_OVERRIDE + { + BT_PROFILE( "parallelSum_TBB" ); + SumBodyAdapter tbbBody( &body ); + btPushThreadsAreRunning(); + tbb::parallel_deterministic_reduce( tbb::blocked_range<int>( iBegin, iEnd, grainSize ), tbbBody ); + btPopThreadsAreRunning(); + return tbbBody.mSum; + } }; #endif // #if BT_USE_TBB && BT_THREADSAFE @@ -605,6 +673,7 @@ public: class btTaskSchedulerPPL : public btITaskScheduler { int m_numThreads; + concurrency::combinable<btScalar> m_sum; // for parallelSum public: btTaskSchedulerPPL() : btITaskScheduler( "PPL" ) { @@ -644,15 +713,16 @@ public: btResetThreadIndexCounter(); } } - struct BodyAdapter + struct ForBodyAdapter { const btIParallelForBody* mBody; int mGrainSize; int mIndexEnd; + ForBodyAdapter( const btIParallelForBody* body, int grainSize, int end ) : mBody( body ), mGrainSize( grainSize ), mIndexEnd( end ) {} void operator()( int i ) const { - BT_PROFILE( "PPL_job" ); + BT_PROFILE( "PPL_forJob" ); mBody->forLoop( i, ( std::min )( i + mGrainSize, mIndexEnd ) ); } }; @@ -660,10 +730,36 @@ public: { BT_PROFILE( "parallelFor_PPL" ); // PPL dispatch - BodyAdapter pplBody; - pplBody.mBody = &body; - pplBody.mGrainSize = grainSize; - pplBody.mIndexEnd = iEnd; + ForBodyAdapter pplBody( &body, grainSize, iEnd ); + btPushThreadsAreRunning(); + // note: MSVC 2010 doesn't support partitioner args, so avoid them + concurrency::parallel_for( iBegin, + iEnd, + grainSize, + pplBody + ); + btPopThreadsAreRunning(); + } + struct SumBodyAdapter + { + const btIParallelSumBody* mBody; + concurrency::combinable<btScalar>* mSum; + int mGrainSize; + int mIndexEnd; + + SumBodyAdapter( const btIParallelSumBody* body, concurrency::combinable<btScalar>* sum, int grainSize, int end ) : mBody( body ), mSum(sum), mGrainSize( grainSize ), mIndexEnd( end ) {} + void operator()( int i ) const + { + BT_PROFILE( "PPL_sumJob" ); + mSum->local() += mBody->sumLoop( i, ( std::min )( i + mGrainSize, mIndexEnd ) ); + } + }; + static btScalar sumFunc( btScalar a, btScalar b ) { return a + b; } + virtual btScalar parallelSum( int iBegin, int iEnd, int grainSize, const btIParallelSumBody& body ) BT_OVERRIDE + { + BT_PROFILE( "parallelSum_PPL" ); + m_sum.clear(); + SumBodyAdapter pplBody( &body, &m_sum, grainSize, iEnd ); btPushThreadsAreRunning(); // note: MSVC 2010 doesn't support partitioner args, so avoid them concurrency::parallel_for( iBegin, @@ -672,6 +768,7 @@ public: pplBody ); btPopThreadsAreRunning(); + return m_sum.combine( sumFunc ); } }; #endif // #if BT_USE_PPL && BT_THREADSAFE diff --git a/thirdparty/bullet/LinearMath/btThreads.h b/thirdparty/bullet/LinearMath/btThreads.h index 05fd15ec82..921fd088c0 100644 --- a/thirdparty/bullet/LinearMath/btThreads.h +++ b/thirdparty/bullet/LinearMath/btThreads.h @@ -28,6 +28,8 @@ subject to the following restrictions: #define BT_OVERRIDE #endif +// Don't set this to larger than 64, without modifying btThreadSupportPosix +// and btThreadSupportWin32. They use UINT64 bit-masks. const unsigned int BT_MAX_THREAD_COUNT = 64; // only if BT_THREADSAFE is 1 // for internal use only @@ -72,6 +74,8 @@ SIMD_FORCE_INLINE void btMutexLock( btSpinMutex* mutex ) { #if BT_THREADSAFE mutex->lock(); +#else + (void)mutex; #endif // #if BT_THREADSAFE } @@ -79,6 +83,8 @@ SIMD_FORCE_INLINE void btMutexUnlock( btSpinMutex* mutex ) { #if BT_THREADSAFE mutex->unlock(); +#else + (void)mutex; #endif // #if BT_THREADSAFE } @@ -87,6 +93,7 @@ SIMD_FORCE_INLINE bool btMutexTryLock( btSpinMutex* mutex ) #if BT_THREADSAFE return mutex->tryLock(); #else + (void)mutex; return true; #endif // #if BT_THREADSAFE } @@ -103,6 +110,17 @@ public: }; // +// btIParallelSumBody -- subclass this to express work that can be done in parallel +// and produces a sum over all loop elements +// +class btIParallelSumBody +{ +public: + virtual ~btIParallelSumBody() {} + virtual btScalar sumLoop( int iBegin, int iEnd ) const = 0; +}; + +// // btITaskScheduler -- subclass this to implement a task scheduler that can dispatch work to // worker threads // @@ -117,6 +135,8 @@ public: virtual int getNumThreads() const = 0; virtual void setNumThreads( int numThreads ) = 0; virtual void parallelFor( int iBegin, int iEnd, int grainSize, const btIParallelForBody& body ) = 0; + virtual btScalar parallelSum( int iBegin, int iEnd, int grainSize, const btIParallelSumBody& body ) = 0; + virtual void sleepWorkerThreadsHint() {} // hint the task scheduler that we may not be using these threads for a little while // internal use only virtual void activate(); @@ -138,6 +158,9 @@ btITaskScheduler* btGetTaskScheduler(); // get non-threaded task scheduler (always available) btITaskScheduler* btGetSequentialTaskScheduler(); +// create a default task scheduler (Win32 or pthreads based) +btITaskScheduler* btCreateDefaultTaskScheduler(); + // get OpenMP task scheduler (if available, otherwise returns null) btITaskScheduler* btGetOpenMPTaskScheduler(); @@ -151,5 +174,9 @@ btITaskScheduler* btGetPPLTaskScheduler(); // (iterations may be done out of order, so no dependencies are allowed) void btParallelFor( int iBegin, int iEnd, int grainSize, const btIParallelForBody& body ); +// btParallelSum -- call this to dispatch work like a for-loop, returns the sum of all iterations +// (iterations may be done out of order, so no dependencies are allowed) +btScalar btParallelSum( int iBegin, int iEnd, int grainSize, const btIParallelSumBody& body ); + #endif diff --git a/thirdparty/bullet/LinearMath/btVector3.h b/thirdparty/bullet/LinearMath/btVector3.h index c69effa96e..76024f1236 100644 --- a/thirdparty/bullet/LinearMath/btVector3.h +++ b/thirdparty/bullet/LinearMath/btVector3.h @@ -705,7 +705,9 @@ public: SIMD_FORCE_INLINE void serialize(struct btVector3Data& dataOut) const; - SIMD_FORCE_INLINE void deSerialize(const struct btVector3Data& dataIn); + SIMD_FORCE_INLINE void deSerialize(const struct btVector3DoubleData& dataIn); + + SIMD_FORCE_INLINE void deSerialize(const struct btVector3FloatData& dataIn); SIMD_FORCE_INLINE void serializeFloat(struct btVector3FloatData& dataOut) const; @@ -1236,9 +1238,9 @@ public: ///btSwapVector3Endian swaps vector endianness, useful for network and cross-platform serialization SIMD_FORCE_INLINE void btSwapScalarEndian(const btScalar& sourceVal, btScalar& destVal) { - #ifdef BT_USE_DOUBLE_PRECISION +#ifdef BT_USE_DOUBLE_PRECISION unsigned char* dest = (unsigned char*) &destVal; - unsigned char* src = (unsigned char*) &sourceVal; + const unsigned char* src = (const unsigned char*) &sourceVal; dest[0] = src[7]; dest[1] = src[6]; dest[2] = src[5]; @@ -1249,7 +1251,7 @@ SIMD_FORCE_INLINE void btSwapScalarEndian(const btScalar& sourceVal, btScalar& d dest[7] = src[0]; #else unsigned char* dest = (unsigned char*) &destVal; - unsigned char* src = (unsigned char*) &sourceVal; + const unsigned char* src = (const unsigned char*) &sourceVal; dest[0] = src[3]; dest[1] = src[2]; dest[2] = src[1]; @@ -1354,10 +1356,18 @@ SIMD_FORCE_INLINE void btVector3::serialize(struct btVector3Data& dataOut) const dataOut.m_floats[i] = m_floats[i]; } -SIMD_FORCE_INLINE void btVector3::deSerialize(const struct btVector3Data& dataIn) + +SIMD_FORCE_INLINE void btVector3::deSerialize(const struct btVector3FloatData& dataIn) +{ + for (int i = 0; i<4; i++) + m_floats[i] = (btScalar)dataIn.m_floats[i]; +} + + +SIMD_FORCE_INLINE void btVector3::deSerialize(const struct btVector3DoubleData& dataIn) { for (int i=0;i<4;i++) - m_floats[i] = dataIn.m_floats[i]; + m_floats[i] = (btScalar)dataIn.m_floats[i]; } #endif //BT_VECTOR3_H diff --git a/thirdparty/bullet/clew/clew.c b/thirdparty/bullet/clew/clew.c index a07b0aad75..5afc42a485 100644 --- a/thirdparty/bullet/clew/clew.c +++ b/thirdparty/bullet/clew/clew.c @@ -15,7 +15,7 @@ typedef HMODULE CLEW_DYNLIB_HANDLE; - #define CLEW_DYNLIB_OPEN LoadLibrary + #define CLEW_DYNLIB_OPEN LoadLibraryA #define CLEW_DYNLIB_CLOSE FreeLibrary #define CLEW_DYNLIB_IMPORT GetProcAddress #else diff --git a/thirdparty/cvtt/ConvectionKernels.cpp b/thirdparty/cvtt/ConvectionKernels.cpp index 6661d8120d..8d379344e1 100644 --- a/thirdparty/cvtt/ConvectionKernels.cpp +++ b/thirdparty/cvtt/ConvectionKernels.cpp @@ -233,7 +233,7 @@ namespace cvtt { __m128 m_values[2]; - inline Float operator+(const Float& other) const + inline Float operator+(const Float &other) const { Float result; result.m_values[0] = _mm_add_ps(m_values[0], other.m_values[0]); @@ -281,7 +281,7 @@ namespace cvtt return result; } - inline Float operator/(const Float& other) const + inline Float operator/(const Float &other) const { Float result; result.m_values[0] = _mm_div_ps(m_values[0], other.m_values[0]); @@ -302,14 +302,14 @@ namespace cvtt { __m128i m_value; - inline Int16CompFlag operator&(const Int16CompFlag& other) const + inline Int16CompFlag operator&(const Int16CompFlag &other) const { Int16CompFlag result; result.m_value = _mm_and_si128(m_value, other.m_value); return result; } - inline Int16CompFlag operator|(const Int16CompFlag& other) const + inline Int16CompFlag operator|(const Int16CompFlag &other) const { Int16CompFlag result; result.m_value = _mm_or_si128(m_value, other.m_value); @@ -338,7 +338,7 @@ namespace cvtt return result; } - static Float Select(FloatCompFlag flag, Float a, Float b) + static Float Select(const FloatCompFlag &flag, const Float &a, const Float &b) { Float result; for (int i = 0; i < 2; i++) @@ -347,7 +347,7 @@ namespace cvtt } template<int TSubtype> - static VInt16<TSubtype> Select(Int16CompFlag flag, const VInt16<TSubtype> &a, const VInt16<TSubtype> &b) + static VInt16<TSubtype> Select(const Int16CompFlag &flag, const VInt16<TSubtype> &a, const VInt16<TSubtype> &b) { VInt16<TSubtype> result; result.m_value = _mm_or_si128(_mm_and_si128(flag.m_value, a.m_value), _mm_andnot_si128(flag.m_value, b.m_value)); @@ -355,7 +355,7 @@ namespace cvtt } template<int TSubtype> - static VInt16<TSubtype> SelectOrZero(Int16CompFlag flag, const VInt16<TSubtype> &a) + static VInt16<TSubtype> SelectOrZero(const Int16CompFlag &flag, const VInt16<TSubtype> &a) { VInt16<TSubtype> result; result.m_value = _mm_and_si128(flag.m_value, a.m_value); @@ -363,12 +363,12 @@ namespace cvtt } template<int TSubtype> - static void ConditionalSet(VInt16<TSubtype>& dest, Int16CompFlag flag, const VInt16<TSubtype> src) + static void ConditionalSet(VInt16<TSubtype> &dest, const Int16CompFlag &flag, const VInt16<TSubtype> &src) { dest.m_value = _mm_or_si128(_mm_andnot_si128(flag.m_value, dest.m_value), _mm_and_si128(flag.m_value, src.m_value)); } - static SInt16 ConditionalNegate(const Int16CompFlag &flag, const SInt16& v) + static SInt16 ConditionalNegate(const Int16CompFlag &flag, const SInt16 &v) { SInt16 result; result.m_value = _mm_add_epi16(_mm_xor_si128(flag.m_value, v.m_value), _mm_srli_epi16(flag.m_value, 15)); @@ -376,18 +376,18 @@ namespace cvtt } template<int TSubtype> - static void NotConditionalSet(VInt16<TSubtype>& dest, Int16CompFlag flag, const VInt16<TSubtype> src) + static void NotConditionalSet(VInt16<TSubtype> &dest, const Int16CompFlag &flag, const VInt16<TSubtype> &src) { dest.m_value = _mm_or_si128(_mm_and_si128(flag.m_value, dest.m_value), _mm_andnot_si128(flag.m_value, src.m_value)); } - static void ConditionalSet(Float& dest, FloatCompFlag flag, const Float src) + static void ConditionalSet(Float &dest, const FloatCompFlag &flag, const Float &src) { for (int i = 0; i < 2; i++) dest.m_values[i] = _mm_or_ps(_mm_andnot_ps(flag.m_values[i], dest.m_values[i]), _mm_and_ps(flag.m_values[i], src.m_values[i])); } - static void NotConditionalSet(Float& dest, FloatCompFlag flag, const Float src) + static void NotConditionalSet(Float &dest, const FloatCompFlag &flag, const Float &src) { for (int i = 0; i < 2; i++) dest.m_values[i] = _mm_or_ps(_mm_and_ps(flag.m_values[i], dest.m_values[i]), _mm_andnot_ps(flag.m_values[i], src.m_values[i])); @@ -443,7 +443,7 @@ namespace cvtt return result; } - static Float Min(Float a, Float b) + static Float Min(const Float &a, const Float &b) { Float result; for (int i = 0; i < 2; i++) @@ -474,7 +474,7 @@ namespace cvtt return result; } - static Float Max(Float a, Float b) + static Float Max(const Float &a, const Float &b) { Float result; for (int i = 0; i < 2; i++) @@ -482,7 +482,7 @@ namespace cvtt return result; } - static Float Clamp(Float v, float min, float max) + static Float Clamp(const Float &v, float min, float max) { Float result; for (int i = 0; i < 2; i++) @@ -490,7 +490,7 @@ namespace cvtt return result; } - static Float Reciprocal(Float v) + static Float Reciprocal(const Float &v) { Float result; for (int i = 0; i < 2; i++) @@ -498,7 +498,7 @@ namespace cvtt return result; } - static void ConvertLDRInputs(const PixelBlockU8* inputBlocks, int pxOffset, int channel, UInt15& chOut) + static void ConvertLDRInputs(const PixelBlockU8* inputBlocks, int pxOffset, int channel, UInt15 &chOut) { int16_t values[8]; for (int i = 0; i < 8; i++) @@ -507,7 +507,7 @@ namespace cvtt chOut.m_value = _mm_set_epi16(values[7], values[6], values[5], values[4], values[3], values[2], values[1], values[0]); } - static void ConvertHDRInputs(const PixelBlockF16* inputBlocks, int pxOffset, int channel, SInt16& chOut) + static void ConvertHDRInputs(const PixelBlockF16* inputBlocks, int pxOffset, int channel, SInt16 &chOut) { int16_t values[8]; for (int i = 0; i < 8; i++) @@ -648,7 +648,7 @@ namespace cvtt return result; } - static FloatCompFlag LessOrEqual(Float a, Float b) + static FloatCompFlag LessOrEqual(const Float &a, const Float &b) { FloatCompFlag result; for (int i = 0; i < 2; i++) @@ -664,7 +664,7 @@ namespace cvtt return result; } - static FloatCompFlag Equal(Float a, Float b) + static FloatCompFlag Equal(const Float &a, const Float &b) { FloatCompFlag result; for (int i = 0; i < 2; i++) @@ -776,7 +776,7 @@ namespace cvtt return result; } - static UInt16 RoundAndConvertToU16(Float v, const void* /*roundingMode*/) + static UInt16 RoundAndConvertToU16(const Float &v, const void* /*roundingMode*/) { __m128i lo = _mm_cvtps_epi32(_mm_add_ps(v.m_values[0], _mm_set1_ps(-32768))); __m128i hi = _mm_cvtps_epi32(_mm_add_ps(v.m_values[1], _mm_set1_ps(-32768))); @@ -788,7 +788,7 @@ namespace cvtt return result; } - static UInt15 RoundAndConvertToU15(Float v, const void* /*roundingMode*/) + static UInt15 RoundAndConvertToU15(const Float &v, const void* /*roundingMode*/) { __m128i lo = _mm_cvtps_epi32(v.m_values[0]); __m128i hi = _mm_cvtps_epi32(v.m_values[1]); @@ -800,7 +800,7 @@ namespace cvtt return result; } - static SInt16 RoundAndConvertToS16(Float v, const void* /*roundingMode*/) + static SInt16 RoundAndConvertToS16(const Float &v, const void* /*roundingMode*/) { __m128i lo = _mm_cvtps_epi32(v.m_values[0]); __m128i hi = _mm_cvtps_epi32(v.m_values[1]); @@ -812,7 +812,7 @@ namespace cvtt return result; } - static Float Sqrt(Float f) + static Float Sqrt(const Float &f) { Float result; for (int i = 0; i < 2; i++) @@ -1071,22 +1071,22 @@ namespace cvtt return XMultiply(b, a); } - static bool AnySet(Int16CompFlag v) + static bool AnySet(const Int16CompFlag &v) { return _mm_movemask_epi8(v.m_value) != 0; } - static bool AllSet(Int16CompFlag v) + static bool AllSet(const Int16CompFlag &v) { return _mm_movemask_epi8(v.m_value) == 0xffff; } - static bool AnySet(FloatCompFlag v) + static bool AnySet(const FloatCompFlag &v) { return _mm_movemask_ps(v.m_values[0]) != 0 || _mm_movemask_ps(v.m_values[1]) != 0; } - static bool AllSet(FloatCompFlag v) + static bool AllSet(const FloatCompFlag &v) { return _mm_movemask_ps(v.m_values[0]) == 0xf && _mm_movemask_ps(v.m_values[1]) == 0xf; } @@ -1420,6 +1420,16 @@ namespace cvtt return v; } + static bool MakeBoolInt16(bool b) + { + return b; + } + + static bool MakeBoolFloat(bool b) + { + return b; + } + static bool AndNot(bool a, bool b) { return a && !b; @@ -2562,7 +2572,7 @@ namespace cvtt } }; - void ComputeTweakFactors2(int tweak, int range, float* outFactors) + void ComputeTweakFactors(int tweak, int range, float *outFactors) { int totalUnits = range - 1; int minOutsideUnits = ((tweak >> 1) & 1); @@ -2573,7 +2583,7 @@ namespace cvtt outFactors[1] = static_cast<float>(maxOutsideUnits) / static_cast<float>(insideUnits) + 1.0f; } - ParallelMath::Float ScaleHDRValue(ParallelMath::Float v, bool isSigned) + ParallelMath::Float ScaleHDRValue(const ParallelMath::Float &v, bool isSigned) { if (isSigned) { @@ -2591,7 +2601,7 @@ namespace cvtt assert(ParallelMath::Extract(v, i) != -32768) #endif - ParallelMath::Int16CompFlag negative = ParallelMath::Less(v, ParallelMath::MakeSInt16(0)); + ParallelMath::Int16CompFlag negative = ParallelMath::Less(v, ParallelMath::MakeSInt16(0)); ParallelMath::UInt15 absComp = ParallelMath::LosslessCast<ParallelMath::UInt15>::Cast(ParallelMath::Select(negative, ParallelMath::SInt16(ParallelMath::MakeSInt16(0) - v), v)); ParallelMath::UInt31 multiplied = ParallelMath::XMultiply(absComp, ParallelMath::MakeUInt15(31)); @@ -2635,7 +2645,7 @@ namespace cvtt { } - UnfinishedEndpoints(const MFloat base[TVectorSize], const MFloat offset[TVectorSize]) + UnfinishedEndpoints(const MFloat *base, const MFloat *offset) { for (int ch = 0; ch < TVectorSize; ch++) m_base[ch] = base[ch]; @@ -2651,10 +2661,10 @@ namespace cvtt m_offset[ch] = other.m_offset[ch]; } - void FinishHDRUnsigned(int tweak, int range, MSInt16* outEP0, MSInt16* outEP1, ParallelMath::RoundTowardNearestForScope* roundingMode) + void FinishHDRUnsigned(int tweak, int range, MSInt16 *outEP0, MSInt16 *outEP1, ParallelMath::RoundTowardNearestForScope *roundingMode) { float tweakFactors[2]; - ComputeTweakFactors2(tweak, range, tweakFactors); + ComputeTweakFactors(tweak, range, tweakFactors); for (int ch = 0; ch < TVectorSize; ch++) { @@ -2673,7 +2683,7 @@ namespace cvtt void FinishHDRSigned(int tweak, int range, MSInt16* outEP0, MSInt16* outEP1, ParallelMath::RoundTowardNearestForScope* roundingMode) { float tweakFactors[2]; - ComputeTweakFactors2(tweak, range, tweakFactors); + ComputeTweakFactors(tweak, range, tweakFactors); for (int ch = 0; ch < TVectorSize; ch++) { @@ -2694,7 +2704,7 @@ namespace cvtt ParallelMath::RoundTowardNearestForScope roundingMode; float tweakFactors[2]; - ComputeTweakFactors2(tweak, range, tweakFactors); + ComputeTweakFactors(tweak, range, tweakFactors); for (int ch = 0; ch < TVectorSize; ch++) { @@ -2752,7 +2762,7 @@ namespace cvtt m_values[i] = ParallelMath::MakeFloatZero(); } - void Add(const ParallelMath::Float vec[TMatrixSize], ParallelMath::Float weight) + void Add(const ParallelMath::Float *vec, const ParallelMath::Float &weight) { int index = 0; for (int row = 0; row < TMatrixSize; row++) @@ -2765,7 +2775,7 @@ namespace cvtt } } - void Product(MFloat outVec[TMatrixSize], const MFloat inVec[TMatrixSize]) + void Product(MFloat *outVec, const MFloat *inVec) { for (int row = 0; row < TMatrixSize; row++) { @@ -2809,7 +2819,7 @@ namespace cvtt m_maxDist = ParallelMath::MakeFloat(-FLT_MAX); } - void ContributePass(const MFloat value[TVectorSize], int pass, MFloat weight) + void ContributePass(const MFloat *value, int pass, const MFloat &weight) { if (pass == 0) ContributeCentroid(value, weight); @@ -2849,7 +2859,7 @@ namespace cvtt } private: - void ContributeCentroid(const MFloat value[TVectorSize], MFloat weight) + void ContributeCentroid(const MFloat *value, const MFloat &weight) { for (int ch = 0; ch < TVectorSize; ch++) m_centroid[ch] = m_centroid[ch] + value[ch] * weight; @@ -2865,7 +2875,7 @@ namespace cvtt m_centroid[ch] = m_centroid[ch] / denom; } - void ContributeDirection(const MFloat value[TVectorSize], MFloat weight) + void ContributeDirection(const MFloat *value, const MFloat &weight) { MFloat diff[TVectorSize]; for (int ch = 0; ch < TVectorSize; ch++) @@ -2908,7 +2918,7 @@ namespace cvtt m_direction[ch] = approx[ch] / approxLen; } - void ContributeMinMax(const MFloat value[TVectorSize]) + void ContributeMinMax(const MFloat *value) { MFloat dist = ParallelMath::MakeFloatZero(); for (int ch = 0; ch < TVectorSize; ch++) @@ -2961,7 +2971,7 @@ namespace cvtt typedef ParallelMath::UInt31 MUInt31; template<class TInterpolationEPType, class TColorEPType> - void Init(const float channelWeights[TVectorSize], const TInterpolationEPType interpolationEndPoints[2][TVectorSize], const TColorEPType colorSpaceEndpoints[2][TVectorSize], int range) + void Init(const float *channelWeights, const TInterpolationEPType interpolationEndPoints[2][TVectorSize], const TColorEPType colorSpaceEndpoints[2][TVectorSize], int range) { // In BC6H, the interpolation endpoints are higher-precision than the endpoints in color space. // We need to select indexes using the color-space endpoints. @@ -3184,7 +3194,7 @@ namespace cvtt ReconstructHDRUnsignedUninverted(InvertSingle(index), pixel); } - void ConditionalInvert(ParallelMath::Int16CompFlag invert) + void ConditionalInvert(const ParallelMath::Int16CompFlag &invert) { m_isInverted = invert; } @@ -3268,7 +3278,7 @@ namespace cvtt m_wu = 0; } - void ContributePW(const MFloat *pwFloatPixel, const MUInt15 &index, MFloat weight) + void ContributePW(const MFloat *pwFloatPixel, const MUInt15 &index, const MFloat &weight) { MFloat t = ParallelMath::ToFloat(index) * m_rcpMaxIndex; @@ -3555,7 +3565,7 @@ namespace cvtt ParallelMath::RoundTowardNearestForScope roundingMode; float tf[2]; - ComputeTweakFactors2(tweak, range, tf); + ComputeTweakFactors(tweak, range, tf); MFloat base = ParallelMath::ToFloat(original[0]); MFloat offs = ParallelMath::ToFloat(original[1]) - base; @@ -5390,7 +5400,7 @@ namespace cvtt else if (numRefineRounds > MaxRefineRounds) numRefineRounds = MaxRefineRounds; - bool fastIndexing = (flags & cvtt::Flags::BC6H_FastIndexing); + bool fastIndexing = ((flags & cvtt::Flags::BC6H_FastIndexing) != 0); float channelWeightsSq[3]; ParallelMath::RoundTowardNearestForScope rtn; @@ -6550,7 +6560,7 @@ namespace cvtt } } - static void TestCounts(uint32_t flags, const int *counts, int nCounts, MUInt15 numElements, const MUInt15 pixels[16][4], const MFloat floatPixels[16][4], const MFloat preWeightedPixels[16][4], bool alphaTest, + static void TestCounts(uint32_t flags, const int *counts, int nCounts, const MUInt15 &numElements, const MUInt15 pixels[16][4], const MFloat floatPixels[16][4], const MFloat preWeightedPixels[16][4], bool alphaTest, const MFloat floatSortedInputs[16][4], const MFloat preWeightedFloatSortedInputs[16][4], const float *channelWeights, MFloat &bestError, MUInt15 bestEndpoints[2][3], MUInt15 bestIndexes[16], MUInt15 &bestRange, const ParallelMath::RoundTowardNearestForScope* rtn) { diff --git a/thirdparty/enet/godot.cpp b/thirdparty/enet/godot.cpp index 7813b70286..6ba7cf0000 100644 --- a/thirdparty/enet/godot.cpp +++ b/thirdparty/enet/godot.cpp @@ -108,7 +108,7 @@ int enet_socket_bind(ENetSocket socket, const ENetAddress *address) { ENetSocket enet_socket_create(ENetSocketType type) { - PacketPeerUDP *socket = PacketPeerUDP::create(); + PacketPeerUDP *socket = memnew(PacketPeerUDP); socket->set_blocking_mode(false); return socket; @@ -151,7 +151,7 @@ int enet_socket_send(ENetSocket socket, const ENetAddress *address, const ENetBu err = sock->put_packet((const uint8_t *)&w[0], size); if (err != OK) { - if (err == ERR_UNAVAILABLE) { // blocking call + if (err == ERR_BUSY) { // Blocking call return 0; } @@ -168,8 +168,9 @@ int enet_socket_receive(ENetSocket socket, ENetAddress *address, ENetBuffer *buf PacketPeerUDP *sock = (PacketPeerUDP *)socket; - if (sock->get_available_packet_count() == 0) { - return 0; + int pc = sock->get_available_packet_count(); + if (pc < 1) { + return pc; } const uint8_t *buffer; diff --git a/thirdparty/fonts/NotoSansDevanagariUI_Regular.ttf b/thirdparty/fonts/NotoSansDevanagariUI_Regular.ttf Binary files differnew file mode 100644 index 0000000000..1f9fb2e857 --- /dev/null +++ b/thirdparty/fonts/NotoSansDevanagariUI_Regular.ttf diff --git a/thirdparty/libogg/ogg/config_types.h b/thirdparty/libogg/ogg/config_types.h index 5ea49b8abd..e630657547 100644 --- a/thirdparty/libogg/ogg/config_types.h +++ b/thirdparty/libogg/ogg/config_types.h @@ -1,7 +1,7 @@ #ifndef __CONFIG_TYPES_H__ #define __CONFIG_TYPES_H__ -#include "int_types.h" +#include "core/int_types.h" typedef int16_t ogg_int16_t; typedef uint16_t ogg_uint16_t; diff --git a/thirdparty/libvpx/vpx_config.h b/thirdparty/libvpx/vpx_config.h index fb9e13c4ad..6caec50c81 100644 --- a/thirdparty/libvpx/vpx_config.h +++ b/thirdparty/libvpx/vpx_config.h @@ -67,6 +67,12 @@ #define CONFIG_BIG_ENDIAN 0 //TODO: Autodetect +#ifdef __EMSCRIPTEN__ +#define CONFIG_MULTITHREAD 0 +#else +#define CONFIG_MULTITHREAD 1 +#endif + #ifdef _WIN32 #define HAVE_PTHREAD_H 0 #define HAVE_UNISTD_H 0 @@ -95,7 +101,6 @@ #define CONFIG_RUNTIME_CPU_DETECT 1 #define CONFIG_POSTPROC 0 #define CONFIG_VP9_POSTPROC 0 -#define CONFIG_MULTITHREAD 1 #define CONFIG_INTERNAL_STATS 0 #define CONFIG_VP8_ENCODER 0 #define CONFIG_VP8_DECODER 1 diff --git a/thirdparty/libwebsockets/core/context.c b/thirdparty/libwebsockets/core/context.c index db9151b95f..7be004df33 100644 --- a/thirdparty/libwebsockets/core/context.c +++ b/thirdparty/libwebsockets/core/context.c @@ -134,7 +134,7 @@ lws_protocol_vh_priv_get(struct lws_vhost *vhost, { int n = 0; - if (!vhost || !vhost->protocol_vh_privs) + if (!vhost || !vhost->protocol_vh_privs || !prot) return NULL; while (n < vhost->count_protocols && &vhost->protocols[n] != prot) @@ -808,7 +808,7 @@ lws_create_vhost(struct lws_context *context, #ifdef LWS_WITH_ACCESS_LOG if (info->log_filepath) { - vh->log_fd = open(info->log_filepath, + vh->log_fd = lws_open(info->log_filepath, O_CREAT | O_APPEND | O_RDWR, 0600); if (vh->log_fd == (int)LWS_INVALID_FILE) { lwsl_err("unable to open log filepath %s\n", @@ -936,24 +936,29 @@ lws_create_event_pipes(struct lws_context *context) wsi->tsi = n; wsi->vhost = NULL; wsi->event_pipe = 1; + wsi->desc.sockfd = LWS_SOCK_INVALID; + context->pt[n].pipe_wsi = wsi; + context->count_wsi_allocated++; - if (lws_plat_pipe_create(wsi)) { - lws_free(wsi); + if (lws_plat_pipe_create(wsi)) + /* + * platform code returns 0 if it actually created pipes + * and initialized pt->dummy_pipe_fds[]. If it used + * some other mechanism outside of signaling in the + * normal event loop, we skip treating the pipe as + * related to dummy_pipe_fds[], adding it to the fds, + * etc. + */ continue; - } + wsi->desc.sockfd = context->pt[n].dummy_pipe_fds[0]; lwsl_debug("event pipe fd %d\n", wsi->desc.sockfd); - context->pt[n].pipe_wsi = wsi; - if (context->event_loop_ops->accept) context->event_loop_ops->accept(wsi); if (__insert_wsi_socket_into_fds(context, wsi)) return 1; - - //lws_change_pollfd(context->pt[n].pipe_wsi, 0, LWS_POLLIN); - context->count_wsi_allocated++; } return 0; diff --git a/thirdparty/libwebsockets/core/libwebsockets.c b/thirdparty/libwebsockets/core/libwebsockets.c index 0da02b17e4..58f00226f6 100644 --- a/thirdparty/libwebsockets/core/libwebsockets.c +++ b/thirdparty/libwebsockets/core/libwebsockets.c @@ -28,7 +28,7 @@ #ifdef LWS_WITH_IPV6 #if defined(WIN32) || defined(_WIN32) #include <wincrypt.h> -#include <Iphlpapi.h> +#include <iphlpapi.h> #else #include <net/if.h> #endif @@ -58,6 +58,28 @@ static const char * const log_level_names[] = { }; #endif +int lws_open(const char *__file, int __oflag, ...) +{ + va_list ap; + int n; + + va_start(ap, __oflag); + if (((__oflag & O_CREAT) == O_CREAT) +#if defined(O_TMPFILE) + || ((__oflag & O_TMPFILE) == O_TMPFILE) +#endif + ) + /* last arg is really a mode_t. But windows... */ + n = open(__file, __oflag, va_arg(ap, uint32_t)); + else + n = open(__file, __oflag); + va_end(ap); + + lws_plat_apply_FD_CLOEXEC(n); + + return n; +} + #if defined (_DEBUG) void lwsi_set_role(struct lws *wsi, lws_wsi_state_t role) { @@ -826,7 +848,15 @@ just_kill_connection: if (!wsi->protocol) pro = &wsi->vhost->protocols[0]; - pro->callback(wsi, + if (!wsi->upgraded_to_http2 || !lwsi_role_client(wsi)) + /* + * The network wsi for a client h2 connection shouldn't + * call back for its role: the child stream connections + * own the role. Otherwise h2 will call back closed + * one too many times as the children do it and then + * the closing network stream. + */ + pro->callback(wsi, wsi->role_ops->close_cb[lwsi_role_server(wsi)], wsi->user_space, NULL, 0); wsi->told_user_closed = 1; @@ -1453,7 +1483,7 @@ lws_vfs_select_fops(const struct lws_plat_file_ops *fops, const char *vfs_path, pf = fops->next; while (pf) { n = 0; - while (n < (int)ARRAY_SIZE(pf->fi) && pf->fi[n].sig) { + while (n < (int)LWS_ARRAY_SIZE(pf->fi) && pf->fi[n].sig) { if (p >= vfs_path + pf->fi[n].len) if (!strncmp(p - (pf->fi[n].len - 1), pf->fi[n].sig, @@ -1935,9 +1965,9 @@ static const char * const colours[] = { "[32;1m", /* LLL_INFO */ "[34;1m", /* LLL_DEBUG */ "[33;1m", /* LLL_PARSER */ - "[33;1m", /* LLL_HEADER */ - "[33;1m", /* LLL_EXT */ - "[33;1m", /* LLL_CLIENT */ + "[33m", /* LLL_HEADER */ + "[33m", /* LLL_EXT */ + "[33m", /* LLL_CLIENT */ "[33;1m", /* LLL_LATENCY */ "[30;1m", /* LLL_USER */ }; @@ -1946,14 +1976,14 @@ LWS_VISIBLE void lwsl_emit_stderr(int level, const char *line) { char buf[50]; static char tty = 3; - int n, m = ARRAY_SIZE(colours) - 1; + int n, m = LWS_ARRAY_SIZE(colours) - 1; if (!tty) tty = isatty(2) | 2; lwsl_timestamp(level, buf, sizeof(buf)); if (tty == 3) { - n = 1 << (ARRAY_SIZE(colours) - 1); + n = 1 << (LWS_ARRAY_SIZE(colours) - 1); while (n) { if (level & n) break; @@ -2060,7 +2090,9 @@ lwsl_hexdump_level(int hexdump_level, const void *vbuf, size_t len) LWS_VISIBLE void lwsl_hexdump(const void *vbuf, size_t len) { +#if defined(_DEBUG) lwsl_hexdump_level(LLL_DEBUG, vbuf, len); +#endif } LWS_VISIBLE int @@ -2091,6 +2123,8 @@ lws_partial_buffered(struct lws *wsi) LWS_VISIBLE lws_fileofs_t lws_get_peer_write_allowance(struct lws *wsi) { + if (!wsi->role_ops->tx_credit) + return -1; return wsi->role_ops->tx_credit(wsi); } @@ -3364,7 +3398,7 @@ lws_stats_log_dump(struct lws_context *context) wl = pt->http.ah_wait_list; while (wl) { m++; - wl = wl->ah_wait_list; + wl = wl->http.ah_wait_list; } lwsl_notice(" AH wait list count / actual: %d / %d\n", @@ -3401,7 +3435,8 @@ lws_stats_log_dump(struct lws_context *context) strcpy(buf, "unknown"); #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) lwsl_notice(" peer %s: count wsi: %d, count ah: %d\n", - buf, df->count_wsi, df->count_ah); + buf, df->count_wsi, + df->http.count_ah); #else lwsl_notice(" peer %s: count wsi: %d\n", buf, df->count_wsi); diff --git a/thirdparty/libwebsockets/core/output.c b/thirdparty/libwebsockets/core/output.c index e2ff18ef27..11965a06b9 100644 --- a/thirdparty/libwebsockets/core/output.c +++ b/thirdparty/libwebsockets/core/output.c @@ -126,6 +126,18 @@ int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len) lwsl_info("** %p signalling to close now\n", wsi); return -1; /* retry closing now */ } + +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) +#if !defined(LWS_WITHOUT_SERVER) + if (wsi->http.deferred_transaction_completed) { + lwsl_notice("%s: partial completed, doing " + "deferred transaction completed\n", + __func__); + wsi->http.deferred_transaction_completed = 0; + return lws_http_transaction_completed(wsi); + } +#endif +#endif } /* always callback on writeable */ lws_callback_on_writable(wsi); diff --git a/thirdparty/libwebsockets/core/private.h b/thirdparty/libwebsockets/core/private.h index d6b494ac8c..73748b0498 100644 --- a/thirdparty/libwebsockets/core/private.h +++ b/thirdparty/libwebsockets/core/private.h @@ -232,7 +232,7 @@ #endif #else /* not windows */ - static inline int compatible_close(int fd) { return close(fd); } + static LWS_INLINE int compatible_close(int fd) { return close(fd); } #include <sys/stat.h> #include <sys/time.h> @@ -351,7 +351,15 @@ extern "C" { #define LWS_H2_RX_SCRATCH_SIZE 512 - +#if defined(WIN32) || defined(_WIN32) + // Visual studio older than 2015 and WIN_CE has only _stricmp + #if (defined(_MSC_VER) && _MSC_VER < 1900) || defined(_WIN32_WCE) + #define strcasecmp _stricmp + #elif !defined(__MINGW32__) + #define strcasecmp stricmp + #endif + #define getdtablesize() 30000 +#endif /* * All lws_tls...() functions must return this type, converting the @@ -863,6 +871,7 @@ struct lws_context { unsigned int timeout_secs; unsigned int pt_serv_buf_size; int max_http_header_data; + int max_http_header_pool; int simultaneous_ssl_restriction; int simultaneous_ssl; #if defined(LWS_WITH_PEER_LIMITS) @@ -889,7 +898,6 @@ struct lws_context { volatile int service_tid; int service_tid_detected; - short max_http_header_pool; short count_threads; short plugin_protocol_count; short plugin_extension_count; @@ -1216,7 +1224,7 @@ LWS_EXTERN int lws_rxflow_cache(struct lws *wsi, unsigned char *buf, int n, int len); #ifndef LWS_LATENCY -static inline void +static LWS_INLINE void lws_latency(struct lws_context *context, struct lws *wsi, const char *action, int ret, int completion) { do { @@ -1224,7 +1232,7 @@ lws_latency(struct lws_context *context, struct lws *wsi, const char *action, (void)completion; } while (0); } -static inline void +static LWS_INLINE void lws_latency_pre(struct lws_context *context, struct lws *wsi) { do { (void)context; (void)wsi; } while (0); } @@ -1597,6 +1605,9 @@ void lws_free(void *p); #define lws_free_set_NULL(P) do { lws_realloc(P, 0, "free"); (P) = NULL; } while(0) #endif +char * +lws_strdup(const char *s); + int lws_plat_pipe_create(struct lws *wsi); int @@ -1606,6 +1617,9 @@ lws_plat_pipe_close(struct lws *wsi); int lws_create_event_pipes(struct lws_context *context); +int lws_open(const char *__file, int __oflag, ...); +void lws_plat_apply_FD_CLOEXEC(int n); + const struct lws_plat_file_ops * lws_vfs_select_fops(const struct lws_plat_file_ops *fops, const char *vfs_path, const char **vpath); @@ -1673,10 +1687,10 @@ lws_broadcast(struct lws_context *context, int reason, void *in, size_t len); lws_stats_atomic_max(struct lws_context * context, struct lws_context_per_thread *pt, int index, uint64_t val); #else - static inline uint64_t lws_stats_atomic_bump(struct lws_context * context, + static LWS_INLINE uint64_t lws_stats_atomic_bump(struct lws_context * context, struct lws_context_per_thread *pt, int index, uint64_t bump) { (void)context; (void)pt; (void)index; (void)bump; return 0; } - static inline uint64_t lws_stats_atomic_max(struct lws_context * context, + static LWS_INLINE uint64_t lws_stats_atomic_max(struct lws_context * context, struct lws_context_per_thread *pt, int index, uint64_t val) { (void)context; (void)pt; (void)index; (void)val; return 0; } #endif @@ -1703,6 +1717,11 @@ void lws_peer_dump_from_wsi(struct lws *wsi); #endif +#ifdef LWS_WITH_HTTP_PROXY +hubbub_error +html_parser_cb(const hubbub_token *token, void *pw); +#endif + void __lws_remove_from_timeout_list(struct lws *wsi); diff --git a/thirdparty/libwebsockets/libwebsockets.h b/thirdparty/libwebsockets/libwebsockets.h index 7ae563d582..2c01696404 100644 --- a/thirdparty/libwebsockets/libwebsockets.h +++ b/thirdparty/libwebsockets/libwebsockets.h @@ -66,14 +66,6 @@ typedef unsigned long long lws_intptr_t; #define O_RDONLY _O_RDONLY #endif -// Visual studio older than 2015 and WIN_CE has only _stricmp -#if (defined(_MSC_VER) && _MSC_VER < 1900) || defined(_WIN32_WCE) -#define strcasecmp _stricmp -#elif !defined(__MINGW32__) -#define strcasecmp stricmp -#endif -#define getdtablesize() 30000 - #define LWS_INLINE __inline #define LWS_VISIBLE #define LWS_WARN_UNUSED_RESULT @@ -150,6 +142,7 @@ typedef unsigned long long lws_intptr_t; #endif #if defined(__ANDROID__) +#include <netinet/in.h> #include <unistd.h> #define getdtablesize() sysconf(_SC_OPEN_MAX) #endif @@ -164,6 +157,9 @@ typedef unsigned long long lws_intptr_t; #ifdef LWS_HAVE_UV_VERSION_H #include <uv-version.h> #endif +#ifdef LWS_HAVE_NEW_UV_VERSION_H +#include <uv/version.h> +#endif #endif /* LWS_WITH_LIBUV */ #if defined(LWS_WITH_LIBEVENT) #include <event2/event.h> @@ -456,9 +452,6 @@ lwsl_visible(int level); #endif struct lws; -#ifndef ARRAY_SIZE -#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) -#endif typedef int64_t lws_usec_t; @@ -540,7 +533,7 @@ struct timer_mapping { #define lws_uv_getloop(a, b) (NULL) -static inline void uv_timer_init(void *l, uv_timer_t *t) +static LWS_INLINE void uv_timer_init(void *l, uv_timer_t *t) { (void)l; *t = NULL; @@ -548,7 +541,7 @@ static inline void uv_timer_init(void *l, uv_timer_t *t) extern void esp32_uvtimer_cb(TimerHandle_t t); -static inline void uv_timer_start(uv_timer_t *t, uv_cb_t *cb, int first, int rep) +static LWS_INLINE void uv_timer_start(uv_timer_t *t, uv_cb_t *cb, int first, int rep) { struct timer_mapping *tm = (struct timer_mapping *)malloc(sizeof(*tm)); @@ -563,12 +556,12 @@ static inline void uv_timer_start(uv_timer_t *t, uv_cb_t *cb, int first, int rep xTimerStart(*t, 0); } -static inline void uv_timer_stop(uv_timer_t *t) +static LWS_INLINE void uv_timer_stop(uv_timer_t *t) { xTimerStop(*t, 0); } -static inline void uv_close(uv_handle_t *h, void *v) +static LWS_INLINE void uv_close(uv_handle_t *h, void *v) { free(pvTimerGetTimerID((uv_timer_t)h)); xTimerDelete(*(uv_timer_t *)h, 0); @@ -1757,7 +1750,7 @@ lws_genhash_destroy(struct lws_genhash_ctx *ctx, void *result); * If the return is nonzero, it failed and there is nothing needing to be * destroyed. */ -int +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT lws_genhmac_init(struct lws_genhmac_ctx *ctx, enum lws_genhmac_types type, const uint8_t *key, size_t key_len); @@ -1771,7 +1764,7 @@ lws_genhmac_init(struct lws_genhmac_ctx *ctx, enum lws_genhmac_types type, * * If the return is nonzero, it failed and needs destroying. */ -int +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT lws_genhmac_update(struct lws_genhmac_ctx *ctx, const void *in, size_t len); /** lws_genhmac_destroy() - copy out the result digest and destroy the ctx @@ -1785,7 +1778,7 @@ lws_genhmac_update(struct lws_genhmac_ctx *ctx, const void *in, size_t len); * NULL result is supported so that you can destroy the ctx cleanly on error * conditions, where there is no valid result. */ -int +LWS_VISIBLE LWS_EXTERN int lws_genhmac_destroy(struct lws_genhmac_ctx *ctx, void *result); ///@} @@ -2812,7 +2805,7 @@ struct lws_context_creation_info { /**< VHOST: pointer to optional linked list of per-vhost * options made accessible to protocols */ int keepalive_timeout; - /**< VHOST: (default = 0 = 60s) seconds to allow remote + /**< VHOST: (default = 0 = 5s) seconds to allow remote * client to hold on to an idle HTTP/1.1 connection */ const char *log_filepath; /**< VHOST: filepath to append logs to... this is opened before @@ -4943,7 +4936,7 @@ lws_write(struct lws *wsi, unsigned char *buf, size_t len, lws_write(wsi, (unsigned char *)(buf), len, LWS_WRITE_HTTP) /* helper for multi-frame ws message flags */ -static inline int +static LWS_INLINE int lws_write_ws_flags(int initial, int is_start, int is_end) { int r; @@ -5621,13 +5614,13 @@ struct lws_dll_lws { /* typed as struct lws * */ #define lws_dll_is_null(___dll) (!(___dll)->prev && !(___dll)->next) -static inline void +static LWS_INLINE void lws_dll_lws_add_front(struct lws_dll_lws *_a, struct lws_dll_lws *_head) { lws_dll_add_front((struct lws_dll *)_a, (struct lws_dll *)_head); } -static inline void +static LWS_INLINE void lws_dll_lws_remove(struct lws_dll_lws *_a) { lws_dll_remove((struct lws_dll *)_a); @@ -7064,9 +7057,6 @@ lws_email_destroy(struct lws_email *email); //@{ struct lejp_ctx; -#ifndef ARRAY_SIZE -#define ARRAY_SIZE(_x) (sizeof(_x) / sizeof(_x[0])) -#endif #define LWS_ARRAY_SIZE(_x) (sizeof(_x) / sizeof(_x[0])) #define LEJP_FLAG_WS_KEEP 64 #define LEJP_FLAG_WS_COMMENTLINE 32 @@ -7219,7 +7209,7 @@ typedef signed char (*lejp_callback)(struct lejp_ctx *ctx, char reason); #endif #ifndef LEJP_STRING_CHUNK /* must be >= 30 to assemble floats */ -#define LEJP_STRING_CHUNK 255 +#define LEJP_STRING_CHUNK 254 #endif enum num_flags { @@ -7253,7 +7243,7 @@ struct lejp_ctx { uint16_t i[LEJP_MAX_INDEX_DEPTH]; /* index array */ uint16_t wild[LEJP_MAX_INDEX_DEPTH]; /* index array */ char path[LEJP_MAX_PATH]; - char buf[LEJP_STRING_CHUNK]; + char buf[LEJP_STRING_CHUNK + 1]; /* int */ diff --git a/thirdparty/libwebsockets/lws_config.h b/thirdparty/libwebsockets/lws_config.h index e5e15cc2fd..86ce9ac38a 100644 --- a/thirdparty/libwebsockets/lws_config.h +++ b/thirdparty/libwebsockets/lws_config.h @@ -174,7 +174,7 @@ #define LWS_HAVE_MALLOC_H #endif -#if !defined(IPHONE_ENABLED) && !defined(OSX_ENABLED) && !defined(__HAIKU__) +#if !defined(__APPLE__) && !defined(__HAIKU__) #define LWS_HAVE_PIPE2 #endif diff --git a/thirdparty/libwebsockets/lws_config_private.h b/thirdparty/libwebsockets/lws_config_private.h index b26d225afa..e531777624 100644 --- a/thirdparty/libwebsockets/lws_config_private.h +++ b/thirdparty/libwebsockets/lws_config_private.h @@ -81,7 +81,7 @@ /* Define to 1 if you have the <sys/prctl.h> header file. */ #define LWS_HAVE_SYS_PRCTL_H -#if defined(OSX_ENABLED) || defined(IPHONE_ENABLED) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__HAIKU__) +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__HAIKU__) #undef LWS_HAVE_SYS_PRCTL_H #endif diff --git a/thirdparty/libwebsockets/misc/lejp.c b/thirdparty/libwebsockets/misc/lejp.c index 00d350f81e..99142b9553 100644 --- a/thirdparty/libwebsockets/misc/lejp.c +++ b/thirdparty/libwebsockets/misc/lejp.c @@ -20,6 +20,7 @@ */ #include <libwebsockets.h> +#include "core/private.h" #include <string.h> #include <stdio.h> @@ -30,7 +31,7 @@ * \param callback: your user callback which will received parsed tokens * \param user: optional user data pointer untouched by lejp * \param paths: your array of name elements you are interested in - * \param count_paths: ARRAY_SIZE() of @paths + * \param count_paths: LWS_ARRAY_SIZE() of @paths * * Prepares your context struct for use with lejp */ @@ -250,7 +251,7 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len) case LEJP_MP_STRING: if (c == '\"') { - if (!ctx->sp) { + if (!ctx->sp) { /* JSON can't end on quote */ ret = LEJP_REJECT_MP_STRING_UNDERRUN; goto reject; } @@ -417,7 +418,7 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len) goto reject; } ctx->i[ctx->ipos++] = 0; - if (ctx->ipos > ARRAY_SIZE(ctx->i)) { + if (ctx->ipos > LWS_ARRAY_SIZE(ctx->i)) { ret = LEJP_REJECT_MP_DELIM_ISTACK; goto reject; } @@ -425,17 +426,23 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len) case ']': /* pop */ + if (!ctx->sp) { /* JSON can't end on ] */ + ret = LEJP_REJECT_MP_C_OR_E_UNDERF; + goto reject; + } ctx->sp--; if (ctx->st[ctx->sp].s != LEJP_MP_ARRAY_END) { ret = LEJP_REJECT_MP_C_OR_E_NOTARRAY; goto reject; } /* drop the path [n] bit */ - ctx->ppos = ctx->st[ctx->sp - 1].p; - ctx->ipos = ctx->st[ctx->sp - 1].i; + if (ctx->sp) { + ctx->ppos = ctx->st[ctx->sp - 1].p; + ctx->ipos = ctx->st[ctx->sp - 1].i; + } ctx->path[ctx->ppos] = '\0'; if (ctx->path_match && - ctx->ppos <= ctx->path_match_len) + ctx->ppos <= ctx->path_match_len) /* * we shrank the path to be * smaller than the matching point @@ -603,7 +610,7 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len) break; } if (c == ']') { - if (!ctx->sp) { + if (!ctx->sp) { /* JSON can't end on ] */ ret = LEJP_REJECT_MP_C_OR_E_UNDERF; goto reject; } @@ -631,7 +638,7 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len) goto redo_character; } if (c == '}') { - if (ctx->sp == 0) { + if (!ctx->sp) { lejp_check_path_match(ctx); if (ctx->callback(ctx, LEJPCB_OBJECT_END)) { ret = LEJP_REJECT_CALLBACK; @@ -716,7 +723,7 @@ add_stack_level: ctx->st[ctx->sp].p = ctx->ppos; ctx->st[ctx->sp].i = ctx->ipos; - if (++ctx->sp == ARRAY_SIZE(ctx->st)) { + if (++ctx->sp == LWS_ARRAY_SIZE(ctx->st)) { ret = LEJP_REJECT_STACK_OVERFLOW; goto reject; } diff --git a/thirdparty/libwebsockets/plat/lws-plat-unix.c b/thirdparty/libwebsockets/plat/lws-plat-unix.c index bacc6af647..7dba3bd82f 100644 --- a/thirdparty/libwebsockets/plat/lws-plat-unix.c +++ b/thirdparty/libwebsockets/plat/lws-plat-unix.c @@ -30,6 +30,12 @@ #endif #include <dirent.h> +void lws_plat_apply_FD_CLOEXEC(int n) +{ + if (n != -1) + fcntl(n, F_SETFD, FD_CLOEXEC ); +} + int lws_plat_socket_offset(void) { @@ -330,6 +336,8 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, int fd) struct protoent *tcp_proto; #endif + fcntl(fd, F_SETFD, FD_CLOEXEC); + if (vhost->ka_time) { /* enable keepalive on this socket */ optval = 1; @@ -952,7 +960,7 @@ lws_plat_write_file(const char *filename, void *buf, int len) LWS_VISIBLE int lws_plat_read_file(const char *filename, void *buf, int len) { - int n, fd = open(filename, O_RDONLY); + int n, fd = lws_open(filename, O_RDONLY); if (fd == -1) return -1; diff --git a/thirdparty/libwebsockets/plat/lws-plat-win.c b/thirdparty/libwebsockets/plat/lws-plat-win.c index 948db62896..1850b64250 100644 --- a/thirdparty/libwebsockets/plat/lws-plat-win.c +++ b/thirdparty/libwebsockets/plat/lws-plat-win.c @@ -3,6 +3,10 @@ #endif #include "core/private.h" +void lws_plat_apply_FD_CLOEXEC(int n) +{ +} + int lws_plat_socket_offset(void) { @@ -54,7 +58,7 @@ time_in_microseconds() memcpy(&datetime, &filetime, sizeof(datetime)); /* Windows file times are in 100s of nanoseconds. */ - return (datetime.QuadPart - DELTA_EPOCH_IN_MICROSECS) / 10; + return (datetime.QuadPart / 10) - DELTA_EPOCH_IN_MICROSECS; } #ifdef _WIN32_WCE @@ -229,23 +233,21 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi) continue; wsi = wsi_from_fd(context, pfd->fd); - if (wsi->listener) + if (!wsi || wsi->listener) continue; - if (!wsi || wsi->sock_send_blocking) + if (wsi->sock_send_blocking) continue; pfd->revents = LWS_POLLOUT; n = lws_service_fd(context, pfd); if (n < 0) return -1; + + /* Force WSAWaitForMultipleEvents() to check events and then return immediately. */ + timeout_ms = 0; + /* if something closed, retry this slot */ if (n) i--; - - /* - * any wsi has truncated, force him signalled - */ - if (wsi->trunc_len) - WSASetEvent(pt->events[0]); } /* @@ -261,9 +263,11 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi) } if (timeout_ms) { + lws_usec_t t; + lws_pt_lock(pt, __func__); /* don't stay in poll wait longer than next hr timeout */ - lws_usec_t t = __lws_hrtimer_service(pt); + t = __lws_hrtimer_service(pt); if ((lws_usec_t)timeout_ms * 1000 > t) timeout_ms = (int)(t / 1000); @@ -310,8 +314,10 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi) if (pfd->revents & LWS_POLLHUP) --eIdx; - if (pfd->revents) + if (pfd->revents) { + recv(pfd->fd, NULL, 0, 0); lws_service_fd_tsi(context, pfd, tsi); + } } } @@ -635,9 +641,20 @@ _lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename, HANDLE ret; WCHAR buf[MAX_PATH]; lws_fop_fd_t fop_fd; - LARGE_INTEGER llFileSize = {0}; + FILE_STANDARD_INFO fInfo = {0}; + + MultiByteToWideChar(CP_UTF8, 0, filename, -1, buf, LWS_ARRAY_SIZE(buf)); + +#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0602 // Windows 8 (minimum when UWP_ENABLED, but can be used in Windows builds) + CREATEFILE2_EXTENDED_PARAMETERS extParams = {0}; + extParams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL; - MultiByteToWideChar(CP_UTF8, 0, filename, -1, buf, ARRAY_SIZE(buf)); + if (((*flags) & 7) == _O_RDONLY) { + ret = CreateFile2(buf, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, &extParams); + } else { + ret = CreateFile2(buf, GENERIC_WRITE, 0, CREATE_ALWAYS, &extParams); + } +#else if (((*flags) & 7) == _O_RDONLY) { ret = CreateFileW(buf, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); @@ -645,6 +662,7 @@ _lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename, ret = CreateFileW(buf, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); } +#endif if (ret == LWS_INVALID_FILE) goto bail; @@ -657,9 +675,9 @@ _lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename, fop_fd->fd = ret; fop_fd->filesystem_priv = NULL; /* we don't use it */ fop_fd->flags = *flags; - fop_fd->len = GetFileSize(ret, NULL); - if(GetFileSizeEx(ret, &llFileSize)) - fop_fd->len = llFileSize.QuadPart; + fop_fd->len = 0; + if(GetFileInformationByHandleEx(ret, FileStandardInfo, &fInfo, sizeof(fInfo))) + fop_fd->len = fInfo.EndOfFile.QuadPart; fop_fd->pos = 0; @@ -798,7 +816,7 @@ lws_plat_write_file(const char *filename, void *buf, int len) { int m, fd; - fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); + fd = lws_open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); if (fd == -1) return -1; @@ -812,7 +830,7 @@ lws_plat_write_file(const char *filename, void *buf, int len) LWS_VISIBLE int lws_plat_read_file(const char *filename, void *buf, int len) { - int n, fd = open(filename, O_RDONLY); + int n, fd = lws_open(filename, O_RDONLY); if (fd == -1) return -1; diff --git a/thirdparty/libwebsockets/roles/h1/ops-h1.c b/thirdparty/libwebsockets/roles/h1/ops-h1.c index d3b16f4d1f..9001c864ea 100644 --- a/thirdparty/libwebsockets/roles/h1/ops-h1.c +++ b/thirdparty/libwebsockets/roles/h1/ops-h1.c @@ -195,6 +195,7 @@ postbody_completion: } break; + case LRS_RETURNED_CLOSE: case LRS_AWAITING_CLOSE_ACK: case LRS_WAITING_TO_SEND_CLOSE: case LRS_SHUTDOWN: @@ -444,6 +445,16 @@ try_pollout: if (lwsi_state(wsi) != LRS_ISSUING_FILE) { + if (wsi->trunc_len) { + //lwsl_notice("%s: completing partial\n", __func__); + if (lws_issue_raw(wsi, wsi->trunc_alloc + wsi->trunc_offset, + wsi->trunc_len) < 0) { + lwsl_info("%s signalling to close\n", __func__); + goto fail; + } + return LWS_HPI_RET_HANDLED; + } + lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITEABLE_CB, 1); #if defined(LWS_WITH_STATS) @@ -655,6 +666,9 @@ rops_destroy_role_h1(struct lws *wsi) ah = ah->next; } +#ifdef LWS_ROLE_WS + lws_free_set_NULL(wsi->ws); +#endif return 0; } diff --git a/thirdparty/libwebsockets/roles/http/client/client-handshake.c b/thirdparty/libwebsockets/roles/http/client/client-handshake.c index 4830fc9eca..0095c79a69 100644 --- a/thirdparty/libwebsockets/roles/http/client/client-handshake.c +++ b/thirdparty/libwebsockets/roles/http/client/client-handshake.c @@ -162,7 +162,7 @@ create_new_conn: if (!wsi->client_hostname_copy) wsi->client_hostname_copy = - strdup(lws_hdr_simple_ptr(wsi, + lws_strdup(lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS)); /* @@ -654,13 +654,13 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port, lws_ssl_close(wsi); #endif + __remove_wsi_socket_from_fds(wsi); + if (wsi->context->event_loop_ops->close_handle_manually) wsi->context->event_loop_ops->close_handle_manually(wsi); else compatible_close(wsi->desc.sockfd); - __remove_wsi_socket_from_fds(wsi); - #if defined(LWS_WITH_TLS) wsi->tls.use_ssl = ssl; #else @@ -717,7 +717,7 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port, } #ifdef LWS_WITH_HTTP_PROXY -static hubbub_error +hubbub_error html_parser_cb(const hubbub_token *token, void *pw) { struct lws_rewrite *r = (struct lws_rewrite *)pw; @@ -846,7 +846,7 @@ html_parser_cb(const hubbub_token *token, void *pw) #endif -static char * +char * lws_strdup(const char *s) { char *d = lws_malloc(strlen(s) + 1, "strdup"); diff --git a/thirdparty/libwebsockets/roles/http/client/client.c b/thirdparty/libwebsockets/roles/http/client/client.c index ce42dc6cd3..5645fa2b7a 100644 --- a/thirdparty/libwebsockets/roles/http/client/client.c +++ b/thirdparty/libwebsockets/roles/http/client/client.c @@ -93,7 +93,7 @@ lws_client_socket_service(struct lws *wsi, struct lws_pollfd *pollfd, char *sb = p; int n = 0; #if defined(LWS_WITH_SOCKS5) - char conn_mode = 0, pending_timeout = 0; + int conn_mode = 0, pending_timeout = 0; #endif if ((pollfd->revents & LWS_POLLOUT) && @@ -252,6 +252,8 @@ socks_reply_fail: /* clear his proxy connection timeout */ lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); goto start_ws_handshake; + default: + break; } break; #endif @@ -578,6 +580,8 @@ lws_http_transaction_completed_client(struct lws *wsi) "queued client done"); } + _lws_header_table_reset(wsi->http.ah); + /* after the first one, they can only be coming from the queue */ wsi->transaction_from_pipeline_queue = 1; @@ -629,12 +633,20 @@ lws_http_transaction_completed_client(struct lws *wsi) } LWS_VISIBLE LWS_EXTERN unsigned int -lws_http_client_http_response(struct lws *wsi) +lws_http_client_http_response(struct lws *_wsi) { - if (!wsi->http.ah) - return 0; + struct lws *wsi; + unsigned int resp; + + if (_wsi->http.ah && _wsi->http.ah->http_response) + return _wsi->http.ah->http_response; - return wsi->http.ah->http_response; + lws_vhost_lock(_wsi->vhost); + wsi = _lws_client_wsi_master(_wsi); + resp = wsi->http.ah->http_response; + lws_vhost_unlock(_wsi->vhost); + + return resp; } #endif #if defined(LWS_PLAT_OPTEE) @@ -781,7 +793,7 @@ lws_client_interpret_server_handshake(struct lws *wsi) q = strrchr(new_path, '/'); if (q) lws_strncpy(q + 1, p, sizeof(new_path) - - (q - new_path)); + (q - new_path) - 1); else path = p; } @@ -910,9 +922,9 @@ lws_client_interpret_server_handshake(struct lws *wsi) * we seem to be good to go, give client last chance to check * headers and OK it */ - if (wsi->protocol->callback(wsi, + if (w->protocol->callback(w, LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH, - wsi->user_space, NULL, 0)) { + w->user_space, NULL, 0)) { cce = "HS: disallowed by client filter"; goto bail2; @@ -924,9 +936,9 @@ lws_client_interpret_server_handshake(struct lws *wsi) wsi->rxflow_change_to = LWS_RXFLOW_ALLOW; /* call him back to inform him he is up */ - if (wsi->protocol->callback(wsi, + if (w->protocol->callback(w, LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP, - wsi->user_space, NULL, 0)) { + w->user_space, NULL, 0)) { cce = "HS: disallowed at ESTABLISHED"; goto bail3; } @@ -964,9 +976,9 @@ bail2: n = 0; if (cce) n = (int)strlen(cce); - wsi->protocol->callback(wsi, + w->protocol->callback(w, LWS_CALLBACK_CLIENT_CONNECTION_ERROR, - wsi->user_space, (void *)cce, + w->user_space, (void *)cce, (unsigned int)n); } wsi->already_did_cce = 1; @@ -1228,4 +1240,4 @@ completed: return 0; } -#endif
\ No newline at end of file +#endif diff --git a/thirdparty/libwebsockets/roles/http/header.c b/thirdparty/libwebsockets/roles/http/header.c index 99e56f7564..dbcf27cbd1 100644 --- a/thirdparty/libwebsockets/roles/http/header.c +++ b/thirdparty/libwebsockets/roles/http/header.c @@ -26,7 +26,7 @@ const unsigned char * lws_token_to_string(enum lws_token_indexes token) { - if ((unsigned int)token >= ARRAY_SIZE(set)) + if ((unsigned int)token >= LWS_ARRAY_SIZE(set)) return NULL; return (unsigned char *)set[token]; @@ -149,9 +149,17 @@ lws_add_http_common_headers(struct lws *wsi, unsigned int code, (int)strlen(content_type), p, end)) return 1; - if (content_len != LWS_ILLEGAL_HTTP_CONTENT_LEN && - lws_add_http_header_content_length(wsi, content_len, p, end)) - return 1; + if (content_len != LWS_ILLEGAL_HTTP_CONTENT_LEN) { + if (lws_add_http_header_content_length(wsi, content_len, p, end)) + return 1; + } else { + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_CONNECTION, + (unsigned char *)"close", 5, + p, end)) + return 1; + + wsi->http.connection_type = HTTP_CONNECTION_CLOSE; + } return 0; } @@ -204,34 +212,40 @@ lws_add_http_header_status(struct lws *wsi, unsigned int _code, #endif #ifdef LWS_WITH_HTTP2 - if (lwsi_role_h2(wsi) || lwsi_role_h2_ENCAPSULATION(wsi)) - return lws_add_http2_header_status(wsi, code, p, end); + if (lwsi_role_h2(wsi) || lwsi_role_h2_ENCAPSULATION(wsi)) { + n = lws_add_http2_header_status(wsi, code, p, end); + if (n) + return n; + } else #endif - if (code >= 400 && code < (400 + ARRAY_SIZE(err400))) - description = err400[code - 400]; - if (code >= 500 && code < (500 + ARRAY_SIZE(err500))) - description = err500[code - 500]; - - if (code == 100) - description = "Continue"; - if (code == 200) - description = "OK"; - if (code == 304) - description = "Not Modified"; - else - if (code >= 300 && code < 400) - description = "Redirect"; - - if (wsi->http.request_version < ARRAY_SIZE(hver)) - p1 = hver[wsi->http.request_version]; - else - p1 = hver[0]; - - n = sprintf((char *)code_and_desc, "%s %u %s", p1, code, description); - - if (lws_add_http_header_by_name(wsi, NULL, code_and_desc, n, p, end)) - return 1; - + { + if (code >= 400 && code < (400 + LWS_ARRAY_SIZE(err400))) + description = err400[code - 400]; + if (code >= 500 && code < (500 + LWS_ARRAY_SIZE(err500))) + description = err500[code - 500]; + + if (code == 100) + description = "Continue"; + if (code == 200) + description = "OK"; + if (code == 304) + description = "Not Modified"; + else + if (code >= 300 && code < 400) + description = "Redirect"; + + if (wsi->http.request_version < LWS_ARRAY_SIZE(hver)) + p1 = hver[wsi->http.request_version]; + else + p1 = hver[0]; + + n = sprintf((char *)code_and_desc, "%s %u %s", p1, code, + description); + + if (lws_add_http_header_by_name(wsi, NULL, code_and_desc, n, p, + end)) + return 1; + } headers = wsi->vhost->headers; while (headers) { if (lws_add_http_header_by_name(wsi, diff --git a/thirdparty/libwebsockets/roles/http/private.h b/thirdparty/libwebsockets/roles/http/private.h index 2aa7a92f75..5699914742 100644 --- a/thirdparty/libwebsockets/roles/http/private.h +++ b/thirdparty/libwebsockets/roles/http/private.h @@ -227,6 +227,7 @@ struct _lws_http_mode_related { #if defined(LWS_WITH_HTTP_PROXY) unsigned int perform_rewrite:1; #endif + unsigned int deferred_transaction_completed:1; }; diff --git a/thirdparty/libwebsockets/roles/http/server/lejp-conf.c b/thirdparty/libwebsockets/roles/http/server/lejp-conf.c index e9ce854cfc..fbf10c288e 100644 --- a/thirdparty/libwebsockets/roles/http/server/lejp-conf.c +++ b/thirdparty/libwebsockets/roles/http/server/lejp-conf.c @@ -205,6 +205,7 @@ struct jpargs { unsigned int enable_client_ssl:1; unsigned int fresh_mount:1; unsigned int any_vhosts:1; + unsigned int chunk:1; }; static void * @@ -213,6 +214,8 @@ lwsws_align(struct jpargs *a) if ((lws_intptr_t)(a->p) & 15) a->p += 16 - ((lws_intptr_t)(a->p) & 15); + a->chunk = 0; + return a->p; } @@ -225,7 +228,7 @@ arg_to_bool(const char *s) if (n) return 1; - for (n = 0; n < (int)ARRAY_SIZE(on); n++) + for (n = 0; n < (int)LWS_ARRAY_SIZE(on); n++) if (!strcasecmp(s, on[n])) return 1; @@ -413,25 +416,30 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason) } /* this catches, eg, vhosts[].headers[].xxx */ - if (reason == LEJPCB_VAL_STR_END && + if ((reason == LEJPCB_VAL_STR_END || reason == LEJPCB_VAL_STR_CHUNK) && ctx->path_match == LEJPVP_HEADERS_NAME + 1) { - headers = lwsws_align(a); - a->p += sizeof(*headers); - - n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p); - /* ie, enable this protocol, no options yet */ - headers->next = a->info->headers; - a->info->headers = headers; - headers->name = a->p; - // lwsl_notice(" adding header %s=%s\n", a->p, ctx->buf); - a->p += n - 1; - *(a->p++) = ':'; - if (a->p < a->end) - *(a->p++) = '\0'; - else - *(a->p - 1) = '\0'; - headers->value = a->p; - headers->options = NULL; + if (!a->chunk) { + headers = lwsws_align(a); + a->p += sizeof(*headers); + + n = lejp_get_wildcard(ctx, 0, a->p, + lws_ptr_diff(a->end, a->p)); + /* ie, add this header */ + headers->next = a->info->headers; + a->info->headers = headers; + headers->name = a->p; + + lwsl_notice(" adding header %s=%s\n", a->p, ctx->buf); + a->p += n - 1; + *(a->p++) = ':'; + if (a->p < a->end) + *(a->p++) = '\0'; + else + *(a->p - 1) = '\0'; + headers->value = a->p; + headers->options = NULL; + } + a->chunk = reason == LEJPCB_VAL_STR_CHUNK; goto dostring; } @@ -502,7 +510,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason) if (a->last) a->last->mount_next = m; - for (n = 0; n < (int)ARRAY_SIZE(mount_protocols); n++) + for (n = 0; n < (int)LWS_ARRAY_SIZE(mount_protocols); n++) if (!strncmp(a->m.origin, mount_protocols[n], strlen(mount_protocols[n]))) { lwsl_info("----%s\n", a->m.origin); @@ -512,7 +520,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason) break; } - if (n == (int)ARRAY_SIZE(mount_protocols)) { + if (n == (int)LWS_ARRAY_SIZE(mount_protocols)) { lwsl_err("unsupported protocol:// %s\n", a->m.origin); return 1; } @@ -750,6 +758,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason) dostring: p = ctx->buf; + p[LEJP_STRING_CHUNK] = '\0'; p1 = strstr(p, ESC_INSTALL_DATADIR); if (p1) { n = p1 - p; @@ -762,7 +771,8 @@ dostring: } a->p += lws_snprintf(a->p, a->end - a->p, "%s", p); - *(a->p)++ = '\0'; + if (reason == LEJPCB_VAL_STR_END) + *(a->p)++ = '\0'; return 0; } @@ -779,7 +789,7 @@ lwsws_get_config(void *user, const char *f, const char * const *paths, struct lejp_ctx ctx; int n, m, fd; - fd = open(f, O_RDONLY); + fd = lws_open(f, O_RDONLY); if (fd < 0) { lwsl_err("Cannot open %s\n", f); return 2; @@ -927,11 +937,11 @@ lwsws_get_config_globals(struct lws_context_creation_info *info, const char *d, lws_snprintf(dd, sizeof(dd) - 1, "%s/conf", d); if (lwsws_get_config(&a, dd, paths_global, - ARRAY_SIZE(paths_global), lejp_globals_cb) > 1) + LWS_ARRAY_SIZE(paths_global), lejp_globals_cb) > 1) return 1; lws_snprintf(dd, sizeof(dd) - 1, "%s/conf.d", d); if (lwsws_get_config_d(&a, dd, paths_global, - ARRAY_SIZE(paths_global), lejp_globals_cb) > 1) + LWS_ARRAY_SIZE(paths_global), lejp_globals_cb) > 1) return 1; a.plugin_dirs[a.count_plugin_dirs] = NULL; @@ -962,11 +972,11 @@ lwsws_get_config_vhosts(struct lws_context *context, lws_snprintf(dd, sizeof(dd) - 1, "%s/conf", d); if (lwsws_get_config(&a, dd, paths_vhosts, - ARRAY_SIZE(paths_vhosts), lejp_vhosts_cb) > 1) + LWS_ARRAY_SIZE(paths_vhosts), lejp_vhosts_cb) > 1) return 1; lws_snprintf(dd, sizeof(dd) - 1, "%s/conf.d", d); if (lwsws_get_config_d(&a, dd, paths_vhosts, - ARRAY_SIZE(paths_vhosts), lejp_vhosts_cb) > 1) + LWS_ARRAY_SIZE(paths_vhosts), lejp_vhosts_cb) > 1) return 1; *cs = a.p; diff --git a/thirdparty/libwebsockets/roles/http/server/parsers.c b/thirdparty/libwebsockets/roles/http/server/parsers.c index cb022e362b..482bdc676a 100644 --- a/thirdparty/libwebsockets/roles/http/server/parsers.c +++ b/thirdparty/libwebsockets/roles/http/server/parsers.c @@ -563,7 +563,7 @@ int LWS_WARN_UNUSED_RESULT lws_hdr_simple_create(struct lws *wsi, enum lws_token_indexes h, const char *s) { wsi->http.ah->nfrag++; - if (wsi->http.ah->nfrag == ARRAY_SIZE(wsi->http.ah->frags)) { + if (wsi->http.ah->nfrag == LWS_ARRAY_SIZE(wsi->http.ah->frags)) { lwsl_warn("More hdr frags than we can deal with, dropping\n"); return -1; } @@ -677,18 +677,16 @@ lws_parse_urldecode(struct lws *wsi, uint8_t *_c) return -1; /* genuine delimiter */ if ((c == '&' || c == ';') && !enc) { - if (issue_char(wsi, c) < 0) + if (issue_char(wsi, '\0') < 0) return -1; - /* swallow the terminator */ - ah->frags[ah->nfrag].len--; /* link to next fragment */ ah->frags[ah->nfrag].nfrag = ah->nfrag + 1; ah->nfrag++; - if (ah->nfrag >= ARRAY_SIZE(ah->frags)) + if (ah->nfrag >= LWS_ARRAY_SIZE(ah->frags)) goto excessive; /* start next fragment after the & */ ah->post_literal_equal = 0; - ah->frags[ah->nfrag].offset = ah->pos; + ah->frags[ah->nfrag].offset = ++ah->pos; ah->frags[ah->nfrag].len = 0; ah->frags[ah->nfrag].nfrag = 0; goto swallow; @@ -787,9 +785,9 @@ lws_parse_urldecode(struct lws *wsi, uint8_t *_c) /* move to using WSI_TOKEN_HTTP_URI_ARGS */ ah->nfrag++; - if (ah->nfrag >= ARRAY_SIZE(ah->frags)) + if (ah->nfrag >= LWS_ARRAY_SIZE(ah->frags)) goto excessive; - ah->frags[ah->nfrag].offset = ah->pos; + ah->frags[ah->nfrag].offset = ++ah->pos; ah->frags[ah->nfrag].len = 0; ah->frags[ah->nfrag].nfrag = 0; @@ -852,10 +850,10 @@ lws_parse(struct lws *wsi, unsigned char *buf, int *len) c == ' ') break; - for (m = 0; m < ARRAY_SIZE(methods); m++) + for (m = 0; m < LWS_ARRAY_SIZE(methods); m++) if (ah->parser_state == methods[m]) break; - if (m == ARRAY_SIZE(methods)) + if (m == LWS_ARRAY_SIZE(methods)) /* it was not any of the methods */ goto check_eol; @@ -983,7 +981,7 @@ nope: if (ah->lextable_pos < 0 && lwsi_role_h1(wsi) && lwsi_role_server(wsi)) { /* this is not a header we know about */ - for (m = 0; m < ARRAY_SIZE(methods); m++) + for (m = 0; m < LWS_ARRAY_SIZE(methods); m++) if (ah->frag_index[methods[m]]) { /* * already had the method, no idea what @@ -996,7 +994,7 @@ nope: * hm it's an unknown http method from a client in fact, * it cannot be valid http */ - if (m == ARRAY_SIZE(methods)) { + if (m == LWS_ARRAY_SIZE(methods)) { /* * are we set up to accept raw in these cases? */ @@ -1025,7 +1023,7 @@ nope: lextable[ah->lextable_pos + 1]; lwsl_parser("known hdr %d\n", n); - for (m = 0; m < ARRAY_SIZE(methods); m++) + for (m = 0; m < LWS_ARRAY_SIZE(methods); m++) if (n == methods[m] && ah->frag_index[methods[m]]) { lwsl_warn("Duplicated method\n"); @@ -1061,7 +1059,7 @@ nope: start_fragment: ah->nfrag++; excessive: - if (ah->nfrag == ARRAY_SIZE(ah->frags)) { + if (ah->nfrag == LWS_ARRAY_SIZE(ah->frags)) { lwsl_warn("More hdr frags than we can deal with\n"); return -1; } diff --git a/thirdparty/libwebsockets/roles/http/server/server.c b/thirdparty/libwebsockets/roles/http/server/server.c index 350af3cd7e..abd86dc9b5 100644 --- a/thirdparty/libwebsockets/roles/http/server/server.c +++ b/thirdparty/libwebsockets/roles/http/server/server.c @@ -131,6 +131,17 @@ done_list: (void)n; #if defined(__linux__) +#ifdef LWS_WITH_UNIX_SOCK + /* + * A Unix domain sockets cannot be bound for several times, even if we set + * the SO_REUSE* options on. + * However, fortunately, each thread is able to independently listen when + * running on a reasonably new Linux kernel. So we can safely assume + * creating just one listening socket for a multi-threaded environment won't + * fail in most cases. + */ + if (!LWS_UNIX_SOCK_ENABLED(vhost)) +#endif limit = vhost->context->count_threads; #endif @@ -694,7 +705,7 @@ lws_find_string_in_file(const char *filename, const char *string, int stringlen) char buf[128]; int fd, match = 0, pos = 0, n = 0, hit = 0; - fd = open(filename, O_RDONLY); + fd = lws_open(filename, O_RDONLY); if (fd < 0) { lwsl_err("can't open auth file: %s\n", filename); return 0; @@ -812,7 +823,7 @@ lws_http_get_uri_and_method(struct lws *wsi, char **puri_ptr, int *puri_len) { int n, count = 0; - for (n = 0; n < (int)ARRAY_SIZE(methods); n++) + for (n = 0; n < (int)LWS_ARRAY_SIZE(methods); n++) if (lws_hdr_total_length(wsi, methods[n])) count++; if (!count) { @@ -827,7 +838,7 @@ lws_http_get_uri_and_method(struct lws *wsi, char **puri_ptr, int *puri_len) return -1; } - for (n = 0; n < (int)ARRAY_SIZE(methods); n++) + for (n = 0; n < (int)LWS_ARRAY_SIZE(methods); n++) if (lws_hdr_total_length(wsi, methods[n])) { *puri_ptr = lws_hdr_simple_ptr(wsi, methods[n]); *puri_len = lws_hdr_total_length(wsi, methods[n]); @@ -857,7 +868,7 @@ lws_http_action(struct lws *wsi) }; meth = lws_http_get_uri_and_method(wsi, &uri_ptr, &uri_len); - if (meth < 0 || meth >= (int)ARRAY_SIZE(method_names)) + if (meth < 0 || meth >= (int)LWS_ARRAY_SIZE(method_names)) goto bail_nuke_ah; /* we insist on absolute paths */ @@ -1128,7 +1139,7 @@ lws_http_action(struct lws *wsi) } if (pcolon > pslash) pcolon = NULL; - + if (pcolon) n = pcolon - hit->origin; else @@ -1142,13 +1153,13 @@ lws_http_action(struct lws *wsi) i.address = ads; i.port = 80; - if (hit->origin_protocol == LWSMPRO_HTTPS) { + if (hit->origin_protocol == LWSMPRO_HTTPS) { i.port = 443; i.ssl_connection = 1; } if (pcolon) i.port = atoi(pcolon + 1); - + lws_snprintf(rpath, sizeof(rpath) - 1, "/%s/%s", pslash + 1, uri_ptr + hit->mountpoint_len); lws_clean_url(rpath); @@ -1164,7 +1175,7 @@ lws_http_action(struct lws *wsi) p++; } } - + i.path = rpath; i.host = i.address; @@ -1178,7 +1189,7 @@ lws_http_action(struct lws *wsi) "from %s, to %s\n", i.address, i.port, i.path, i.ssl_connection, i.uri_replace_from, i.uri_replace_to); - + if (!lws_client_connect_via_info(&i)) { lwsl_err("proxy connect fail\n"); return 1; @@ -1714,12 +1725,31 @@ lws_http_transaction_completed(struct lws *wsi) { int n = NO_PENDING_TIMEOUT; + if (wsi->trunc_len) { + /* + * ...so he tried to send something large as the http reply, + * it went as a partial, but he immediately said the + * transaction was completed. + * + * Defer the transaction completed until the last part of the + * partial is sent. + */ + lwsl_notice("%s: deferring due to partial\n", __func__); + wsi->http.deferred_transaction_completed = 1; + + return 0; + } + lwsl_info("%s: wsi %p\n", __func__, wsi); lws_access_log(wsi); if (!wsi->hdr_parsing_completed) { - lwsl_notice("%s: ignoring, ah parsing incomplete\n", __func__); + char peer[64]; + lws_get_peer_simple(wsi, peer, sizeof(peer) - 1); + peer[sizeof(peer) - 1] = '\0'; + lwsl_notice("%s: (from %s) ignoring, ah parsing incomplete\n", + __func__, peer); return 0; } diff --git a/thirdparty/libwebsockets/roles/ws/client-parser-ws.c b/thirdparty/libwebsockets/roles/ws/client-parser-ws.c index aa561ce034..7287fb1590 100644 --- a/thirdparty/libwebsockets/roles/ws/client-parser-ws.c +++ b/thirdparty/libwebsockets/roles/ws/client-parser-ws.c @@ -450,7 +450,7 @@ ping_drop: break; case LWSWSOPC_PONG: - lwsl_info("client receied pong\n"); + lwsl_info("client received pong\n"); lwsl_hexdump(&wsi->ws->rx_ubuf[LWS_PRE], wsi->ws->rx_ubuf_head); @@ -488,9 +488,6 @@ ping_drop: ebuf.token = &wsi->ws->rx_ubuf[LWS_PRE]; ebuf.len = wsi->ws->rx_ubuf_head; - if (wsi->ws->opcode == LWSWSOPC_PONG && !ebuf.len) - goto already_done; - #if !defined(LWS_WITHOUT_EXTENSIONS) drain_extension: lwsl_ext("%s: passing %d to ext\n", __func__, ebuf.len); @@ -504,14 +501,12 @@ drain_extension: #endif lwsl_debug("post inflate ebuf len %d\n", ebuf.len); - if ( #if !defined(LWS_WITHOUT_EXTENSIONS) - rx_draining_ext && -#endif - !ebuf.len) { + if (rx_draining_ext && !ebuf.len) { lwsl_debug(" --- ending drain on 0 read result\n"); goto already_done; } +#endif if (wsi->ws->check_utf8 && !wsi->ws->defeat_check_utf8) { if (lws_check_utf8(&wsi->ws->utf8, diff --git a/thirdparty/libwebsockets/roles/ws/ops-ws.c b/thirdparty/libwebsockets/roles/ws/ops-ws.c index 5ddaba9e18..665b2c9b74 100644 --- a/thirdparty/libwebsockets/roles/ws/ops-ws.c +++ b/thirdparty/libwebsockets/roles/ws/ops-ws.c @@ -1246,8 +1246,7 @@ int rops_handle_POLLOUT_ws(struct lws *wsi) return LWS_HP_RET_BAIL_OK; } - if (lwsi_role_client(wsi) && !wsi->socket_is_permanently_unusable && - wsi->ws->send_check_ping) { + if (!wsi->socket_is_permanently_unusable && wsi->ws->send_check_ping) { lwsl_info("issuing ping on wsi %p\n", wsi); wsi->ws->send_check_ping = 0; @@ -1282,7 +1281,7 @@ int rops_handle_POLLOUT_ws(struct lws *wsi) * payload ordering, but since they are always complete * fragments control packets can interleave OK. */ - if (lwsi_role_client(wsi) && wsi->ws->tx_draining_ext) { + if (wsi->ws->tx_draining_ext) { lwsl_ext("SERVICING TX EXT DRAINING\n"); if (lws_write(wsi, NULL, 0, LWS_WRITE_CONTINUATION) < 0) return LWS_HP_RET_BAIL_DIE; @@ -1292,8 +1291,10 @@ int rops_handle_POLLOUT_ws(struct lws *wsi) /* Priority 6: extensions */ - if (!wsi->ws->extension_data_pending) + if (!wsi->ws->extension_data_pending && !wsi->ws->tx_draining_ext) { + lwsl_ext("%s: !wsi->ws->extension_data_pending\n", __func__); return LWS_HP_RET_USER_SERVICE; + } /* * check in on the active extensions, see if they @@ -1412,15 +1413,13 @@ rops_periodic_checks_ws(struct lws_context *context, int tsi, time_t now) wsi->ws->time_next_ping_check) > context->ws_ping_pong_interval) { - lwsl_info("req pp on wsi %p\n", - wsi); + lwsl_info("req pp on wsi %p\n", wsi); wsi->ws->send_check_ping = 1; lws_set_timeout(wsi, PENDING_TIMEOUT_WS_PONG_CHECK_SEND_PING, context->timeout_secs); lws_callback_on_writable(wsi); - wsi->ws->time_next_ping_check = - now; + wsi->ws->time_next_ping_check = now; } wsi = wsi->same_vh_protocol_next; } @@ -1466,6 +1465,9 @@ rops_service_flag_pending_ws(struct lws_context *context, int tsi) static int rops_close_via_role_protocol_ws(struct lws *wsi, enum lws_close_status reason) { + if (!wsi->ws) + return 0; + if (!wsi->ws->close_in_ping_buffer_len && /* already a reason */ (reason == LWS_CLOSE_STATUS_NOSTATUS || reason == LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY)) @@ -1512,7 +1514,7 @@ rops_close_role_ws(struct lws_context_per_thread *pt, struct lws *wsi) if (wsi->ws->tx_draining_ext) { struct lws **w = &pt->ws.tx_draining_ext_list; - lwsl_notice("%s: CLEARING tx_draining_ext\n", __func__); + lwsl_ext("%s: CLEARING tx_draining_ext\n", __func__); wsi->ws->tx_draining_ext = 0; /* remove us from context draining ext list */ while (*w) { @@ -1563,7 +1565,7 @@ rops_write_role_protocol_ws(struct lws *wsi, unsigned char *buf, size_t len, /* remove us from the list */ struct lws **w = &pt->ws.tx_draining_ext_list; - lwsl_notice("%s: CLEARING tx_draining_ext\n", __func__); + lwsl_ext("%s: CLEARING tx_draining_ext\n", __func__); wsi->ws->tx_draining_ext = 0; /* remove us from context draining ext list */ while (*w) { @@ -1588,7 +1590,7 @@ rops_write_role_protocol_ws(struct lws *wsi, unsigned char *buf, size_t len, if (!(wpt & LWS_WRITE_NO_FIN) && len) *wp &= ~LWS_WRITE_NO_FIN; - lwsl_notice("FORCED draining wp to 0x%02X (stashed 0x%02X, incoming 0x%02X)\n", *wp, + lwsl_ext("FORCED draining wp to 0x%02X (stashed 0x%02X, incoming 0x%02X)\n", *wp, wsi->ws->tx_draining_stashed_wp, wpt); // assert(0); } @@ -1644,7 +1646,7 @@ rops_write_role_protocol_ws(struct lws *wsi, unsigned char *buf, size_t len, // lwsl_notice("ext processed %d plaintext into %d compressed (wp 0x%x)\n", m, (int)ebuf.len, *wp); if (n && ebuf.len) { - lwsl_notice("write drain len %d (wp 0x%x) SETTING tx_draining_ext\n", (int)ebuf.len, *wp); + lwsl_ext("write drain len %d (wp 0x%x) SETTING tx_draining_ext\n", (int)ebuf.len, *wp); /* extension requires further draining */ wsi->ws->tx_draining_ext = 1; wsi->ws->tx_draining_ext_list = pt->ws.tx_draining_ext_list; diff --git a/thirdparty/libwebsockets/tls/mbedtls/mbedtls-server.c b/thirdparty/libwebsockets/tls/mbedtls/mbedtls-server.c index 2de6d422e3..f17c7e5494 100644 --- a/thirdparty/libwebsockets/tls/mbedtls/mbedtls-server.c +++ b/thirdparty/libwebsockets/tls/mbedtls/mbedtls-server.c @@ -631,7 +631,7 @@ lws_tls_acme_sni_csr_create(struct lws_context *context, const char *elements[], /* subject must be formatted like "C=TW,O=warmcat,CN=myserver" */ - for (n = 0; n < (int)ARRAY_SIZE(x5); n++) { + for (n = 0; n < (int)LWS_ARRAY_SIZE(x5); n++) { if (p != subject) *p++ = ','; if (elements[n]) diff --git a/thirdparty/libwebsockets/tls/mbedtls/ssl.c b/thirdparty/libwebsockets/tls/mbedtls/ssl.c index 6ae9d2556b..f311ef50e3 100644 --- a/thirdparty/libwebsockets/tls/mbedtls/ssl.c +++ b/thirdparty/libwebsockets/tls/mbedtls/ssl.c @@ -121,8 +121,6 @@ lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len) if (wsi->vhost) wsi->vhost->conn_stats.rx += n; - lws_restart_ws_ping_pong_timer(wsi); - /* * if it was our buffer that limited what we read, * check if SSL has additional data pending inside SSL buffers. diff --git a/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_types.h b/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_types.h index ba19663d9e..68ac748a28 100644 --- a/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_types.h +++ b/thirdparty/libwebsockets/tls/mbedtls/wrapper/include/internal/ssl_types.h @@ -37,7 +37,11 @@ typedef void RSA; typedef void STACK; typedef void BIO; +#if defined(WIN32) || defined(_WIN32) +#define ossl_inline __inline +#else #define ossl_inline inline +#endif #define SSL_METHOD_CALL(f, s, ...) s->method->func->ssl_##f(s, ##__VA_ARGS__) #define X509_METHOD_CALL(f, x, ...) x->method->x509_##f(x, ##__VA_ARGS__) diff --git a/thirdparty/libwebsockets/uwp_fixes.diff b/thirdparty/libwebsockets/uwp_fixes.diff new file mode 100644 index 0000000000..21c3275bba --- /dev/null +++ b/thirdparty/libwebsockets/uwp_fixes.diff @@ -0,0 +1,47 @@ +diff --git a/thirdparty/libwebsockets/plat/lws-plat-win.c b/thirdparty/libwebsockets/plat/lws-plat-win.c +index bd513b494..1850b6425 100644 +--- a/thirdparty/libwebsockets/plat/lws-plat-win.c ++++ b/thirdparty/libwebsockets/plat/lws-plat-win.c +@@ -641,9 +641,20 @@ _lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename, + HANDLE ret; + WCHAR buf[MAX_PATH]; + lws_fop_fd_t fop_fd; +- LARGE_INTEGER llFileSize = {0}; ++ FILE_STANDARD_INFO fInfo = {0}; + + MultiByteToWideChar(CP_UTF8, 0, filename, -1, buf, LWS_ARRAY_SIZE(buf)); ++ ++#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0602 // Windows 8 (minimum when UWP_ENABLED, but can be used in Windows builds) ++ CREATEFILE2_EXTENDED_PARAMETERS extParams = {0}; ++ extParams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL; ++ ++ if (((*flags) & 7) == _O_RDONLY) { ++ ret = CreateFile2(buf, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, &extParams); ++ } else { ++ ret = CreateFile2(buf, GENERIC_WRITE, 0, CREATE_ALWAYS, &extParams); ++ } ++#else + if (((*flags) & 7) == _O_RDONLY) { + ret = CreateFileW(buf, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); +@@ -651,6 +662,7 @@ _lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename, + ret = CreateFileW(buf, GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + } ++#endif + + if (ret == LWS_INVALID_FILE) + goto bail; +@@ -663,9 +675,9 @@ _lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename, + fop_fd->fd = ret; + fop_fd->filesystem_priv = NULL; /* we don't use it */ + fop_fd->flags = *flags; +- fop_fd->len = GetFileSize(ret, NULL); +- if(GetFileSizeEx(ret, &llFileSize)) +- fop_fd->len = llFileSize.QuadPart; ++ fop_fd->len = 0; ++ if(GetFileInformationByHandleEx(ret, FileStandardInfo, &fInfo, sizeof(fInfo))) ++ fop_fd->len = fInfo.EndOfFile.QuadPart; + + fop_fd->pos = 0; + diff --git a/thirdparty/libwebsockets/win32helpers/getopt.c b/thirdparty/libwebsockets/win32helpers/getopt.c index 2181f1cb12..3bb21f6f28 100644 --- a/thirdparty/libwebsockets/win32helpers/getopt.c +++ b/thirdparty/libwebsockets/win32helpers/getopt.c @@ -1,153 +1,153 @@ -/* $NetBSD: getopt.c,v 1.16 1999/12/02 13:15:56 kleink Exp $ */
-
-/*
- * Copyright (c) 1987, 1993, 1994
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#if 0
-static char sccsid[] = "@(#)getopt.c 8.3 (Berkeley) 4/27/95";
-#endif
-
-#include <assert.h>
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-
-#define __P(x) x
-#define _DIAGASSERT(x) assert(x)
-
-#ifdef __weak_alias
-__weak_alias(getopt,_getopt);
-#endif
-
-
-int opterr = 1, /* if error message should be printed */
- optind = 1, /* index into parent argv vector */
- optopt, /* character checked for validity */
- optreset; /* reset getopt */
-char *optarg; /* argument associated with option */
-
-static char * _progname __P((char *));
-int getopt_internal __P((int, char * const *, const char *));
-
-static char *
-_progname(nargv0)
- char * nargv0;
-{
- char * tmp;
-
- _DIAGASSERT(nargv0 != NULL);
-
- tmp = strrchr(nargv0, '/');
- if (tmp)
- tmp++;
- else
- tmp = nargv0;
- return(tmp);
-}
-
-#define BADCH (int)'?'
-#define BADARG (int)':'
-#define EMSG ""
-
-/*
- * getopt --
- * Parse argc/argv argument vector.
- */
-int
-getopt(nargc, nargv, ostr)
- int nargc;
- char * const nargv[];
- const char *ostr;
-{
- static char *__progname = 0;
- static char *place = EMSG; /* option letter processing */
- char *oli; /* option letter list index */
- __progname = __progname?__progname:_progname(*nargv);
-
- _DIAGASSERT(nargv != NULL);
- _DIAGASSERT(ostr != NULL);
-
- if (optreset || !*place) { /* update scanning pointer */
- optreset = 0;
- if (optind >= nargc || *(place = nargv[optind]) != '-') {
- place = EMSG;
- return (-1);
- }
- if (place[1] && *++place == '-' /* found "--" */
- && place[1] == '\0') {
- ++optind;
- place = EMSG;
- return (-1);
- }
- } /* option letter okay? */
- if ((optopt = (int)*place++) == (int)':' ||
- !(oli = strchr(ostr, optopt))) {
- /*
- * if the user didn't specify '-' as an option,
- * assume it means -1.
- */
- if (optopt == (int)'-')
- return (-1);
- if (!*place)
- ++optind;
- if (opterr && *ostr != ':')
- (void)fprintf(stderr,
- "%s: illegal option -- %c\n", __progname, optopt);
- return (BADCH);
- }
- if (*++oli != ':') { /* don't need argument */
- optarg = NULL;
- if (!*place)
- ++optind;
- }
- else { /* need an argument */
- if (*place) /* no white space */
- optarg = place;
- else if (nargc <= ++optind) { /* no arg */
- place = EMSG;
- if (*ostr == ':')
- return (BADARG);
- if (opterr)
- (void)fprintf(stderr,
- "%s: option requires an argument -- %c\n",
- __progname, optopt);
- return (BADCH);
- }
- else /* white space */
- optarg = nargv[optind];
- place = EMSG;
- ++optind;
- }
- return (optopt); /* dump back option letter */
-}
-
+/* $NetBSD: getopt.c,v 1.16 1999/12/02 13:15:56 kleink Exp $ */ + +/* + * Copyright (c) 1987, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +static char sccsid[] = "@(#)getopt.c 8.3 (Berkeley) 4/27/95"; +#endif + +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +#define __P(x) x +#define _DIAGASSERT(x) assert(x) + +#ifdef __weak_alias +__weak_alias(getopt,_getopt); +#endif + + +int opterr = 1, /* if error message should be printed */ + optind = 1, /* index into parent argv vector */ + optopt, /* character checked for validity */ + optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ + +static char * _progname __P((char *)); +int getopt_internal __P((int, char * const *, const char *)); + +static char * +_progname(nargv0) + char * nargv0; +{ + char * tmp; + + _DIAGASSERT(nargv0 != NULL); + + tmp = strrchr(nargv0, '/'); + if (tmp) + tmp++; + else + tmp = nargv0; + return(tmp); +} + +#define BADCH (int)'?' +#define BADARG (int)':' +#define EMSG "" + +/* + * getopt -- + * Parse argc/argv argument vector. + */ +int +getopt(nargc, nargv, ostr) + int nargc; + char * const nargv[]; + const char *ostr; +{ + static char *__progname = 0; + static char *place = EMSG; /* option letter processing */ + char *oli; /* option letter list index */ + __progname = __progname?__progname:_progname(*nargv); + + _DIAGASSERT(nargv != NULL); + _DIAGASSERT(ostr != NULL); + + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc || *(place = nargv[optind]) != '-') { + place = EMSG; + return (-1); + } + if (place[1] && *++place == '-' /* found "--" */ + && place[1] == '\0') { + ++optind; + place = EMSG; + return (-1); + } + } /* option letter okay? */ + if ((optopt = (int)*place++) == (int)':' || + !(oli = strchr(ostr, optopt))) { + /* + * if the user didn't specify '-' as an option, + * assume it means -1. + */ + if (optopt == (int)'-') + return (-1); + if (!*place) + ++optind; + if (opterr && *ostr != ':') + (void)fprintf(stderr, + "%s: illegal option -- %c\n", __progname, optopt); + return (BADCH); + } + if (*++oli != ':') { /* don't need argument */ + optarg = NULL; + if (!*place) + ++optind; + } + else { /* need an argument */ + if (*place) /* no white space */ + optarg = place; + else if (nargc <= ++optind) { /* no arg */ + place = EMSG; + if (*ostr == ':') + return (BADARG); + if (opterr) + (void)fprintf(stderr, + "%s: option requires an argument -- %c\n", + __progname, optopt); + return (BADCH); + } + else /* white space */ + optarg = nargv[optind]; + place = EMSG; + ++optind; + } + return (optopt); /* dump back option letter */ +} + diff --git a/thirdparty/libwebsockets/win32helpers/getopt_long.c b/thirdparty/libwebsockets/win32helpers/getopt_long.c index 22e5fa8945..6dfccf367d 100644 --- a/thirdparty/libwebsockets/win32helpers/getopt_long.c +++ b/thirdparty/libwebsockets/win32helpers/getopt_long.c @@ -1,240 +1,240 @@ -
-/*
- * Copyright (c) 1987, 1993, 1994, 1996
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-#include <assert.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "getopt.h"
-
-#define lws_ptr_diff(head, tail) \
- ((int)((char *)(head) - (char *)(tail)))
-
-extern int opterr; /* if error message should be printed */
-extern int optind; /* index into parent argv vector */
-extern int optopt; /* character checked for validity */
-extern int optreset; /* reset getopt */
-extern char *optarg; /* argument associated with option */
-
-#define __P(x) x
-#define _DIAGASSERT(x) assert(x)
-
-static char * __progname __P((char *));
-int getopt_internal __P((int, char * const *, const char *));
-
-static char *
-__progname(nargv0)
- char * nargv0;
-{
- char * tmp;
-
- _DIAGASSERT(nargv0 != NULL);
-
- tmp = strrchr(nargv0, '/');
- if (tmp)
- tmp++;
- else
- tmp = nargv0;
- return(tmp);
-}
-
-#define BADCH (int)'?'
-#define BADARG (int)':'
-#define EMSG ""
-
-/*
- * getopt --
- * Parse argc/argv argument vector.
- */
-int
-getopt_internal(nargc, nargv, ostr)
- int nargc;
- char * const *nargv;
- const char *ostr;
-{
- static char *place = EMSG; /* option letter processing */
- char *oli; /* option letter list index */
-
- _DIAGASSERT(nargv != NULL);
- _DIAGASSERT(ostr != NULL);
-
- if (optreset || !*place) { /* update scanning pointer */
- optreset = 0;
- if (optind >= nargc || *(place = nargv[optind]) != '-') {
- place = EMSG;
- return (-1);
- }
- if (place[1] && *++place == '-') { /* found "--" */
- /* ++optind; */
- place = EMSG;
- return (-2);
- }
- } /* option letter okay? */
- if ((optopt = (int)*place++) == (int)':' ||
- !(oli = strchr(ostr, optopt))) {
- /*
- * if the user didn't specify '-' as an option,
- * assume it means -1.
- */
- if (optopt == (int)'-')
- return (-1);
- if (!*place)
- ++optind;
- if (opterr && *ostr != ':')
- (void)fprintf(stderr,
- "%s: illegal option -- %c\n", __progname(nargv[0]), optopt);
- return (BADCH);
- }
- if (*++oli != ':') { /* don't need argument */
- optarg = NULL;
- if (!*place)
- ++optind;
- } else { /* need an argument */
- if (*place) /* no white space */
- optarg = place;
- else if (nargc <= ++optind) { /* no arg */
- place = EMSG;
- if ((opterr) && (*ostr != ':'))
- (void)fprintf(stderr,
- "%s: option requires an argument -- %c\n",
- __progname(nargv[0]), optopt);
- return (BADARG);
- } else /* white space */
- optarg = nargv[optind];
- place = EMSG;
- ++optind;
- }
- return (optopt); /* dump back option letter */
-}
-
-#if 0
-/*
- * getopt --
- * Parse argc/argv argument vector.
- */
-int
-getopt2(nargc, nargv, ostr)
- int nargc;
- char * const *nargv;
- const char *ostr;
-{
- int retval;
-
- if ((retval = getopt_internal(nargc, nargv, ostr)) == -2) {
- retval = -1;
- ++optind;
- }
- return(retval);
-}
-#endif
-
-/*
- * getopt_long --
- * Parse argc/argv argument vector.
- */
-int
-getopt_long(nargc, nargv, options, long_options, index)
- int nargc;
- char ** nargv;
- char * options;
- struct option * long_options;
- int * index;
-{
- int retval;
-
- _DIAGASSERT(nargv != NULL);
- _DIAGASSERT(options != NULL);
- _DIAGASSERT(long_options != NULL);
- /* index may be NULL */
-
- if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
- char *current_argv = nargv[optind++] + 2, *has_equal;
- int i, current_argv_len, match = -1;
-
- if (*current_argv == '\0') {
- return(-1);
- }
- if ((has_equal = strchr(current_argv, '=')) != NULL) {
- current_argv_len = lws_ptr_diff(has_equal, current_argv);
- has_equal++;
- } else
- current_argv_len = (int)strlen(current_argv);
-
- for (i = 0; long_options[i].name; i++) {
- if (strncmp(current_argv, long_options[i].name, current_argv_len))
- continue;
-
- if (strlen(long_options[i].name) == (unsigned)current_argv_len) {
- match = i;
- break;
- }
- if (match == -1)
- match = i;
- }
- if (match != -1) {
- if (long_options[match].has_arg == required_argument ||
- long_options[match].has_arg == optional_argument) {
- if (has_equal)
- optarg = has_equal;
- else
- optarg = nargv[optind++];
- }
- if ((long_options[match].has_arg == required_argument)
- && (optarg == NULL)) {
- /*
- * Missing argument, leading :
- * indicates no error should be generated
- */
- if ((opterr) && (*options != ':'))
- (void)fprintf(stderr,
- "%s: option requires an argument -- %s\n",
- __progname(nargv[0]), current_argv);
- return (BADARG);
- }
- } else { /* No matching argument */
- if ((opterr) && (*options != ':'))
- (void)fprintf(stderr,
- "%s: illegal option -- %s\n", __progname(nargv[0]), current_argv);
- return (BADCH);
- }
- if (long_options[match].flag) {
- *long_options[match].flag = long_options[match].val;
- retval = 0;
- } else
- retval = long_options[match].val;
- if (index)
- *index = match;
- }
- return(retval);
-}
+ +/* + * Copyright (c) 1987, 1993, 1994, 1996 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "getopt.h" + +#define lws_ptr_diff(head, tail) \ + ((int)((char *)(head) - (char *)(tail))) + +extern int opterr; /* if error message should be printed */ +extern int optind; /* index into parent argv vector */ +extern int optopt; /* character checked for validity */ +extern int optreset; /* reset getopt */ +extern char *optarg; /* argument associated with option */ + +#define __P(x) x +#define _DIAGASSERT(x) assert(x) + +static char * __progname __P((char *)); +int getopt_internal __P((int, char * const *, const char *)); + +static char * +__progname(nargv0) + char * nargv0; +{ + char * tmp; + + _DIAGASSERT(nargv0 != NULL); + + tmp = strrchr(nargv0, '/'); + if (tmp) + tmp++; + else + tmp = nargv0; + return(tmp); +} + +#define BADCH (int)'?' +#define BADARG (int)':' +#define EMSG "" + +/* + * getopt -- + * Parse argc/argv argument vector. + */ +int +getopt_internal(nargc, nargv, ostr) + int nargc; + char * const *nargv; + const char *ostr; +{ + static char *place = EMSG; /* option letter processing */ + char *oli; /* option letter list index */ + + _DIAGASSERT(nargv != NULL); + _DIAGASSERT(ostr != NULL); + + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc || *(place = nargv[optind]) != '-') { + place = EMSG; + return (-1); + } + if (place[1] && *++place == '-') { /* found "--" */ + /* ++optind; */ + place = EMSG; + return (-2); + } + } /* option letter okay? */ + if ((optopt = (int)*place++) == (int)':' || + !(oli = strchr(ostr, optopt))) { + /* + * if the user didn't specify '-' as an option, + * assume it means -1. + */ + if (optopt == (int)'-') + return (-1); + if (!*place) + ++optind; + if (opterr && *ostr != ':') + (void)fprintf(stderr, + "%s: illegal option -- %c\n", __progname(nargv[0]), optopt); + return (BADCH); + } + if (*++oli != ':') { /* don't need argument */ + optarg = NULL; + if (!*place) + ++optind; + } else { /* need an argument */ + if (*place) /* no white space */ + optarg = place; + else if (nargc <= ++optind) { /* no arg */ + place = EMSG; + if ((opterr) && (*ostr != ':')) + (void)fprintf(stderr, + "%s: option requires an argument -- %c\n", + __progname(nargv[0]), optopt); + return (BADARG); + } else /* white space */ + optarg = nargv[optind]; + place = EMSG; + ++optind; + } + return (optopt); /* dump back option letter */ +} + +#if 0 +/* + * getopt -- + * Parse argc/argv argument vector. + */ +int +getopt2(nargc, nargv, ostr) + int nargc; + char * const *nargv; + const char *ostr; +{ + int retval; + + if ((retval = getopt_internal(nargc, nargv, ostr)) == -2) { + retval = -1; + ++optind; + } + return(retval); +} +#endif + +/* + * getopt_long -- + * Parse argc/argv argument vector. + */ +int +getopt_long(nargc, nargv, options, long_options, index) + int nargc; + char ** nargv; + char * options; + struct option * long_options; + int * index; +{ + int retval; + + _DIAGASSERT(nargv != NULL); + _DIAGASSERT(options != NULL); + _DIAGASSERT(long_options != NULL); + /* index may be NULL */ + + if ((retval = getopt_internal(nargc, nargv, options)) == -2) { + char *current_argv = nargv[optind++] + 2, *has_equal; + int i, current_argv_len, match = -1; + + if (*current_argv == '\0') { + return(-1); + } + if ((has_equal = strchr(current_argv, '=')) != NULL) { + current_argv_len = lws_ptr_diff(has_equal, current_argv); + has_equal++; + } else + current_argv_len = (int)strlen(current_argv); + + for (i = 0; long_options[i].name; i++) { + if (strncmp(current_argv, long_options[i].name, current_argv_len)) + continue; + + if (strlen(long_options[i].name) == (unsigned)current_argv_len) { + match = i; + break; + } + if (match == -1) + match = i; + } + if (match != -1) { + if (long_options[match].has_arg == required_argument || + long_options[match].has_arg == optional_argument) { + if (has_equal) + optarg = has_equal; + else + optarg = nargv[optind++]; + } + if ((long_options[match].has_arg == required_argument) + && (optarg == NULL)) { + /* + * Missing argument, leading : + * indicates no error should be generated + */ + if ((opterr) && (*options != ':')) + (void)fprintf(stderr, + "%s: option requires an argument -- %s\n", + __progname(nargv[0]), current_argv); + return (BADARG); + } + } else { /* No matching argument */ + if ((opterr) && (*options != ':')) + (void)fprintf(stderr, + "%s: illegal option -- %s\n", __progname(nargv[0]), current_argv); + return (BADCH); + } + if (long_options[match].flag) { + *long_options[match].flag = long_options[match].val; + retval = 0; + } else + retval = long_options[match].val; + if (index) + *index = match; + } + return(retval); +} diff --git a/thirdparty/libwebsockets/win32helpers/gettimeofday.c b/thirdparty/libwebsockets/win32helpers/gettimeofday.c index 08385c2320..35dd73531d 100644 --- a/thirdparty/libwebsockets/win32helpers/gettimeofday.c +++ b/thirdparty/libwebsockets/win32helpers/gettimeofday.c @@ -1,36 +1,36 @@ -#include <time.h>
-#include <windows.h> //I've omitted context line
-
-#include "gettimeofday.h"
-
-int gettimeofday(struct timeval *tv, struct timezone *tz)
-{
- FILETIME ft;
- unsigned __int64 tmpres = 0;
- static int tzflag;
-
- if (NULL != tv) {
- GetSystemTimeAsFileTime(&ft);
-
- tmpres |= ft.dwHighDateTime;
- tmpres <<= 32;
- tmpres |= ft.dwLowDateTime;
-
- /*converting file time to unix epoch*/
- tmpres /= 10; /*convert into microseconds*/
+#include <time.h> +#include <windows.h> //I've omitted context line + +#include "gettimeofday.h" + +int gettimeofday(struct timeval *tv, struct timezone *tz) +{ + FILETIME ft; + unsigned __int64 tmpres = 0; + static int tzflag; + + if (NULL != tv) { + GetSystemTimeAsFileTime(&ft); + + tmpres |= ft.dwHighDateTime; + tmpres <<= 32; + tmpres |= ft.dwLowDateTime; + + /*converting file time to unix epoch*/ + tmpres /= 10; /*convert into microseconds*/ tmpres -= DELTA_EPOCH_IN_MICROSECS; - tv->tv_sec = (long)(tmpres / 1000000UL);
- tv->tv_usec = (long)(tmpres % 1000000UL);
- }
-
- if (NULL != tz) {
- if (!tzflag) {
- _tzset();
- tzflag++;
- }
- tz->tz_minuteswest = _timezone / 60;
- tz->tz_dsttime = _daylight;
- }
-
- return 0;
-}
+ tv->tv_sec = (long)(tmpres / 1000000UL); + tv->tv_usec = (long)(tmpres % 1000000UL); + } + + if (NULL != tz) { + if (!tzflag) { + _tzset(); + tzflag++; + } + tz->tz_minuteswest = _timezone / 60; + tz->tz_dsttime = _daylight; + } + + return 0; +} diff --git a/thirdparty/miniupnpc/minissdpc.c b/thirdparty/miniupnpc/minissdpc.c index d76b242ad0..1d29b4ba5b 100644 --- a/thirdparty/miniupnpc/minissdpc.c +++ b/thirdparty/miniupnpc/minissdpc.c @@ -494,7 +494,6 @@ ssdpDiscoverDevices(const char * const deviceTypes[], struct addrinfo hints, *servinfo, *p; #endif #ifdef _WIN32 - MIB_IPFORWARDROW ip_forward; unsigned long _ttl = (unsigned long)ttl; #endif int linklocal = 1; @@ -538,61 +537,103 @@ ssdpDiscoverDevices(const char * const deviceTypes[], * SSDP multicast traffic */ /* Get IP associated with the index given in the ip_forward struct * in order to give this ip to setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF) */ - if(!ipv6 - && (GetBestRoute(inet_addr("223.255.255.255"), 0, &ip_forward) == NO_ERROR)) { - DWORD dwRetVal = 0; - PMIB_IPADDRTABLE pIPAddrTable; - DWORD dwSize = 0; -#ifdef DEBUG - IN_ADDR IPAddr; -#endif - int i; -#ifdef DEBUG - printf("ifIndex=%lu nextHop=%lx \n", ip_forward.dwForwardIfIndex, ip_forward.dwForwardNextHop); -#endif - pIPAddrTable = (MIB_IPADDRTABLE *) malloc(sizeof (MIB_IPADDRTABLE)); - if(pIPAddrTable) { - if (GetIpAddrTable(pIPAddrTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) { - free(pIPAddrTable); - pIPAddrTable = (MIB_IPADDRTABLE *) malloc(dwSize); - } - } - if(pIPAddrTable) { - dwRetVal = GetIpAddrTable( pIPAddrTable, &dwSize, 0 ); + if(!ipv6) { + DWORD ifbestidx; + SOCKADDR_IN destAddr; + memset(&destAddr, 0, sizeof(destAddr)); + destAddr.sin_family = AF_INET; + destAddr.sin_addr.s_addr = inet_addr("223.255.255.255"); + destAddr.sin_port = 0; + if (GetBestInterfaceEx((struct sockaddr *)&destAddr, &ifbestidx) == NO_ERROR) { + DWORD dwSize = 0; + DWORD dwRetVal = 0; + unsigned int i = 0; + ULONG flags = GAA_FLAG_INCLUDE_PREFIX; + ULONG family = AF_INET; + LPVOID lpMsgBuf = NULL; + PIP_ADAPTER_ADDRESSES pAddresses = NULL; + ULONG outBufLen = 0; + ULONG Iterations = 0; + PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL; + PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL; + PIP_ADAPTER_ANYCAST_ADDRESS pAnycast = NULL; + PIP_ADAPTER_MULTICAST_ADDRESS pMulticast = NULL; + IP_ADAPTER_DNS_SERVER_ADDRESS *pDnServer = NULL; + IP_ADAPTER_PREFIX *pPrefix = NULL; + + outBufLen = 15360; + do { + pAddresses = (IP_ADAPTER_ADDRESSES *) HeapAlloc(GetProcessHeap(), 0, outBufLen); + if (pAddresses == NULL) { + break; + } + + dwRetVal = GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen); + + if (dwRetVal == ERROR_BUFFER_OVERFLOW) { + HeapFree(GetProcessHeap(), 0, pAddresses); + pAddresses = NULL; + } else { + break; + } + Iterations++; + } while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (Iterations < 3)); + if (dwRetVal == NO_ERROR) { + pCurrAddresses = pAddresses; + while (pCurrAddresses) { #ifdef DEBUG - printf("\tNum Entries: %ld\n", pIPAddrTable->dwNumEntries); -#endif - for (i=0; i < (int) pIPAddrTable->dwNumEntries; i++) { -#ifdef DEBUG - printf("\n\tInterface Index[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwIndex); - IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwAddr; - printf("\tIP Address[%d]: \t%s\n", i, inet_ntoa(IPAddr) ); - IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwMask; - printf("\tSubnet Mask[%d]: \t%s\n", i, inet_ntoa(IPAddr) ); - IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwBCastAddr; - printf("\tBroadCast[%d]: \t%s (%ld)\n", i, inet_ntoa(IPAddr), pIPAddrTable->table[i].dwBCastAddr); - printf("\tReassembly size[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwReasmSize); - printf("\tType and State[%d]:", i); + printf("\tIfIndex (IPv4 interface): %u\n", pCurrAddresses->IfIndex); + printf("\tAdapter name: %s\n", pCurrAddresses->AdapterName); + pUnicast = pCurrAddresses->FirstUnicastAddress; + if (pUnicast != NULL) { + for (i = 0; pUnicast != NULL; i++) { + IPAddr.S_un.S_addr = (u_long) pUnicast->Address; + printf("\tIP Address[%d]: \t%s\n", i, inet_ntoa(IPAddr) ); + pUnicast = pUnicast->Next; + } + printf("\tNumber of Unicast Addresses: %d\n", i); + } + pAnycast = pCurrAddresses->FirstAnycastAddress; + if (pAnycast) { + for (i = 0; pAnycast != NULL; i++) { + IPAddr.S_un.S_addr = (u_long) pAnyCast->Address; + printf("\tAnycast Address[%d]: \t%s\n", i, inet_ntoa(IPAddr) ); + pAnycast = pAnycast->Next; + } + printf("\tNumber of Anycast Addresses: %d\n", i); + } + pMulticast = pCurrAddresses->FirstMulticastAddress; + if (pMulticast) { + for (i = 0; pMulticast != NULL; i++) { + IPAddr.S_un.S_addr = (u_long) pMultiCast->Address; + printf("\tMulticast Address[%d]: \t%s\n", i, inet_ntoa(IPAddr) ); + } + } printf("\n"); #endif - if (pIPAddrTable->table[i].dwIndex == ip_forward.dwForwardIfIndex) { + pUnicast = pCurrAddresses->FirstUnicastAddress; + if (pCurrAddresses->IfIndex == ifbestidx && pUnicast != NULL) { + SOCKADDR_IN *ipv4 = (SOCKADDR_IN *)(pUnicast->Address.lpSockaddr); /* Set the address of this interface to be used */ struct in_addr mc_if; memset(&mc_if, 0, sizeof(mc_if)); - mc_if.s_addr = pIPAddrTable->table[i].dwAddr; + mc_if.s_addr = ipv4->sin_addr.s_addr; if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) { PRINT_SOCKET_ERROR("setsockopt"); } - ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = pIPAddrTable->table[i].dwAddr; + ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = ipv4->sin_addr.s_addr; #ifndef DEBUG break; #endif } + pCurrAddresses = pCurrAddresses->Next; } } - free(pIPAddrTable); - pIPAddrTable = NULL; + if (pAddresses != NULL) { + HeapFree(GetProcessHeap(), 0, pAddresses); + pAddresses = NULL; + } } } #endif /* _WIN32 */ diff --git a/thirdparty/miniupnpc/miniupnpcstrings.h b/thirdparty/miniupnpc/miniupnpcstrings.h index 1d5c5882fd..a718cc7bbf 100644 --- a/thirdparty/miniupnpc/miniupnpcstrings.h +++ b/thirdparty/miniupnpc/miniupnpcstrings.h @@ -1,7 +1,7 @@ #ifndef MINIUPNPCSTRINGS_H_INCLUDED #define MINIUPNPCSTRINGS_H_INCLUDED -#include <version.h> +#include "core/version.h" #define OS_STRING VERSION_NAME "/1.0" #define MINIUPNPC_VERSION_STRING "2.1" diff --git a/thirdparty/misc/aes256.h b/thirdparty/misc/aes256.h index 8fcc25a4de..150a0670f5 100644 --- a/thirdparty/misc/aes256.h +++ b/thirdparty/misc/aes256.h @@ -21,7 +21,7 @@ #ifndef AES_256_H #define AES_256_H -#include "typedefs.h" +#include "core/typedefs.h" #ifdef __cplusplus extern "C" { diff --git a/scene/animation/tween_interpolaters.cpp b/thirdparty/misc/easing_equations.cpp index 11f2b0c17f..bc84564b19 100644 --- a/scene/animation/tween_interpolaters.cpp +++ b/thirdparty/misc/easing_equations.cpp @@ -1,34 +1,10 @@ -/*************************************************************************/ -/* tween_interpolaters.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 "tween.h" +/** + * Adapted from Penner Easing equations' C++ port. + * Source: https://github.com/jesusgollonet/ofpennereasing + * License: BSD-3-clause + */ + +#include "scene/animation/tween.h" const real_t pi = 3.1415926535898; diff --git a/thirdparty/misc/hq2x.cpp b/thirdparty/misc/hq2x.cpp index 7ebb505d64..9c089ba85c 100644 --- a/thirdparty/misc/hq2x.cpp +++ b/thirdparty/misc/hq2x.cpp @@ -16,8 +16,8 @@ #include "hq2x.h" -#include "math_funcs.h" +#include "core/math/math_funcs.h" static const uint32_t AMASK = 0xFF000000; static const uint32_t YMASK = 0x00FF0000; diff --git a/thirdparty/misc/hq2x.h b/thirdparty/misc/hq2x.h index 8f119d2a01..bebd917950 100644 --- a/thirdparty/misc/hq2x.h +++ b/thirdparty/misc/hq2x.h @@ -1,7 +1,7 @@ #ifndef HQ2X_H #define HQ2X_H -#include "typedefs.h" +#include "core/typedefs.h" uint32_t *hq2x_resize( diff --git a/thirdparty/misc/md5.h b/thirdparty/misc/md5.h index e99d58b443..14b3cd3ddf 100644 --- a/thirdparty/misc/md5.h +++ b/thirdparty/misc/md5.h @@ -42,7 +42,7 @@ /* NOT typedef a 32 bit type */ -#include "typedefs.h" +#include "core/typedefs.h" /* Data structure for MD5 (Message Digest) computation */ typedef struct { diff --git a/thirdparty/misc/open-simplex-noise-LICENSE b/thirdparty/misc/open-simplex-noise-LICENSE new file mode 100644 index 0000000000..a84c395662 --- /dev/null +++ b/thirdparty/misc/open-simplex-noise-LICENSE @@ -0,0 +1,25 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +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 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. + +For more information, please refer to <http://unlicense.org> + diff --git a/thirdparty/misc/open-simplex-noise-no-allocate.patch b/thirdparty/misc/open-simplex-noise-no-allocate.patch new file mode 100644 index 0000000000..fc3abe7d00 --- /dev/null +++ b/thirdparty/misc/open-simplex-noise-no-allocate.patch @@ -0,0 +1,133 @@ +diff -u orig/open-simplex-noise.c misc/open-simplex-noise.c +--- orig/open-simplex-noise.c 2018-09-14 11:11:40.049810000 +0200 ++++ misc/open-simplex-noise.c 2018-09-14 11:09:39.726457000 +0200 +@@ -13,6 +13,11 @@ + * of any particular randomization library, so results + * will be the same when ported to other languages. + */ ++ ++// -- GODOT start -- ++// Modified to work without allocating memory, also removed some unused function. ++// -- GODOT end -- ++ + #include <math.h> + #include <stdlib.h> + #include <stdint.h> +@@ -34,11 +39,12 @@ + + #define DEFAULT_SEED (0LL) + +-struct osn_context { ++// -- GODOT start -- ++/*struct osn_context { + int16_t *perm; + int16_t *permGradIndex3D; +-}; +- ++};*/ ++// -- GODOT end -- + #define ARRAYSIZE(x) (sizeof((x)) / sizeof((x)[0])) + + /* +@@ -126,7 +132,9 @@ + int xi = (int) x; + return x < xi ? xi - 1 : xi; + } +- ++ ++// -- GODOT start -- ++/* + static int allocate_perm(struct osn_context *ctx, int nperm, int ngrad) + { + if (ctx->perm) +@@ -154,18 +162,21 @@ + memcpy(ctx->perm, p, sizeof(*ctx->perm) * nelements); + + for (i = 0; i < 256; i++) { +- /* Since 3D has 24 gradients, simple bitmask won't work, so precompute modulo array. */ ++ // Since 3D has 24 gradients, simple bitmask won't work, so precompute modulo array. + ctx->permGradIndex3D[i] = (int16_t)((ctx->perm[i] % (ARRAYSIZE(gradients3D) / 3)) * 3); + } + return 0; + } ++*/ ++// -- GODOT end -- + + /* + * Initializes using a permutation array generated from a 64-bit seed. + * Generates a proper permutation (i.e. doesn't merely perform N successive pair + * swaps on a base array). Uses a simple 64-bit LCG. + */ +-int open_simplex_noise(int64_t seed, struct osn_context **ctx) ++// -- GODOT start -- ++int open_simplex_noise(int64_t seed, struct osn_context *ctx) + { + int rc; + int16_t source[256]; +@@ -174,20 +185,9 @@ + int16_t *permGradIndex3D; + int r; + +- *ctx = (struct osn_context *) malloc(sizeof(**ctx)); +- if (!(*ctx)) +- return -ENOMEM; +- (*ctx)->perm = NULL; +- (*ctx)->permGradIndex3D = NULL; +- +- rc = allocate_perm(*ctx, 256, 256); +- if (rc) { +- free(*ctx); +- return rc; +- } +- +- perm = (*ctx)->perm; +- permGradIndex3D = (*ctx)->permGradIndex3D; ++ perm = ctx->perm; ++ permGradIndex3D = ctx->permGradIndex3D; ++// -- GODOT end -- + + for (i = 0; i < 256; i++) + source[i] = (int16_t) i; +@@ -206,6 +206,8 @@ + return 0; + } + ++// -- GODOT start -- ++/* + void open_simplex_noise_free(struct osn_context *ctx) + { + if (!ctx) +@@ -220,6 +222,8 @@ + } + free(ctx); + } ++*/ ++// -- GODOT end -- + + /* 2D OpenSimplex (Simplectic) Noise. */ + double open_simplex_noise2(struct osn_context *ctx, double x, double y) +diff -u orig/open-simplex-noise.h misc/open-simplex-noise.h +--- orig/open-simplex-noise.h 2018-09-14 11:11:19.659807000 +0200 ++++ misc/open-simplex-noise.h 2018-09-14 11:10:05.006460000 +0200 +@@ -35,11 +35,18 @@ + extern "C" { + #endif + +-struct osn_context; ++// -- GODOT start -- ++// Modified to work without allocating memory, also removed some unused function. + +-int open_simplex_noise(int64_t seed, struct osn_context **ctx); ++struct osn_context { ++ int16_t perm[256]; ++ int16_t permGradIndex3D[256]; ++}; ++ ++int open_simplex_noise(int64_t seed, struct osn_context *ctx); ++//int open_simplex_noise_init_perm(struct osn_context *ctx, int16_t p[], int nelements); ++// -- GODOT end -- + void open_simplex_noise_free(struct osn_context *ctx); +-int open_simplex_noise_init_perm(struct osn_context *ctx, int16_t p[], int nelements); + double open_simplex_noise2(struct osn_context *ctx, double x, double y); + double open_simplex_noise3(struct osn_context *ctx, double x, double y, double z); + double open_simplex_noise4(struct osn_context *ctx, double x, double y, double z, double w); diff --git a/thirdparty/misc/open-simplex-noise.c b/thirdparty/misc/open-simplex-noise.c new file mode 100644 index 0000000000..42f2fbb5be --- /dev/null +++ b/thirdparty/misc/open-simplex-noise.c @@ -0,0 +1,2254 @@ +/* + * OpenSimplex (Simplectic) Noise in C. + * Ported by Stephen M. Cameron from Kurt Spencer's java implementation + * + * v1.1 (October 5, 2014) + * - Added 2D and 4D implementations. + * - Proper gradient sets for all dimensions, from a + * dimensionally-generalizable scheme with an actual + * rhyme and reason behind it. + * - Removed default permutation array in favor of + * default seed. + * - Changed seed-based constructor to be independent + * of any particular randomization library, so results + * will be the same when ported to other languages. + */ + +// -- GODOT start -- +// Modified to work without allocating memory, also removed some unused function. +// -- GODOT end -- + +#include <math.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <errno.h> + +#include "open-simplex-noise.h" + +#define STRETCH_CONSTANT_2D (-0.211324865405187) /* (1 / sqrt(2 + 1) - 1 ) / 2; */ +#define SQUISH_CONSTANT_2D (0.366025403784439) /* (sqrt(2 + 1) -1) / 2; */ +#define STRETCH_CONSTANT_3D (-1.0 / 6.0) /* (1 / sqrt(3 + 1) - 1) / 3; */ +#define SQUISH_CONSTANT_3D (1.0 / 3.0) /* (sqrt(3+1)-1)/3; */ +#define STRETCH_CONSTANT_4D (-0.138196601125011) /* (1 / sqrt(4 + 1) - 1) / 4; */ +#define SQUISH_CONSTANT_4D (0.309016994374947) /* (sqrt(4 + 1) - 1) / 4; */ + +#define NORM_CONSTANT_2D (47.0) +#define NORM_CONSTANT_3D (103.0) +#define NORM_CONSTANT_4D (30.0) + +#define DEFAULT_SEED (0LL) + +// -- GODOT start -- +/*struct osn_context { + int16_t *perm; + int16_t *permGradIndex3D; +};*/ +// -- GODOT end -- +#define ARRAYSIZE(x) (sizeof((x)) / sizeof((x)[0])) + +/* + * Gradients for 2D. They approximate the directions to the + * vertices of an octagon from the center. + */ +static const int8_t gradients2D[] = { + 5, 2, 2, 5, + -5, 2, -2, 5, + 5, -2, 2, -5, + -5, -2, -2, -5, +}; + +/* + * Gradients for 3D. They approximate the directions to the + * vertices of a rhombicuboctahedron from the center, skewed so + * that the triangular and square facets can be inscribed inside + * circles of the same radius. + */ +static const signed char gradients3D[] = { + -11, 4, 4, -4, 11, 4, -4, 4, 11, + 11, 4, 4, 4, 11, 4, 4, 4, 11, + -11, -4, 4, -4, -11, 4, -4, -4, 11, + 11, -4, 4, 4, -11, 4, 4, -4, 11, + -11, 4, -4, -4, 11, -4, -4, 4, -11, + 11, 4, -4, 4, 11, -4, 4, 4, -11, + -11, -4, -4, -4, -11, -4, -4, -4, -11, + 11, -4, -4, 4, -11, -4, 4, -4, -11, +}; + +/* + * Gradients for 4D. They approximate the directions to the + * vertices of a disprismatotesseractihexadecachoron from the center, + * skewed so that the tetrahedral and cubic facets can be inscribed inside + * spheres of the same radius. + */ +static const signed char gradients4D[] = { + 3, 1, 1, 1, 1, 3, 1, 1, 1, 1, 3, 1, 1, 1, 1, 3, + -3, 1, 1, 1, -1, 3, 1, 1, -1, 1, 3, 1, -1, 1, 1, 3, + 3, -1, 1, 1, 1, -3, 1, 1, 1, -1, 3, 1, 1, -1, 1, 3, + -3, -1, 1, 1, -1, -3, 1, 1, -1, -1, 3, 1, -1, -1, 1, 3, + 3, 1, -1, 1, 1, 3, -1, 1, 1, 1, -3, 1, 1, 1, -1, 3, + -3, 1, -1, 1, -1, 3, -1, 1, -1, 1, -3, 1, -1, 1, -1, 3, + 3, -1, -1, 1, 1, -3, -1, 1, 1, -1, -3, 1, 1, -1, -1, 3, + -3, -1, -1, 1, -1, -3, -1, 1, -1, -1, -3, 1, -1, -1, -1, 3, + 3, 1, 1, -1, 1, 3, 1, -1, 1, 1, 3, -1, 1, 1, 1, -3, + -3, 1, 1, -1, -1, 3, 1, -1, -1, 1, 3, -1, -1, 1, 1, -3, + 3, -1, 1, -1, 1, -3, 1, -1, 1, -1, 3, -1, 1, -1, 1, -3, + -3, -1, 1, -1, -1, -3, 1, -1, -1, -1, 3, -1, -1, -1, 1, -3, + 3, 1, -1, -1, 1, 3, -1, -1, 1, 1, -3, -1, 1, 1, -1, -3, + -3, 1, -1, -1, -1, 3, -1, -1, -1, 1, -3, -1, -1, 1, -1, -3, + 3, -1, -1, -1, 1, -3, -1, -1, 1, -1, -3, -1, 1, -1, -1, -3, + -3, -1, -1, -1, -1, -3, -1, -1, -1, -1, -3, -1, -1, -1, -1, -3, +}; + +static double extrapolate2(struct osn_context *ctx, int xsb, int ysb, double dx, double dy) +{ + int16_t *perm = ctx->perm; + int index = perm[(perm[xsb & 0xFF] + ysb) & 0xFF] & 0x0E; + return gradients2D[index] * dx + + gradients2D[index + 1] * dy; +} + +static double extrapolate3(struct osn_context *ctx, int xsb, int ysb, int zsb, double dx, double dy, double dz) +{ + int16_t *perm = ctx->perm; + int16_t *permGradIndex3D = ctx->permGradIndex3D; + int index = permGradIndex3D[(perm[(perm[xsb & 0xFF] + ysb) & 0xFF] + zsb) & 0xFF]; + return gradients3D[index] * dx + + gradients3D[index + 1] * dy + + gradients3D[index + 2] * dz; +} + +static double extrapolate4(struct osn_context *ctx, int xsb, int ysb, int zsb, int wsb, double dx, double dy, double dz, double dw) +{ + int16_t *perm = ctx->perm; + int index = perm[(perm[(perm[(perm[xsb & 0xFF] + ysb) & 0xFF] + zsb) & 0xFF] + wsb) & 0xFF] & 0xFC; + return gradients4D[index] * dx + + gradients4D[index + 1] * dy + + gradients4D[index + 2] * dz + + gradients4D[index + 3] * dw; +} + +static INLINE int fastFloor(double x) { + int xi = (int) x; + return x < xi ? xi - 1 : xi; +} + +// -- GODOT start -- +/* +static int allocate_perm(struct osn_context *ctx, int nperm, int ngrad) +{ + if (ctx->perm) + free(ctx->perm); + if (ctx->permGradIndex3D) + free(ctx->permGradIndex3D); + ctx->perm = (int16_t *) malloc(sizeof(*ctx->perm) * nperm); + if (!ctx->perm) + return -ENOMEM; + ctx->permGradIndex3D = (int16_t *) malloc(sizeof(*ctx->permGradIndex3D) * ngrad); + if (!ctx->permGradIndex3D) { + free(ctx->perm); + return -ENOMEM; + } + return 0; +} + +int open_simplex_noise_init_perm(struct osn_context *ctx, int16_t p[], int nelements) +{ + int i, rc; + + rc = allocate_perm(ctx, nelements, 256); + if (rc) + return rc; + memcpy(ctx->perm, p, sizeof(*ctx->perm) * nelements); + + for (i = 0; i < 256; i++) { + // Since 3D has 24 gradients, simple bitmask won't work, so precompute modulo array. + ctx->permGradIndex3D[i] = (int16_t)((ctx->perm[i] % (ARRAYSIZE(gradients3D) / 3)) * 3); + } + return 0; +} +*/ +// -- GODOT end -- + +/* + * Initializes using a permutation array generated from a 64-bit seed. + * Generates a proper permutation (i.e. doesn't merely perform N successive pair + * swaps on a base array). Uses a simple 64-bit LCG. + */ +// -- GODOT start -- +int open_simplex_noise(int64_t seed, struct osn_context *ctx) +{ + int rc; + int16_t source[256]; + int i; + int16_t *perm; + int16_t *permGradIndex3D; + int r; + + perm = ctx->perm; + permGradIndex3D = ctx->permGradIndex3D; +// -- GODOT end -- + + for (i = 0; i < 256; i++) + source[i] = (int16_t) i; + seed = seed * 6364136223846793005LL + 1442695040888963407LL; + seed = seed * 6364136223846793005LL + 1442695040888963407LL; + seed = seed * 6364136223846793005LL + 1442695040888963407LL; + for (i = 255; i >= 0; i--) { + seed = seed * 6364136223846793005LL + 1442695040888963407LL; + r = (int)((seed + 31) % (i + 1)); + if (r < 0) + r += (i + 1); + perm[i] = source[r]; + permGradIndex3D[i] = (short)((perm[i] % (ARRAYSIZE(gradients3D) / 3)) * 3); + source[r] = source[i]; + } + return 0; +} + +// -- GODOT start -- +/* +void open_simplex_noise_free(struct osn_context *ctx) +{ + if (!ctx) + return; + if (ctx->perm) { + free(ctx->perm); + ctx->perm = NULL; + } + if (ctx->permGradIndex3D) { + free(ctx->permGradIndex3D); + ctx->permGradIndex3D = NULL; + } + free(ctx); +} +*/ +// -- GODOT end -- + +/* 2D OpenSimplex (Simplectic) Noise. */ +double open_simplex_noise2(struct osn_context *ctx, double x, double y) +{ + + /* Place input coordinates onto grid. */ + double stretchOffset = (x + y) * STRETCH_CONSTANT_2D; + double xs = x + stretchOffset; + double ys = y + stretchOffset; + + /* Floor to get grid coordinates of rhombus (stretched square) super-cell origin. */ + int xsb = fastFloor(xs); + int ysb = fastFloor(ys); + + /* Skew out to get actual coordinates of rhombus origin. We'll need these later. */ + double squishOffset = (xsb + ysb) * SQUISH_CONSTANT_2D; + double xb = xsb + squishOffset; + double yb = ysb + squishOffset; + + /* Compute grid coordinates relative to rhombus origin. */ + double xins = xs - xsb; + double yins = ys - ysb; + + /* Sum those together to get a value that determines which region we're in. */ + double inSum = xins + yins; + + /* Positions relative to origin point. */ + double dx0 = x - xb; + double dy0 = y - yb; + + /* We'll be defining these inside the next block and using them afterwards. */ + double dx_ext, dy_ext; + int xsv_ext, ysv_ext; + + double dx1; + double dy1; + double attn1; + double dx2; + double dy2; + double attn2; + double zins; + double attn0; + double attn_ext; + + double value = 0; + + /* Contribution (1,0) */ + dx1 = dx0 - 1 - SQUISH_CONSTANT_2D; + dy1 = dy0 - 0 - SQUISH_CONSTANT_2D; + attn1 = 2 - dx1 * dx1 - dy1 * dy1; + if (attn1 > 0) { + attn1 *= attn1; + value += attn1 * attn1 * extrapolate2(ctx, xsb + 1, ysb + 0, dx1, dy1); + } + + /* Contribution (0,1) */ + dx2 = dx0 - 0 - SQUISH_CONSTANT_2D; + dy2 = dy0 - 1 - SQUISH_CONSTANT_2D; + attn2 = 2 - dx2 * dx2 - dy2 * dy2; + if (attn2 > 0) { + attn2 *= attn2; + value += attn2 * attn2 * extrapolate2(ctx, xsb + 0, ysb + 1, dx2, dy2); + } + + if (inSum <= 1) { /* We're inside the triangle (2-Simplex) at (0,0) */ + zins = 1 - inSum; + if (zins > xins || zins > yins) { /* (0,0) is one of the closest two triangular vertices */ + if (xins > yins) { + xsv_ext = xsb + 1; + ysv_ext = ysb - 1; + dx_ext = dx0 - 1; + dy_ext = dy0 + 1; + } else { + xsv_ext = xsb - 1; + ysv_ext = ysb + 1; + dx_ext = dx0 + 1; + dy_ext = dy0 - 1; + } + } else { /* (1,0) and (0,1) are the closest two vertices. */ + xsv_ext = xsb + 1; + ysv_ext = ysb + 1; + dx_ext = dx0 - 1 - 2 * SQUISH_CONSTANT_2D; + dy_ext = dy0 - 1 - 2 * SQUISH_CONSTANT_2D; + } + } else { /* We're inside the triangle (2-Simplex) at (1,1) */ + zins = 2 - inSum; + if (zins < xins || zins < yins) { /* (0,0) is one of the closest two triangular vertices */ + if (xins > yins) { + xsv_ext = xsb + 2; + ysv_ext = ysb + 0; + dx_ext = dx0 - 2 - 2 * SQUISH_CONSTANT_2D; + dy_ext = dy0 + 0 - 2 * SQUISH_CONSTANT_2D; + } else { + xsv_ext = xsb + 0; + ysv_ext = ysb + 2; + dx_ext = dx0 + 0 - 2 * SQUISH_CONSTANT_2D; + dy_ext = dy0 - 2 - 2 * SQUISH_CONSTANT_2D; + } + } else { /* (1,0) and (0,1) are the closest two vertices. */ + dx_ext = dx0; + dy_ext = dy0; + xsv_ext = xsb; + ysv_ext = ysb; + } + xsb += 1; + ysb += 1; + dx0 = dx0 - 1 - 2 * SQUISH_CONSTANT_2D; + dy0 = dy0 - 1 - 2 * SQUISH_CONSTANT_2D; + } + + /* Contribution (0,0) or (1,1) */ + attn0 = 2 - dx0 * dx0 - dy0 * dy0; + if (attn0 > 0) { + attn0 *= attn0; + value += attn0 * attn0 * extrapolate2(ctx, xsb, ysb, dx0, dy0); + } + + /* Extra Vertex */ + attn_ext = 2 - dx_ext * dx_ext - dy_ext * dy_ext; + if (attn_ext > 0) { + attn_ext *= attn_ext; + value += attn_ext * attn_ext * extrapolate2(ctx, xsv_ext, ysv_ext, dx_ext, dy_ext); + } + + return value / NORM_CONSTANT_2D; +} + +/* + * 3D OpenSimplex (Simplectic) Noise + */ +double open_simplex_noise3(struct osn_context *ctx, double x, double y, double z) +{ + + /* Place input coordinates on simplectic honeycomb. */ + double stretchOffset = (x + y + z) * STRETCH_CONSTANT_3D; + double xs = x + stretchOffset; + double ys = y + stretchOffset; + double zs = z + stretchOffset; + + /* Floor to get simplectic honeycomb coordinates of rhombohedron (stretched cube) super-cell origin. */ + int xsb = fastFloor(xs); + int ysb = fastFloor(ys); + int zsb = fastFloor(zs); + + /* Skew out to get actual coordinates of rhombohedron origin. We'll need these later. */ + double squishOffset = (xsb + ysb + zsb) * SQUISH_CONSTANT_3D; + double xb = xsb + squishOffset; + double yb = ysb + squishOffset; + double zb = zsb + squishOffset; + + /* Compute simplectic honeycomb coordinates relative to rhombohedral origin. */ + double xins = xs - xsb; + double yins = ys - ysb; + double zins = zs - zsb; + + /* Sum those together to get a value that determines which region we're in. */ + double inSum = xins + yins + zins; + + /* Positions relative to origin point. */ + double dx0 = x - xb; + double dy0 = y - yb; + double dz0 = z - zb; + + /* We'll be defining these inside the next block and using them afterwards. */ + double dx_ext0, dy_ext0, dz_ext0; + double dx_ext1, dy_ext1, dz_ext1; + int xsv_ext0, ysv_ext0, zsv_ext0; + int xsv_ext1, ysv_ext1, zsv_ext1; + + double wins; + int8_t c, c1, c2; + int8_t aPoint, bPoint; + double aScore, bScore; + int aIsFurtherSide; + int bIsFurtherSide; + double p1, p2, p3; + double score; + double attn0, attn1, attn2, attn3, attn4, attn5, attn6; + double dx1, dy1, dz1; + double dx2, dy2, dz2; + double dx3, dy3, dz3; + double dx4, dy4, dz4; + double dx5, dy5, dz5; + double dx6, dy6, dz6; + double attn_ext0, attn_ext1; + + double value = 0; + if (inSum <= 1) { /* We're inside the tetrahedron (3-Simplex) at (0,0,0) */ + + /* Determine which two of (0,0,1), (0,1,0), (1,0,0) are closest. */ + aPoint = 0x01; + aScore = xins; + bPoint = 0x02; + bScore = yins; + if (aScore >= bScore && zins > bScore) { + bScore = zins; + bPoint = 0x04; + } else if (aScore < bScore && zins > aScore) { + aScore = zins; + aPoint = 0x04; + } + + /* Now we determine the two lattice points not part of the tetrahedron that may contribute. + This depends on the closest two tetrahedral vertices, including (0,0,0) */ + wins = 1 - inSum; + if (wins > aScore || wins > bScore) { /* (0,0,0) is one of the closest two tetrahedral vertices. */ + c = (bScore > aScore ? bPoint : aPoint); /* Our other closest vertex is the closest out of a and b. */ + + if ((c & 0x01) == 0) { + xsv_ext0 = xsb - 1; + xsv_ext1 = xsb; + dx_ext0 = dx0 + 1; + dx_ext1 = dx0; + } else { + xsv_ext0 = xsv_ext1 = xsb + 1; + dx_ext0 = dx_ext1 = dx0 - 1; + } + + if ((c & 0x02) == 0) { + ysv_ext0 = ysv_ext1 = ysb; + dy_ext0 = dy_ext1 = dy0; + if ((c & 0x01) == 0) { + ysv_ext1 -= 1; + dy_ext1 += 1; + } else { + ysv_ext0 -= 1; + dy_ext0 += 1; + } + } else { + ysv_ext0 = ysv_ext1 = ysb + 1; + dy_ext0 = dy_ext1 = dy0 - 1; + } + + if ((c & 0x04) == 0) { + zsv_ext0 = zsb; + zsv_ext1 = zsb - 1; + dz_ext0 = dz0; + dz_ext1 = dz0 + 1; + } else { + zsv_ext0 = zsv_ext1 = zsb + 1; + dz_ext0 = dz_ext1 = dz0 - 1; + } + } else { /* (0,0,0) is not one of the closest two tetrahedral vertices. */ + c = (int8_t)(aPoint | bPoint); /* Our two extra vertices are determined by the closest two. */ + + if ((c & 0x01) == 0) { + xsv_ext0 = xsb; + xsv_ext1 = xsb - 1; + dx_ext0 = dx0 - 2 * SQUISH_CONSTANT_3D; + dx_ext1 = dx0 + 1 - SQUISH_CONSTANT_3D; + } else { + xsv_ext0 = xsv_ext1 = xsb + 1; + dx_ext0 = dx0 - 1 - 2 * SQUISH_CONSTANT_3D; + dx_ext1 = dx0 - 1 - SQUISH_CONSTANT_3D; + } + + if ((c & 0x02) == 0) { + ysv_ext0 = ysb; + ysv_ext1 = ysb - 1; + dy_ext0 = dy0 - 2 * SQUISH_CONSTANT_3D; + dy_ext1 = dy0 + 1 - SQUISH_CONSTANT_3D; + } else { + ysv_ext0 = ysv_ext1 = ysb + 1; + dy_ext0 = dy0 - 1 - 2 * SQUISH_CONSTANT_3D; + dy_ext1 = dy0 - 1 - SQUISH_CONSTANT_3D; + } + + if ((c & 0x04) == 0) { + zsv_ext0 = zsb; + zsv_ext1 = zsb - 1; + dz_ext0 = dz0 - 2 * SQUISH_CONSTANT_3D; + dz_ext1 = dz0 + 1 - SQUISH_CONSTANT_3D; + } else { + zsv_ext0 = zsv_ext1 = zsb + 1; + dz_ext0 = dz0 - 1 - 2 * SQUISH_CONSTANT_3D; + dz_ext1 = dz0 - 1 - SQUISH_CONSTANT_3D; + } + } + + /* Contribution (0,0,0) */ + attn0 = 2 - dx0 * dx0 - dy0 * dy0 - dz0 * dz0; + if (attn0 > 0) { + attn0 *= attn0; + value += attn0 * attn0 * extrapolate3(ctx, xsb + 0, ysb + 0, zsb + 0, dx0, dy0, dz0); + } + + /* Contribution (1,0,0) */ + dx1 = dx0 - 1 - SQUISH_CONSTANT_3D; + dy1 = dy0 - 0 - SQUISH_CONSTANT_3D; + dz1 = dz0 - 0 - SQUISH_CONSTANT_3D; + attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1; + if (attn1 > 0) { + attn1 *= attn1; + value += attn1 * attn1 * extrapolate3(ctx, xsb + 1, ysb + 0, zsb + 0, dx1, dy1, dz1); + } + + /* Contribution (0,1,0) */ + dx2 = dx0 - 0 - SQUISH_CONSTANT_3D; + dy2 = dy0 - 1 - SQUISH_CONSTANT_3D; + dz2 = dz1; + attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2; + if (attn2 > 0) { + attn2 *= attn2; + value += attn2 * attn2 * extrapolate3(ctx, xsb + 0, ysb + 1, zsb + 0, dx2, dy2, dz2); + } + + /* Contribution (0,0,1) */ + dx3 = dx2; + dy3 = dy1; + dz3 = dz0 - 1 - SQUISH_CONSTANT_3D; + attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3; + if (attn3 > 0) { + attn3 *= attn3; + value += attn3 * attn3 * extrapolate3(ctx, xsb + 0, ysb + 0, zsb + 1, dx3, dy3, dz3); + } + } else if (inSum >= 2) { /* We're inside the tetrahedron (3-Simplex) at (1,1,1) */ + + /* Determine which two tetrahedral vertices are the closest, out of (1,1,0), (1,0,1), (0,1,1) but not (1,1,1). */ + aPoint = 0x06; + aScore = xins; + bPoint = 0x05; + bScore = yins; + if (aScore <= bScore && zins < bScore) { + bScore = zins; + bPoint = 0x03; + } else if (aScore > bScore && zins < aScore) { + aScore = zins; + aPoint = 0x03; + } + + /* Now we determine the two lattice points not part of the tetrahedron that may contribute. + This depends on the closest two tetrahedral vertices, including (1,1,1) */ + wins = 3 - inSum; + if (wins < aScore || wins < bScore) { /* (1,1,1) is one of the closest two tetrahedral vertices. */ + c = (bScore < aScore ? bPoint : aPoint); /* Our other closest vertex is the closest out of a and b. */ + + if ((c & 0x01) != 0) { + xsv_ext0 = xsb + 2; + xsv_ext1 = xsb + 1; + dx_ext0 = dx0 - 2 - 3 * SQUISH_CONSTANT_3D; + dx_ext1 = dx0 - 1 - 3 * SQUISH_CONSTANT_3D; + } else { + xsv_ext0 = xsv_ext1 = xsb; + dx_ext0 = dx_ext1 = dx0 - 3 * SQUISH_CONSTANT_3D; + } + + if ((c & 0x02) != 0) { + ysv_ext0 = ysv_ext1 = ysb + 1; + dy_ext0 = dy_ext1 = dy0 - 1 - 3 * SQUISH_CONSTANT_3D; + if ((c & 0x01) != 0) { + ysv_ext1 += 1; + dy_ext1 -= 1; + } else { + ysv_ext0 += 1; + dy_ext0 -= 1; + } + } else { + ysv_ext0 = ysv_ext1 = ysb; + dy_ext0 = dy_ext1 = dy0 - 3 * SQUISH_CONSTANT_3D; + } + + if ((c & 0x04) != 0) { + zsv_ext0 = zsb + 1; + zsv_ext1 = zsb + 2; + dz_ext0 = dz0 - 1 - 3 * SQUISH_CONSTANT_3D; + dz_ext1 = dz0 - 2 - 3 * SQUISH_CONSTANT_3D; + } else { + zsv_ext0 = zsv_ext1 = zsb; + dz_ext0 = dz_ext1 = dz0 - 3 * SQUISH_CONSTANT_3D; + } + } else { /* (1,1,1) is not one of the closest two tetrahedral vertices. */ + c = (int8_t)(aPoint & bPoint); /* Our two extra vertices are determined by the closest two. */ + + if ((c & 0x01) != 0) { + xsv_ext0 = xsb + 1; + xsv_ext1 = xsb + 2; + dx_ext0 = dx0 - 1 - SQUISH_CONSTANT_3D; + dx_ext1 = dx0 - 2 - 2 * SQUISH_CONSTANT_3D; + } else { + xsv_ext0 = xsv_ext1 = xsb; + dx_ext0 = dx0 - SQUISH_CONSTANT_3D; + dx_ext1 = dx0 - 2 * SQUISH_CONSTANT_3D; + } + + if ((c & 0x02) != 0) { + ysv_ext0 = ysb + 1; + ysv_ext1 = ysb + 2; + dy_ext0 = dy0 - 1 - SQUISH_CONSTANT_3D; + dy_ext1 = dy0 - 2 - 2 * SQUISH_CONSTANT_3D; + } else { + ysv_ext0 = ysv_ext1 = ysb; + dy_ext0 = dy0 - SQUISH_CONSTANT_3D; + dy_ext1 = dy0 - 2 * SQUISH_CONSTANT_3D; + } + + if ((c & 0x04) != 0) { + zsv_ext0 = zsb + 1; + zsv_ext1 = zsb + 2; + dz_ext0 = dz0 - 1 - SQUISH_CONSTANT_3D; + dz_ext1 = dz0 - 2 - 2 * SQUISH_CONSTANT_3D; + } else { + zsv_ext0 = zsv_ext1 = zsb; + dz_ext0 = dz0 - SQUISH_CONSTANT_3D; + dz_ext1 = dz0 - 2 * SQUISH_CONSTANT_3D; + } + } + + /* Contribution (1,1,0) */ + dx3 = dx0 - 1 - 2 * SQUISH_CONSTANT_3D; + dy3 = dy0 - 1 - 2 * SQUISH_CONSTANT_3D; + dz3 = dz0 - 0 - 2 * SQUISH_CONSTANT_3D; + attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3; + if (attn3 > 0) { + attn3 *= attn3; + value += attn3 * attn3 * extrapolate3(ctx, xsb + 1, ysb + 1, zsb + 0, dx3, dy3, dz3); + } + + /* Contribution (1,0,1) */ + dx2 = dx3; + dy2 = dy0 - 0 - 2 * SQUISH_CONSTANT_3D; + dz2 = dz0 - 1 - 2 * SQUISH_CONSTANT_3D; + attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2; + if (attn2 > 0) { + attn2 *= attn2; + value += attn2 * attn2 * extrapolate3(ctx, xsb + 1, ysb + 0, zsb + 1, dx2, dy2, dz2); + } + + /* Contribution (0,1,1) */ + dx1 = dx0 - 0 - 2 * SQUISH_CONSTANT_3D; + dy1 = dy3; + dz1 = dz2; + attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1; + if (attn1 > 0) { + attn1 *= attn1; + value += attn1 * attn1 * extrapolate3(ctx, xsb + 0, ysb + 1, zsb + 1, dx1, dy1, dz1); + } + + /* Contribution (1,1,1) */ + dx0 = dx0 - 1 - 3 * SQUISH_CONSTANT_3D; + dy0 = dy0 - 1 - 3 * SQUISH_CONSTANT_3D; + dz0 = dz0 - 1 - 3 * SQUISH_CONSTANT_3D; + attn0 = 2 - dx0 * dx0 - dy0 * dy0 - dz0 * dz0; + if (attn0 > 0) { + attn0 *= attn0; + value += attn0 * attn0 * extrapolate3(ctx, xsb + 1, ysb + 1, zsb + 1, dx0, dy0, dz0); + } + } else { /* We're inside the octahedron (Rectified 3-Simplex) in between. + Decide between point (0,0,1) and (1,1,0) as closest */ + p1 = xins + yins; + if (p1 > 1) { + aScore = p1 - 1; + aPoint = 0x03; + aIsFurtherSide = 1; + } else { + aScore = 1 - p1; + aPoint = 0x04; + aIsFurtherSide = 0; + } + + /* Decide between point (0,1,0) and (1,0,1) as closest */ + p2 = xins + zins; + if (p2 > 1) { + bScore = p2 - 1; + bPoint = 0x05; + bIsFurtherSide = 1; + } else { + bScore = 1 - p2; + bPoint = 0x02; + bIsFurtherSide = 0; + } + + /* The closest out of the two (1,0,0) and (0,1,1) will replace the furthest out of the two decided above, if closer. */ + p3 = yins + zins; + if (p3 > 1) { + score = p3 - 1; + if (aScore <= bScore && aScore < score) { + aScore = score; + aPoint = 0x06; + aIsFurtherSide = 1; + } else if (aScore > bScore && bScore < score) { + bScore = score; + bPoint = 0x06; + bIsFurtherSide = 1; + } + } else { + score = 1 - p3; + if (aScore <= bScore && aScore < score) { + aScore = score; + aPoint = 0x01; + aIsFurtherSide = 0; + } else if (aScore > bScore && bScore < score) { + bScore = score; + bPoint = 0x01; + bIsFurtherSide = 0; + } + } + + /* Where each of the two closest points are determines how the extra two vertices are calculated. */ + if (aIsFurtherSide == bIsFurtherSide) { + if (aIsFurtherSide) { /* Both closest points on (1,1,1) side */ + + /* One of the two extra points is (1,1,1) */ + dx_ext0 = dx0 - 1 - 3 * SQUISH_CONSTANT_3D; + dy_ext0 = dy0 - 1 - 3 * SQUISH_CONSTANT_3D; + dz_ext0 = dz0 - 1 - 3 * SQUISH_CONSTANT_3D; + xsv_ext0 = xsb + 1; + ysv_ext0 = ysb + 1; + zsv_ext0 = zsb + 1; + + /* Other extra point is based on the shared axis. */ + c = (int8_t)(aPoint & bPoint); + if ((c & 0x01) != 0) { + dx_ext1 = dx0 - 2 - 2 * SQUISH_CONSTANT_3D; + dy_ext1 = dy0 - 2 * SQUISH_CONSTANT_3D; + dz_ext1 = dz0 - 2 * SQUISH_CONSTANT_3D; + xsv_ext1 = xsb + 2; + ysv_ext1 = ysb; + zsv_ext1 = zsb; + } else if ((c & 0x02) != 0) { + dx_ext1 = dx0 - 2 * SQUISH_CONSTANT_3D; + dy_ext1 = dy0 - 2 - 2 * SQUISH_CONSTANT_3D; + dz_ext1 = dz0 - 2 * SQUISH_CONSTANT_3D; + xsv_ext1 = xsb; + ysv_ext1 = ysb + 2; + zsv_ext1 = zsb; + } else { + dx_ext1 = dx0 - 2 * SQUISH_CONSTANT_3D; + dy_ext1 = dy0 - 2 * SQUISH_CONSTANT_3D; + dz_ext1 = dz0 - 2 - 2 * SQUISH_CONSTANT_3D; + xsv_ext1 = xsb; + ysv_ext1 = ysb; + zsv_ext1 = zsb + 2; + } + } else { /* Both closest points on (0,0,0) side */ + + /* One of the two extra points is (0,0,0) */ + dx_ext0 = dx0; + dy_ext0 = dy0; + dz_ext0 = dz0; + xsv_ext0 = xsb; + ysv_ext0 = ysb; + zsv_ext0 = zsb; + + /* Other extra point is based on the omitted axis. */ + c = (int8_t)(aPoint | bPoint); + if ((c & 0x01) == 0) { + dx_ext1 = dx0 + 1 - SQUISH_CONSTANT_3D; + dy_ext1 = dy0 - 1 - SQUISH_CONSTANT_3D; + dz_ext1 = dz0 - 1 - SQUISH_CONSTANT_3D; + xsv_ext1 = xsb - 1; + ysv_ext1 = ysb + 1; + zsv_ext1 = zsb + 1; + } else if ((c & 0x02) == 0) { + dx_ext1 = dx0 - 1 - SQUISH_CONSTANT_3D; + dy_ext1 = dy0 + 1 - SQUISH_CONSTANT_3D; + dz_ext1 = dz0 - 1 - SQUISH_CONSTANT_3D; + xsv_ext1 = xsb + 1; + ysv_ext1 = ysb - 1; + zsv_ext1 = zsb + 1; + } else { + dx_ext1 = dx0 - 1 - SQUISH_CONSTANT_3D; + dy_ext1 = dy0 - 1 - SQUISH_CONSTANT_3D; + dz_ext1 = dz0 + 1 - SQUISH_CONSTANT_3D; + xsv_ext1 = xsb + 1; + ysv_ext1 = ysb + 1; + zsv_ext1 = zsb - 1; + } + } + } else { /* One point on (0,0,0) side, one point on (1,1,1) side */ + if (aIsFurtherSide) { + c1 = aPoint; + c2 = bPoint; + } else { + c1 = bPoint; + c2 = aPoint; + } + + /* One contribution is a permutation of (1,1,-1) */ + if ((c1 & 0x01) == 0) { + dx_ext0 = dx0 + 1 - SQUISH_CONSTANT_3D; + dy_ext0 = dy0 - 1 - SQUISH_CONSTANT_3D; + dz_ext0 = dz0 - 1 - SQUISH_CONSTANT_3D; + xsv_ext0 = xsb - 1; + ysv_ext0 = ysb + 1; + zsv_ext0 = zsb + 1; + } else if ((c1 & 0x02) == 0) { + dx_ext0 = dx0 - 1 - SQUISH_CONSTANT_3D; + dy_ext0 = dy0 + 1 - SQUISH_CONSTANT_3D; + dz_ext0 = dz0 - 1 - SQUISH_CONSTANT_3D; + xsv_ext0 = xsb + 1; + ysv_ext0 = ysb - 1; + zsv_ext0 = zsb + 1; + } else { + dx_ext0 = dx0 - 1 - SQUISH_CONSTANT_3D; + dy_ext0 = dy0 - 1 - SQUISH_CONSTANT_3D; + dz_ext0 = dz0 + 1 - SQUISH_CONSTANT_3D; + xsv_ext0 = xsb + 1; + ysv_ext0 = ysb + 1; + zsv_ext0 = zsb - 1; + } + + /* One contribution is a permutation of (0,0,2) */ + dx_ext1 = dx0 - 2 * SQUISH_CONSTANT_3D; + dy_ext1 = dy0 - 2 * SQUISH_CONSTANT_3D; + dz_ext1 = dz0 - 2 * SQUISH_CONSTANT_3D; + xsv_ext1 = xsb; + ysv_ext1 = ysb; + zsv_ext1 = zsb; + if ((c2 & 0x01) != 0) { + dx_ext1 -= 2; + xsv_ext1 += 2; + } else if ((c2 & 0x02) != 0) { + dy_ext1 -= 2; + ysv_ext1 += 2; + } else { + dz_ext1 -= 2; + zsv_ext1 += 2; + } + } + + /* Contribution (1,0,0) */ + dx1 = dx0 - 1 - SQUISH_CONSTANT_3D; + dy1 = dy0 - 0 - SQUISH_CONSTANT_3D; + dz1 = dz0 - 0 - SQUISH_CONSTANT_3D; + attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1; + if (attn1 > 0) { + attn1 *= attn1; + value += attn1 * attn1 * extrapolate3(ctx, xsb + 1, ysb + 0, zsb + 0, dx1, dy1, dz1); + } + + /* Contribution (0,1,0) */ + dx2 = dx0 - 0 - SQUISH_CONSTANT_3D; + dy2 = dy0 - 1 - SQUISH_CONSTANT_3D; + dz2 = dz1; + attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2; + if (attn2 > 0) { + attn2 *= attn2; + value += attn2 * attn2 * extrapolate3(ctx, xsb + 0, ysb + 1, zsb + 0, dx2, dy2, dz2); + } + + /* Contribution (0,0,1) */ + dx3 = dx2; + dy3 = dy1; + dz3 = dz0 - 1 - SQUISH_CONSTANT_3D; + attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3; + if (attn3 > 0) { + attn3 *= attn3; + value += attn3 * attn3 * extrapolate3(ctx, xsb + 0, ysb + 0, zsb + 1, dx3, dy3, dz3); + } + + /* Contribution (1,1,0) */ + dx4 = dx0 - 1 - 2 * SQUISH_CONSTANT_3D; + dy4 = dy0 - 1 - 2 * SQUISH_CONSTANT_3D; + dz4 = dz0 - 0 - 2 * SQUISH_CONSTANT_3D; + attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4; + if (attn4 > 0) { + attn4 *= attn4; + value += attn4 * attn4 * extrapolate3(ctx, xsb + 1, ysb + 1, zsb + 0, dx4, dy4, dz4); + } + + /* Contribution (1,0,1) */ + dx5 = dx4; + dy5 = dy0 - 0 - 2 * SQUISH_CONSTANT_3D; + dz5 = dz0 - 1 - 2 * SQUISH_CONSTANT_3D; + attn5 = 2 - dx5 * dx5 - dy5 * dy5 - dz5 * dz5; + if (attn5 > 0) { + attn5 *= attn5; + value += attn5 * attn5 * extrapolate3(ctx, xsb + 1, ysb + 0, zsb + 1, dx5, dy5, dz5); + } + + /* Contribution (0,1,1) */ + dx6 = dx0 - 0 - 2 * SQUISH_CONSTANT_3D; + dy6 = dy4; + dz6 = dz5; + attn6 = 2 - dx6 * dx6 - dy6 * dy6 - dz6 * dz6; + if (attn6 > 0) { + attn6 *= attn6; + value += attn6 * attn6 * extrapolate3(ctx, xsb + 0, ysb + 1, zsb + 1, dx6, dy6, dz6); + } + } + + /* First extra vertex */ + attn_ext0 = 2 - dx_ext0 * dx_ext0 - dy_ext0 * dy_ext0 - dz_ext0 * dz_ext0; + if (attn_ext0 > 0) + { + attn_ext0 *= attn_ext0; + value += attn_ext0 * attn_ext0 * extrapolate3(ctx, xsv_ext0, ysv_ext0, zsv_ext0, dx_ext0, dy_ext0, dz_ext0); + } + + /* Second extra vertex */ + attn_ext1 = 2 - dx_ext1 * dx_ext1 - dy_ext1 * dy_ext1 - dz_ext1 * dz_ext1; + if (attn_ext1 > 0) + { + attn_ext1 *= attn_ext1; + value += attn_ext1 * attn_ext1 * extrapolate3(ctx, xsv_ext1, ysv_ext1, zsv_ext1, dx_ext1, dy_ext1, dz_ext1); + } + + return value / NORM_CONSTANT_3D; +} + +/* + * 4D OpenSimplex (Simplectic) Noise. + */ +double open_simplex_noise4(struct osn_context *ctx, double x, double y, double z, double w) +{ + double uins; + double dx1, dy1, dz1, dw1; + double dx2, dy2, dz2, dw2; + double dx3, dy3, dz3, dw3; + double dx4, dy4, dz4, dw4; + double dx5, dy5, dz5, dw5; + double dx6, dy6, dz6, dw6; + double dx7, dy7, dz7, dw7; + double dx8, dy8, dz8, dw8; + double dx9, dy9, dz9, dw9; + double dx10, dy10, dz10, dw10; + double attn0, attn1, attn2, attn3, attn4; + double attn5, attn6, attn7, attn8, attn9, attn10; + double attn_ext0, attn_ext1, attn_ext2; + int8_t c, c1, c2; + int8_t aPoint, bPoint; + double aScore, bScore; + int aIsBiggerSide; + int bIsBiggerSide; + double p1, p2, p3, p4; + double score; + + /* Place input coordinates on simplectic honeycomb. */ + double stretchOffset = (x + y + z + w) * STRETCH_CONSTANT_4D; + double xs = x + stretchOffset; + double ys = y + stretchOffset; + double zs = z + stretchOffset; + double ws = w + stretchOffset; + + /* Floor to get simplectic honeycomb coordinates of rhombo-hypercube super-cell origin. */ + int xsb = fastFloor(xs); + int ysb = fastFloor(ys); + int zsb = fastFloor(zs); + int wsb = fastFloor(ws); + + /* Skew out to get actual coordinates of stretched rhombo-hypercube origin. We'll need these later. */ + double squishOffset = (xsb + ysb + zsb + wsb) * SQUISH_CONSTANT_4D; + double xb = xsb + squishOffset; + double yb = ysb + squishOffset; + double zb = zsb + squishOffset; + double wb = wsb + squishOffset; + + /* Compute simplectic honeycomb coordinates relative to rhombo-hypercube origin. */ + double xins = xs - xsb; + double yins = ys - ysb; + double zins = zs - zsb; + double wins = ws - wsb; + + /* Sum those together to get a value that determines which region we're in. */ + double inSum = xins + yins + zins + wins; + + /* Positions relative to origin point. */ + double dx0 = x - xb; + double dy0 = y - yb; + double dz0 = z - zb; + double dw0 = w - wb; + + /* We'll be defining these inside the next block and using them afterwards. */ + double dx_ext0, dy_ext0, dz_ext0, dw_ext0; + double dx_ext1, dy_ext1, dz_ext1, dw_ext1; + double dx_ext2, dy_ext2, dz_ext2, dw_ext2; + int xsv_ext0, ysv_ext0, zsv_ext0, wsv_ext0; + int xsv_ext1, ysv_ext1, zsv_ext1, wsv_ext1; + int xsv_ext2, ysv_ext2, zsv_ext2, wsv_ext2; + + double value = 0; + if (inSum <= 1) { /* We're inside the pentachoron (4-Simplex) at (0,0,0,0) */ + + /* Determine which two of (0,0,0,1), (0,0,1,0), (0,1,0,0), (1,0,0,0) are closest. */ + aPoint = 0x01; + aScore = xins; + bPoint = 0x02; + bScore = yins; + if (aScore >= bScore && zins > bScore) { + bScore = zins; + bPoint = 0x04; + } else if (aScore < bScore && zins > aScore) { + aScore = zins; + aPoint = 0x04; + } + if (aScore >= bScore && wins > bScore) { + bScore = wins; + bPoint = 0x08; + } else if (aScore < bScore && wins > aScore) { + aScore = wins; + aPoint = 0x08; + } + + /* Now we determine the three lattice points not part of the pentachoron that may contribute. + This depends on the closest two pentachoron vertices, including (0,0,0,0) */ + uins = 1 - inSum; + if (uins > aScore || uins > bScore) { /* (0,0,0,0) is one of the closest two pentachoron vertices. */ + c = (bScore > aScore ? bPoint : aPoint); /* Our other closest vertex is the closest out of a and b. */ + if ((c & 0x01) == 0) { + xsv_ext0 = xsb - 1; + xsv_ext1 = xsv_ext2 = xsb; + dx_ext0 = dx0 + 1; + dx_ext1 = dx_ext2 = dx0; + } else { + xsv_ext0 = xsv_ext1 = xsv_ext2 = xsb + 1; + dx_ext0 = dx_ext1 = dx_ext2 = dx0 - 1; + } + + if ((c & 0x02) == 0) { + ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb; + dy_ext0 = dy_ext1 = dy_ext2 = dy0; + if ((c & 0x01) == 0x01) { + ysv_ext0 -= 1; + dy_ext0 += 1; + } else { + ysv_ext1 -= 1; + dy_ext1 += 1; + } + } else { + ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb + 1; + dy_ext0 = dy_ext1 = dy_ext2 = dy0 - 1; + } + + if ((c & 0x04) == 0) { + zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb; + dz_ext0 = dz_ext1 = dz_ext2 = dz0; + if ((c & 0x03) != 0) { + if ((c & 0x03) == 0x03) { + zsv_ext0 -= 1; + dz_ext0 += 1; + } else { + zsv_ext1 -= 1; + dz_ext1 += 1; + } + } else { + zsv_ext2 -= 1; + dz_ext2 += 1; + } + } else { + zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb + 1; + dz_ext0 = dz_ext1 = dz_ext2 = dz0 - 1; + } + + if ((c & 0x08) == 0) { + wsv_ext0 = wsv_ext1 = wsb; + wsv_ext2 = wsb - 1; + dw_ext0 = dw_ext1 = dw0; + dw_ext2 = dw0 + 1; + } else { + wsv_ext0 = wsv_ext1 = wsv_ext2 = wsb + 1; + dw_ext0 = dw_ext1 = dw_ext2 = dw0 - 1; + } + } else { /* (0,0,0,0) is not one of the closest two pentachoron vertices. */ + c = (int8_t)(aPoint | bPoint); /* Our three extra vertices are determined by the closest two. */ + + if ((c & 0x01) == 0) { + xsv_ext0 = xsv_ext2 = xsb; + xsv_ext1 = xsb - 1; + dx_ext0 = dx0 - 2 * SQUISH_CONSTANT_4D; + dx_ext1 = dx0 + 1 - SQUISH_CONSTANT_4D; + dx_ext2 = dx0 - SQUISH_CONSTANT_4D; + } else { + xsv_ext0 = xsv_ext1 = xsv_ext2 = xsb + 1; + dx_ext0 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; + dx_ext1 = dx_ext2 = dx0 - 1 - SQUISH_CONSTANT_4D; + } + + if ((c & 0x02) == 0) { + ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb; + dy_ext0 = dy0 - 2 * SQUISH_CONSTANT_4D; + dy_ext1 = dy_ext2 = dy0 - SQUISH_CONSTANT_4D; + if ((c & 0x01) == 0x01) { + ysv_ext1 -= 1; + dy_ext1 += 1; + } else { + ysv_ext2 -= 1; + dy_ext2 += 1; + } + } else { + ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb + 1; + dy_ext0 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; + dy_ext1 = dy_ext2 = dy0 - 1 - SQUISH_CONSTANT_4D; + } + + if ((c & 0x04) == 0) { + zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb; + dz_ext0 = dz0 - 2 * SQUISH_CONSTANT_4D; + dz_ext1 = dz_ext2 = dz0 - SQUISH_CONSTANT_4D; + if ((c & 0x03) == 0x03) { + zsv_ext1 -= 1; + dz_ext1 += 1; + } else { + zsv_ext2 -= 1; + dz_ext2 += 1; + } + } else { + zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb + 1; + dz_ext0 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; + dz_ext1 = dz_ext2 = dz0 - 1 - SQUISH_CONSTANT_4D; + } + + if ((c & 0x08) == 0) { + wsv_ext0 = wsv_ext1 = wsb; + wsv_ext2 = wsb - 1; + dw_ext0 = dw0 - 2 * SQUISH_CONSTANT_4D; + dw_ext1 = dw0 - SQUISH_CONSTANT_4D; + dw_ext2 = dw0 + 1 - SQUISH_CONSTANT_4D; + } else { + wsv_ext0 = wsv_ext1 = wsv_ext2 = wsb + 1; + dw_ext0 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; + dw_ext1 = dw_ext2 = dw0 - 1 - SQUISH_CONSTANT_4D; + } + } + + /* Contribution (0,0,0,0) */ + attn0 = 2 - dx0 * dx0 - dy0 * dy0 - dz0 * dz0 - dw0 * dw0; + if (attn0 > 0) { + attn0 *= attn0; + value += attn0 * attn0 * extrapolate4(ctx, xsb + 0, ysb + 0, zsb + 0, wsb + 0, dx0, dy0, dz0, dw0); + } + + /* Contribution (1,0,0,0) */ + dx1 = dx0 - 1 - SQUISH_CONSTANT_4D; + dy1 = dy0 - 0 - SQUISH_CONSTANT_4D; + dz1 = dz0 - 0 - SQUISH_CONSTANT_4D; + dw1 = dw0 - 0 - SQUISH_CONSTANT_4D; + attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1 - dw1 * dw1; + if (attn1 > 0) { + attn1 *= attn1; + value += attn1 * attn1 * extrapolate4(ctx, xsb + 1, ysb + 0, zsb + 0, wsb + 0, dx1, dy1, dz1, dw1); + } + + /* Contribution (0,1,0,0) */ + dx2 = dx0 - 0 - SQUISH_CONSTANT_4D; + dy2 = dy0 - 1 - SQUISH_CONSTANT_4D; + dz2 = dz1; + dw2 = dw1; + attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2 - dw2 * dw2; + if (attn2 > 0) { + attn2 *= attn2; + value += attn2 * attn2 * extrapolate4(ctx, xsb + 0, ysb + 1, zsb + 0, wsb + 0, dx2, dy2, dz2, dw2); + } + + /* Contribution (0,0,1,0) */ + dx3 = dx2; + dy3 = dy1; + dz3 = dz0 - 1 - SQUISH_CONSTANT_4D; + dw3 = dw1; + attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3 - dw3 * dw3; + if (attn3 > 0) { + attn3 *= attn3; + value += attn3 * attn3 * extrapolate4(ctx, xsb + 0, ysb + 0, zsb + 1, wsb + 0, dx3, dy3, dz3, dw3); + } + + /* Contribution (0,0,0,1) */ + dx4 = dx2; + dy4 = dy1; + dz4 = dz1; + dw4 = dw0 - 1 - SQUISH_CONSTANT_4D; + attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4 - dw4 * dw4; + if (attn4 > 0) { + attn4 *= attn4; + value += attn4 * attn4 * extrapolate4(ctx, xsb + 0, ysb + 0, zsb + 0, wsb + 1, dx4, dy4, dz4, dw4); + } + } else if (inSum >= 3) { /* We're inside the pentachoron (4-Simplex) at (1,1,1,1) + Determine which two of (1,1,1,0), (1,1,0,1), (1,0,1,1), (0,1,1,1) are closest. */ + aPoint = 0x0E; + aScore = xins; + bPoint = 0x0D; + bScore = yins; + if (aScore <= bScore && zins < bScore) { + bScore = zins; + bPoint = 0x0B; + } else if (aScore > bScore && zins < aScore) { + aScore = zins; + aPoint = 0x0B; + } + if (aScore <= bScore && wins < bScore) { + bScore = wins; + bPoint = 0x07; + } else if (aScore > bScore && wins < aScore) { + aScore = wins; + aPoint = 0x07; + } + + /* Now we determine the three lattice points not part of the pentachoron that may contribute. + This depends on the closest two pentachoron vertices, including (0,0,0,0) */ + uins = 4 - inSum; + if (uins < aScore || uins < bScore) { /* (1,1,1,1) is one of the closest two pentachoron vertices. */ + c = (bScore < aScore ? bPoint : aPoint); /* Our other closest vertex is the closest out of a and b. */ + + if ((c & 0x01) != 0) { + xsv_ext0 = xsb + 2; + xsv_ext1 = xsv_ext2 = xsb + 1; + dx_ext0 = dx0 - 2 - 4 * SQUISH_CONSTANT_4D; + dx_ext1 = dx_ext2 = dx0 - 1 - 4 * SQUISH_CONSTANT_4D; + } else { + xsv_ext0 = xsv_ext1 = xsv_ext2 = xsb; + dx_ext0 = dx_ext1 = dx_ext2 = dx0 - 4 * SQUISH_CONSTANT_4D; + } + + if ((c & 0x02) != 0) { + ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb + 1; + dy_ext0 = dy_ext1 = dy_ext2 = dy0 - 1 - 4 * SQUISH_CONSTANT_4D; + if ((c & 0x01) != 0) { + ysv_ext1 += 1; + dy_ext1 -= 1; + } else { + ysv_ext0 += 1; + dy_ext0 -= 1; + } + } else { + ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb; + dy_ext0 = dy_ext1 = dy_ext2 = dy0 - 4 * SQUISH_CONSTANT_4D; + } + + if ((c & 0x04) != 0) { + zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb + 1; + dz_ext0 = dz_ext1 = dz_ext2 = dz0 - 1 - 4 * SQUISH_CONSTANT_4D; + if ((c & 0x03) != 0x03) { + if ((c & 0x03) == 0) { + zsv_ext0 += 1; + dz_ext0 -= 1; + } else { + zsv_ext1 += 1; + dz_ext1 -= 1; + } + } else { + zsv_ext2 += 1; + dz_ext2 -= 1; + } + } else { + zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb; + dz_ext0 = dz_ext1 = dz_ext2 = dz0 - 4 * SQUISH_CONSTANT_4D; + } + + if ((c & 0x08) != 0) { + wsv_ext0 = wsv_ext1 = wsb + 1; + wsv_ext2 = wsb + 2; + dw_ext0 = dw_ext1 = dw0 - 1 - 4 * SQUISH_CONSTANT_4D; + dw_ext2 = dw0 - 2 - 4 * SQUISH_CONSTANT_4D; + } else { + wsv_ext0 = wsv_ext1 = wsv_ext2 = wsb; + dw_ext0 = dw_ext1 = dw_ext2 = dw0 - 4 * SQUISH_CONSTANT_4D; + } + } else { /* (1,1,1,1) is not one of the closest two pentachoron vertices. */ + c = (int8_t)(aPoint & bPoint); /* Our three extra vertices are determined by the closest two. */ + + if ((c & 0x01) != 0) { + xsv_ext0 = xsv_ext2 = xsb + 1; + xsv_ext1 = xsb + 2; + dx_ext0 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; + dx_ext1 = dx0 - 2 - 3 * SQUISH_CONSTANT_4D; + dx_ext2 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D; + } else { + xsv_ext0 = xsv_ext1 = xsv_ext2 = xsb; + dx_ext0 = dx0 - 2 * SQUISH_CONSTANT_4D; + dx_ext1 = dx_ext2 = dx0 - 3 * SQUISH_CONSTANT_4D; + } + + if ((c & 0x02) != 0) { + ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb + 1; + dy_ext0 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; + dy_ext1 = dy_ext2 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D; + if ((c & 0x01) != 0) { + ysv_ext2 += 1; + dy_ext2 -= 1; + } else { + ysv_ext1 += 1; + dy_ext1 -= 1; + } + } else { + ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb; + dy_ext0 = dy0 - 2 * SQUISH_CONSTANT_4D; + dy_ext1 = dy_ext2 = dy0 - 3 * SQUISH_CONSTANT_4D; + } + + if ((c & 0x04) != 0) { + zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb + 1; + dz_ext0 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; + dz_ext1 = dz_ext2 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D; + if ((c & 0x03) != 0) { + zsv_ext2 += 1; + dz_ext2 -= 1; + } else { + zsv_ext1 += 1; + dz_ext1 -= 1; + } + } else { + zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb; + dz_ext0 = dz0 - 2 * SQUISH_CONSTANT_4D; + dz_ext1 = dz_ext2 = dz0 - 3 * SQUISH_CONSTANT_4D; + } + + if ((c & 0x08) != 0) { + wsv_ext0 = wsv_ext1 = wsb + 1; + wsv_ext2 = wsb + 2; + dw_ext0 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; + dw_ext1 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D; + dw_ext2 = dw0 - 2 - 3 * SQUISH_CONSTANT_4D; + } else { + wsv_ext0 = wsv_ext1 = wsv_ext2 = wsb; + dw_ext0 = dw0 - 2 * SQUISH_CONSTANT_4D; + dw_ext1 = dw_ext2 = dw0 - 3 * SQUISH_CONSTANT_4D; + } + } + + /* Contribution (1,1,1,0) */ + dx4 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D; + dy4 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D; + dz4 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D; + dw4 = dw0 - 3 * SQUISH_CONSTANT_4D; + attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4 - dw4 * dw4; + if (attn4 > 0) { + attn4 *= attn4; + value += attn4 * attn4 * extrapolate4(ctx, xsb + 1, ysb + 1, zsb + 1, wsb + 0, dx4, dy4, dz4, dw4); + } + + /* Contribution (1,1,0,1) */ + dx3 = dx4; + dy3 = dy4; + dz3 = dz0 - 3 * SQUISH_CONSTANT_4D; + dw3 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D; + attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3 - dw3 * dw3; + if (attn3 > 0) { + attn3 *= attn3; + value += attn3 * attn3 * extrapolate4(ctx, xsb + 1, ysb + 1, zsb + 0, wsb + 1, dx3, dy3, dz3, dw3); + } + + /* Contribution (1,0,1,1) */ + dx2 = dx4; + dy2 = dy0 - 3 * SQUISH_CONSTANT_4D; + dz2 = dz4; + dw2 = dw3; + attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2 - dw2 * dw2; + if (attn2 > 0) { + attn2 *= attn2; + value += attn2 * attn2 * extrapolate4(ctx, xsb + 1, ysb + 0, zsb + 1, wsb + 1, dx2, dy2, dz2, dw2); + } + + /* Contribution (0,1,1,1) */ + dx1 = dx0 - 3 * SQUISH_CONSTANT_4D; + dz1 = dz4; + dy1 = dy4; + dw1 = dw3; + attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1 - dw1 * dw1; + if (attn1 > 0) { + attn1 *= attn1; + value += attn1 * attn1 * extrapolate4(ctx, xsb + 0, ysb + 1, zsb + 1, wsb + 1, dx1, dy1, dz1, dw1); + } + + /* Contribution (1,1,1,1) */ + dx0 = dx0 - 1 - 4 * SQUISH_CONSTANT_4D; + dy0 = dy0 - 1 - 4 * SQUISH_CONSTANT_4D; + dz0 = dz0 - 1 - 4 * SQUISH_CONSTANT_4D; + dw0 = dw0 - 1 - 4 * SQUISH_CONSTANT_4D; + attn0 = 2 - dx0 * dx0 - dy0 * dy0 - dz0 * dz0 - dw0 * dw0; + if (attn0 > 0) { + attn0 *= attn0; + value += attn0 * attn0 * extrapolate4(ctx, xsb + 1, ysb + 1, zsb + 1, wsb + 1, dx0, dy0, dz0, dw0); + } + } else if (inSum <= 2) { /* We're inside the first dispentachoron (Rectified 4-Simplex) */ + aIsBiggerSide = 1; + bIsBiggerSide = 1; + + /* Decide between (1,1,0,0) and (0,0,1,1) */ + if (xins + yins > zins + wins) { + aScore = xins + yins; + aPoint = 0x03; + } else { + aScore = zins + wins; + aPoint = 0x0C; + } + + /* Decide between (1,0,1,0) and (0,1,0,1) */ + if (xins + zins > yins + wins) { + bScore = xins + zins; + bPoint = 0x05; + } else { + bScore = yins + wins; + bPoint = 0x0A; + } + + /* Closer between (1,0,0,1) and (0,1,1,0) will replace the further of a and b, if closer. */ + if (xins + wins > yins + zins) { + score = xins + wins; + if (aScore >= bScore && score > bScore) { + bScore = score; + bPoint = 0x09; + } else if (aScore < bScore && score > aScore) { + aScore = score; + aPoint = 0x09; + } + } else { + score = yins + zins; + if (aScore >= bScore && score > bScore) { + bScore = score; + bPoint = 0x06; + } else if (aScore < bScore && score > aScore) { + aScore = score; + aPoint = 0x06; + } + } + + /* Decide if (1,0,0,0) is closer. */ + p1 = 2 - inSum + xins; + if (aScore >= bScore && p1 > bScore) { + bScore = p1; + bPoint = 0x01; + bIsBiggerSide = 0; + } else if (aScore < bScore && p1 > aScore) { + aScore = p1; + aPoint = 0x01; + aIsBiggerSide = 0; + } + + /* Decide if (0,1,0,0) is closer. */ + p2 = 2 - inSum + yins; + if (aScore >= bScore && p2 > bScore) { + bScore = p2; + bPoint = 0x02; + bIsBiggerSide = 0; + } else if (aScore < bScore && p2 > aScore) { + aScore = p2; + aPoint = 0x02; + aIsBiggerSide = 0; + } + + /* Decide if (0,0,1,0) is closer. */ + p3 = 2 - inSum + zins; + if (aScore >= bScore && p3 > bScore) { + bScore = p3; + bPoint = 0x04; + bIsBiggerSide = 0; + } else if (aScore < bScore && p3 > aScore) { + aScore = p3; + aPoint = 0x04; + aIsBiggerSide = 0; + } + + /* Decide if (0,0,0,1) is closer. */ + p4 = 2 - inSum + wins; + if (aScore >= bScore && p4 > bScore) { + bScore = p4; + bPoint = 0x08; + bIsBiggerSide = 0; + } else if (aScore < bScore && p4 > aScore) { + aScore = p4; + aPoint = 0x08; + aIsBiggerSide = 0; + } + + /* Where each of the two closest points are determines how the extra three vertices are calculated. */ + if (aIsBiggerSide == bIsBiggerSide) { + if (aIsBiggerSide) { /* Both closest points on the bigger side */ + c1 = (int8_t)(aPoint | bPoint); + c2 = (int8_t)(aPoint & bPoint); + if ((c1 & 0x01) == 0) { + xsv_ext0 = xsb; + xsv_ext1 = xsb - 1; + dx_ext0 = dx0 - 3 * SQUISH_CONSTANT_4D; + dx_ext1 = dx0 + 1 - 2 * SQUISH_CONSTANT_4D; + } else { + xsv_ext0 = xsv_ext1 = xsb + 1; + dx_ext0 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D; + dx_ext1 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; + } + + if ((c1 & 0x02) == 0) { + ysv_ext0 = ysb; + ysv_ext1 = ysb - 1; + dy_ext0 = dy0 - 3 * SQUISH_CONSTANT_4D; + dy_ext1 = dy0 + 1 - 2 * SQUISH_CONSTANT_4D; + } else { + ysv_ext0 = ysv_ext1 = ysb + 1; + dy_ext0 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D; + dy_ext1 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; + } + + if ((c1 & 0x04) == 0) { + zsv_ext0 = zsb; + zsv_ext1 = zsb - 1; + dz_ext0 = dz0 - 3 * SQUISH_CONSTANT_4D; + dz_ext1 = dz0 + 1 - 2 * SQUISH_CONSTANT_4D; + } else { + zsv_ext0 = zsv_ext1 = zsb + 1; + dz_ext0 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D; + dz_ext1 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; + } + + if ((c1 & 0x08) == 0) { + wsv_ext0 = wsb; + wsv_ext1 = wsb - 1; + dw_ext0 = dw0 - 3 * SQUISH_CONSTANT_4D; + dw_ext1 = dw0 + 1 - 2 * SQUISH_CONSTANT_4D; + } else { + wsv_ext0 = wsv_ext1 = wsb + 1; + dw_ext0 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D; + dw_ext1 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; + } + + /* One combination is a permutation of (0,0,0,2) based on c2 */ + xsv_ext2 = xsb; + ysv_ext2 = ysb; + zsv_ext2 = zsb; + wsv_ext2 = wsb; + dx_ext2 = dx0 - 2 * SQUISH_CONSTANT_4D; + dy_ext2 = dy0 - 2 * SQUISH_CONSTANT_4D; + dz_ext2 = dz0 - 2 * SQUISH_CONSTANT_4D; + dw_ext2 = dw0 - 2 * SQUISH_CONSTANT_4D; + if ((c2 & 0x01) != 0) { + xsv_ext2 += 2; + dx_ext2 -= 2; + } else if ((c2 & 0x02) != 0) { + ysv_ext2 += 2; + dy_ext2 -= 2; + } else if ((c2 & 0x04) != 0) { + zsv_ext2 += 2; + dz_ext2 -= 2; + } else { + wsv_ext2 += 2; + dw_ext2 -= 2; + } + + } else { /* Both closest points on the smaller side */ + /* One of the two extra points is (0,0,0,0) */ + xsv_ext2 = xsb; + ysv_ext2 = ysb; + zsv_ext2 = zsb; + wsv_ext2 = wsb; + dx_ext2 = dx0; + dy_ext2 = dy0; + dz_ext2 = dz0; + dw_ext2 = dw0; + + /* Other two points are based on the omitted axes. */ + c = (int8_t)(aPoint | bPoint); + + if ((c & 0x01) == 0) { + xsv_ext0 = xsb - 1; + xsv_ext1 = xsb; + dx_ext0 = dx0 + 1 - SQUISH_CONSTANT_4D; + dx_ext1 = dx0 - SQUISH_CONSTANT_4D; + } else { + xsv_ext0 = xsv_ext1 = xsb + 1; + dx_ext0 = dx_ext1 = dx0 - 1 - SQUISH_CONSTANT_4D; + } + + if ((c & 0x02) == 0) { + ysv_ext0 = ysv_ext1 = ysb; + dy_ext0 = dy_ext1 = dy0 - SQUISH_CONSTANT_4D; + if ((c & 0x01) == 0x01) + { + ysv_ext0 -= 1; + dy_ext0 += 1; + } else { + ysv_ext1 -= 1; + dy_ext1 += 1; + } + } else { + ysv_ext0 = ysv_ext1 = ysb + 1; + dy_ext0 = dy_ext1 = dy0 - 1 - SQUISH_CONSTANT_4D; + } + + if ((c & 0x04) == 0) { + zsv_ext0 = zsv_ext1 = zsb; + dz_ext0 = dz_ext1 = dz0 - SQUISH_CONSTANT_4D; + if ((c & 0x03) == 0x03) + { + zsv_ext0 -= 1; + dz_ext0 += 1; + } else { + zsv_ext1 -= 1; + dz_ext1 += 1; + } + } else { + zsv_ext0 = zsv_ext1 = zsb + 1; + dz_ext0 = dz_ext1 = dz0 - 1 - SQUISH_CONSTANT_4D; + } + + if ((c & 0x08) == 0) + { + wsv_ext0 = wsb; + wsv_ext1 = wsb - 1; + dw_ext0 = dw0 - SQUISH_CONSTANT_4D; + dw_ext1 = dw0 + 1 - SQUISH_CONSTANT_4D; + } else { + wsv_ext0 = wsv_ext1 = wsb + 1; + dw_ext0 = dw_ext1 = dw0 - 1 - SQUISH_CONSTANT_4D; + } + + } + } else { /* One point on each "side" */ + if (aIsBiggerSide) { + c1 = aPoint; + c2 = bPoint; + } else { + c1 = bPoint; + c2 = aPoint; + } + + /* Two contributions are the bigger-sided point with each 0 replaced with -1. */ + if ((c1 & 0x01) == 0) { + xsv_ext0 = xsb - 1; + xsv_ext1 = xsb; + dx_ext0 = dx0 + 1 - SQUISH_CONSTANT_4D; + dx_ext1 = dx0 - SQUISH_CONSTANT_4D; + } else { + xsv_ext0 = xsv_ext1 = xsb + 1; + dx_ext0 = dx_ext1 = dx0 - 1 - SQUISH_CONSTANT_4D; + } + + if ((c1 & 0x02) == 0) { + ysv_ext0 = ysv_ext1 = ysb; + dy_ext0 = dy_ext1 = dy0 - SQUISH_CONSTANT_4D; + if ((c1 & 0x01) == 0x01) { + ysv_ext0 -= 1; + dy_ext0 += 1; + } else { + ysv_ext1 -= 1; + dy_ext1 += 1; + } + } else { + ysv_ext0 = ysv_ext1 = ysb + 1; + dy_ext0 = dy_ext1 = dy0 - 1 - SQUISH_CONSTANT_4D; + } + + if ((c1 & 0x04) == 0) { + zsv_ext0 = zsv_ext1 = zsb; + dz_ext0 = dz_ext1 = dz0 - SQUISH_CONSTANT_4D; + if ((c1 & 0x03) == 0x03) { + zsv_ext0 -= 1; + dz_ext0 += 1; + } else { + zsv_ext1 -= 1; + dz_ext1 += 1; + } + } else { + zsv_ext0 = zsv_ext1 = zsb + 1; + dz_ext0 = dz_ext1 = dz0 - 1 - SQUISH_CONSTANT_4D; + } + + if ((c1 & 0x08) == 0) { + wsv_ext0 = wsb; + wsv_ext1 = wsb - 1; + dw_ext0 = dw0 - SQUISH_CONSTANT_4D; + dw_ext1 = dw0 + 1 - SQUISH_CONSTANT_4D; + } else { + wsv_ext0 = wsv_ext1 = wsb + 1; + dw_ext0 = dw_ext1 = dw0 - 1 - SQUISH_CONSTANT_4D; + } + + /* One contribution is a permutation of (0,0,0,2) based on the smaller-sided point */ + xsv_ext2 = xsb; + ysv_ext2 = ysb; + zsv_ext2 = zsb; + wsv_ext2 = wsb; + dx_ext2 = dx0 - 2 * SQUISH_CONSTANT_4D; + dy_ext2 = dy0 - 2 * SQUISH_CONSTANT_4D; + dz_ext2 = dz0 - 2 * SQUISH_CONSTANT_4D; + dw_ext2 = dw0 - 2 * SQUISH_CONSTANT_4D; + if ((c2 & 0x01) != 0) { + xsv_ext2 += 2; + dx_ext2 -= 2; + } else if ((c2 & 0x02) != 0) { + ysv_ext2 += 2; + dy_ext2 -= 2; + } else if ((c2 & 0x04) != 0) { + zsv_ext2 += 2; + dz_ext2 -= 2; + } else { + wsv_ext2 += 2; + dw_ext2 -= 2; + } + } + + /* Contribution (1,0,0,0) */ + dx1 = dx0 - 1 - SQUISH_CONSTANT_4D; + dy1 = dy0 - 0 - SQUISH_CONSTANT_4D; + dz1 = dz0 - 0 - SQUISH_CONSTANT_4D; + dw1 = dw0 - 0 - SQUISH_CONSTANT_4D; + attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1 - dw1 * dw1; + if (attn1 > 0) { + attn1 *= attn1; + value += attn1 * attn1 * extrapolate4(ctx, xsb + 1, ysb + 0, zsb + 0, wsb + 0, dx1, dy1, dz1, dw1); + } + + /* Contribution (0,1,0,0) */ + dx2 = dx0 - 0 - SQUISH_CONSTANT_4D; + dy2 = dy0 - 1 - SQUISH_CONSTANT_4D; + dz2 = dz1; + dw2 = dw1; + attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2 - dw2 * dw2; + if (attn2 > 0) { + attn2 *= attn2; + value += attn2 * attn2 * extrapolate4(ctx, xsb + 0, ysb + 1, zsb + 0, wsb + 0, dx2, dy2, dz2, dw2); + } + + /* Contribution (0,0,1,0) */ + dx3 = dx2; + dy3 = dy1; + dz3 = dz0 - 1 - SQUISH_CONSTANT_4D; + dw3 = dw1; + attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3 - dw3 * dw3; + if (attn3 > 0) { + attn3 *= attn3; + value += attn3 * attn3 * extrapolate4(ctx, xsb + 0, ysb + 0, zsb + 1, wsb + 0, dx3, dy3, dz3, dw3); + } + + /* Contribution (0,0,0,1) */ + dx4 = dx2; + dy4 = dy1; + dz4 = dz1; + dw4 = dw0 - 1 - SQUISH_CONSTANT_4D; + attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4 - dw4 * dw4; + if (attn4 > 0) { + attn4 *= attn4; + value += attn4 * attn4 * extrapolate4(ctx, xsb + 0, ysb + 0, zsb + 0, wsb + 1, dx4, dy4, dz4, dw4); + } + + /* Contribution (1,1,0,0) */ + dx5 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; + dy5 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; + dz5 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D; + dw5 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D; + attn5 = 2 - dx5 * dx5 - dy5 * dy5 - dz5 * dz5 - dw5 * dw5; + if (attn5 > 0) { + attn5 *= attn5; + value += attn5 * attn5 * extrapolate4(ctx, xsb + 1, ysb + 1, zsb + 0, wsb + 0, dx5, dy5, dz5, dw5); + } + + /* Contribution (1,0,1,0) */ + dx6 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; + dy6 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D; + dz6 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; + dw6 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D; + attn6 = 2 - dx6 * dx6 - dy6 * dy6 - dz6 * dz6 - dw6 * dw6; + if (attn6 > 0) { + attn6 *= attn6; + value += attn6 * attn6 * extrapolate4(ctx, xsb + 1, ysb + 0, zsb + 1, wsb + 0, dx6, dy6, dz6, dw6); + } + + /* Contribution (1,0,0,1) */ + dx7 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; + dy7 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D; + dz7 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D; + dw7 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; + attn7 = 2 - dx7 * dx7 - dy7 * dy7 - dz7 * dz7 - dw7 * dw7; + if (attn7 > 0) { + attn7 *= attn7; + value += attn7 * attn7 * extrapolate4(ctx, xsb + 1, ysb + 0, zsb + 0, wsb + 1, dx7, dy7, dz7, dw7); + } + + /* Contribution (0,1,1,0) */ + dx8 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D; + dy8 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; + dz8 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; + dw8 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D; + attn8 = 2 - dx8 * dx8 - dy8 * dy8 - dz8 * dz8 - dw8 * dw8; + if (attn8 > 0) { + attn8 *= attn8; + value += attn8 * attn8 * extrapolate4(ctx, xsb + 0, ysb + 1, zsb + 1, wsb + 0, dx8, dy8, dz8, dw8); + } + + /* Contribution (0,1,0,1) */ + dx9 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D; + dy9 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; + dz9 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D; + dw9 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; + attn9 = 2 - dx9 * dx9 - dy9 * dy9 - dz9 * dz9 - dw9 * dw9; + if (attn9 > 0) { + attn9 *= attn9; + value += attn9 * attn9 * extrapolate4(ctx, xsb + 0, ysb + 1, zsb + 0, wsb + 1, dx9, dy9, dz9, dw9); + } + + /* Contribution (0,0,1,1) */ + dx10 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D; + dy10 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D; + dz10 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; + dw10 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; + attn10 = 2 - dx10 * dx10 - dy10 * dy10 - dz10 * dz10 - dw10 * dw10; + if (attn10 > 0) { + attn10 *= attn10; + value += attn10 * attn10 * extrapolate4(ctx, xsb + 0, ysb + 0, zsb + 1, wsb + 1, dx10, dy10, dz10, dw10); + } + } else { /* We're inside the second dispentachoron (Rectified 4-Simplex) */ + aIsBiggerSide = 1; + bIsBiggerSide = 1; + + /* Decide between (0,0,1,1) and (1,1,0,0) */ + if (xins + yins < zins + wins) { + aScore = xins + yins; + aPoint = 0x0C; + } else { + aScore = zins + wins; + aPoint = 0x03; + } + + /* Decide between (0,1,0,1) and (1,0,1,0) */ + if (xins + zins < yins + wins) { + bScore = xins + zins; + bPoint = 0x0A; + } else { + bScore = yins + wins; + bPoint = 0x05; + } + + /* Closer between (0,1,1,0) and (1,0,0,1) will replace the further of a and b, if closer. */ + if (xins + wins < yins + zins) { + score = xins + wins; + if (aScore <= bScore && score < bScore) { + bScore = score; + bPoint = 0x06; + } else if (aScore > bScore && score < aScore) { + aScore = score; + aPoint = 0x06; + } + } else { + score = yins + zins; + if (aScore <= bScore && score < bScore) { + bScore = score; + bPoint = 0x09; + } else if (aScore > bScore && score < aScore) { + aScore = score; + aPoint = 0x09; + } + } + + /* Decide if (0,1,1,1) is closer. */ + p1 = 3 - inSum + xins; + if (aScore <= bScore && p1 < bScore) { + bScore = p1; + bPoint = 0x0E; + bIsBiggerSide = 0; + } else if (aScore > bScore && p1 < aScore) { + aScore = p1; + aPoint = 0x0E; + aIsBiggerSide = 0; + } + + /* Decide if (1,0,1,1) is closer. */ + p2 = 3 - inSum + yins; + if (aScore <= bScore && p2 < bScore) { + bScore = p2; + bPoint = 0x0D; + bIsBiggerSide = 0; + } else if (aScore > bScore && p2 < aScore) { + aScore = p2; + aPoint = 0x0D; + aIsBiggerSide = 0; + } + + /* Decide if (1,1,0,1) is closer. */ + p3 = 3 - inSum + zins; + if (aScore <= bScore && p3 < bScore) { + bScore = p3; + bPoint = 0x0B; + bIsBiggerSide = 0; + } else if (aScore > bScore && p3 < aScore) { + aScore = p3; + aPoint = 0x0B; + aIsBiggerSide = 0; + } + + /* Decide if (1,1,1,0) is closer. */ + p4 = 3 - inSum + wins; + if (aScore <= bScore && p4 < bScore) { + bScore = p4; + bPoint = 0x07; + bIsBiggerSide = 0; + } else if (aScore > bScore && p4 < aScore) { + aScore = p4; + aPoint = 0x07; + aIsBiggerSide = 0; + } + + /* Where each of the two closest points are determines how the extra three vertices are calculated. */ + if (aIsBiggerSide == bIsBiggerSide) { + if (aIsBiggerSide) { /* Both closest points on the bigger side */ + c1 = (int8_t)(aPoint & bPoint); + c2 = (int8_t)(aPoint | bPoint); + + /* Two contributions are permutations of (0,0,0,1) and (0,0,0,2) based on c1 */ + xsv_ext0 = xsv_ext1 = xsb; + ysv_ext0 = ysv_ext1 = ysb; + zsv_ext0 = zsv_ext1 = zsb; + wsv_ext0 = wsv_ext1 = wsb; + dx_ext0 = dx0 - SQUISH_CONSTANT_4D; + dy_ext0 = dy0 - SQUISH_CONSTANT_4D; + dz_ext0 = dz0 - SQUISH_CONSTANT_4D; + dw_ext0 = dw0 - SQUISH_CONSTANT_4D; + dx_ext1 = dx0 - 2 * SQUISH_CONSTANT_4D; + dy_ext1 = dy0 - 2 * SQUISH_CONSTANT_4D; + dz_ext1 = dz0 - 2 * SQUISH_CONSTANT_4D; + dw_ext1 = dw0 - 2 * SQUISH_CONSTANT_4D; + if ((c1 & 0x01) != 0) { + xsv_ext0 += 1; + dx_ext0 -= 1; + xsv_ext1 += 2; + dx_ext1 -= 2; + } else if ((c1 & 0x02) != 0) { + ysv_ext0 += 1; + dy_ext0 -= 1; + ysv_ext1 += 2; + dy_ext1 -= 2; + } else if ((c1 & 0x04) != 0) { + zsv_ext0 += 1; + dz_ext0 -= 1; + zsv_ext1 += 2; + dz_ext1 -= 2; + } else { + wsv_ext0 += 1; + dw_ext0 -= 1; + wsv_ext1 += 2; + dw_ext1 -= 2; + } + + /* One contribution is a permutation of (1,1,1,-1) based on c2 */ + xsv_ext2 = xsb + 1; + ysv_ext2 = ysb + 1; + zsv_ext2 = zsb + 1; + wsv_ext2 = wsb + 1; + dx_ext2 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; + dy_ext2 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; + dz_ext2 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; + dw_ext2 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; + if ((c2 & 0x01) == 0) { + xsv_ext2 -= 2; + dx_ext2 += 2; + } else if ((c2 & 0x02) == 0) { + ysv_ext2 -= 2; + dy_ext2 += 2; + } else if ((c2 & 0x04) == 0) { + zsv_ext2 -= 2; + dz_ext2 += 2; + } else { + wsv_ext2 -= 2; + dw_ext2 += 2; + } + } else { /* Both closest points on the smaller side */ + /* One of the two extra points is (1,1,1,1) */ + xsv_ext2 = xsb + 1; + ysv_ext2 = ysb + 1; + zsv_ext2 = zsb + 1; + wsv_ext2 = wsb + 1; + dx_ext2 = dx0 - 1 - 4 * SQUISH_CONSTANT_4D; + dy_ext2 = dy0 - 1 - 4 * SQUISH_CONSTANT_4D; + dz_ext2 = dz0 - 1 - 4 * SQUISH_CONSTANT_4D; + dw_ext2 = dw0 - 1 - 4 * SQUISH_CONSTANT_4D; + + /* Other two points are based on the shared axes. */ + c = (int8_t)(aPoint & bPoint); + + if ((c & 0x01) != 0) { + xsv_ext0 = xsb + 2; + xsv_ext1 = xsb + 1; + dx_ext0 = dx0 - 2 - 3 * SQUISH_CONSTANT_4D; + dx_ext1 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D; + } else { + xsv_ext0 = xsv_ext1 = xsb; + dx_ext0 = dx_ext1 = dx0 - 3 * SQUISH_CONSTANT_4D; + } + + if ((c & 0x02) != 0) { + ysv_ext0 = ysv_ext1 = ysb + 1; + dy_ext0 = dy_ext1 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D; + if ((c & 0x01) == 0) + { + ysv_ext0 += 1; + dy_ext0 -= 1; + } else { + ysv_ext1 += 1; + dy_ext1 -= 1; + } + } else { + ysv_ext0 = ysv_ext1 = ysb; + dy_ext0 = dy_ext1 = dy0 - 3 * SQUISH_CONSTANT_4D; + } + + if ((c & 0x04) != 0) { + zsv_ext0 = zsv_ext1 = zsb + 1; + dz_ext0 = dz_ext1 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D; + if ((c & 0x03) == 0) + { + zsv_ext0 += 1; + dz_ext0 -= 1; + } else { + zsv_ext1 += 1; + dz_ext1 -= 1; + } + } else { + zsv_ext0 = zsv_ext1 = zsb; + dz_ext0 = dz_ext1 = dz0 - 3 * SQUISH_CONSTANT_4D; + } + + if ((c & 0x08) != 0) + { + wsv_ext0 = wsb + 1; + wsv_ext1 = wsb + 2; + dw_ext0 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D; + dw_ext1 = dw0 - 2 - 3 * SQUISH_CONSTANT_4D; + } else { + wsv_ext0 = wsv_ext1 = wsb; + dw_ext0 = dw_ext1 = dw0 - 3 * SQUISH_CONSTANT_4D; + } + } + } else { /* One point on each "side" */ + if (aIsBiggerSide) { + c1 = aPoint; + c2 = bPoint; + } else { + c1 = bPoint; + c2 = aPoint; + } + + /* Two contributions are the bigger-sided point with each 1 replaced with 2. */ + if ((c1 & 0x01) != 0) { + xsv_ext0 = xsb + 2; + xsv_ext1 = xsb + 1; + dx_ext0 = dx0 - 2 - 3 * SQUISH_CONSTANT_4D; + dx_ext1 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D; + } else { + xsv_ext0 = xsv_ext1 = xsb; + dx_ext0 = dx_ext1 = dx0 - 3 * SQUISH_CONSTANT_4D; + } + + if ((c1 & 0x02) != 0) { + ysv_ext0 = ysv_ext1 = ysb + 1; + dy_ext0 = dy_ext1 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D; + if ((c1 & 0x01) == 0) { + ysv_ext0 += 1; + dy_ext0 -= 1; + } else { + ysv_ext1 += 1; + dy_ext1 -= 1; + } + } else { + ysv_ext0 = ysv_ext1 = ysb; + dy_ext0 = dy_ext1 = dy0 - 3 * SQUISH_CONSTANT_4D; + } + + if ((c1 & 0x04) != 0) { + zsv_ext0 = zsv_ext1 = zsb + 1; + dz_ext0 = dz_ext1 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D; + if ((c1 & 0x03) == 0) { + zsv_ext0 += 1; + dz_ext0 -= 1; + } else { + zsv_ext1 += 1; + dz_ext1 -= 1; + } + } else { + zsv_ext0 = zsv_ext1 = zsb; + dz_ext0 = dz_ext1 = dz0 - 3 * SQUISH_CONSTANT_4D; + } + + if ((c1 & 0x08) != 0) { + wsv_ext0 = wsb + 1; + wsv_ext1 = wsb + 2; + dw_ext0 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D; + dw_ext1 = dw0 - 2 - 3 * SQUISH_CONSTANT_4D; + } else { + wsv_ext0 = wsv_ext1 = wsb; + dw_ext0 = dw_ext1 = dw0 - 3 * SQUISH_CONSTANT_4D; + } + + /* One contribution is a permutation of (1,1,1,-1) based on the smaller-sided point */ + xsv_ext2 = xsb + 1; + ysv_ext2 = ysb + 1; + zsv_ext2 = zsb + 1; + wsv_ext2 = wsb + 1; + dx_ext2 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; + dy_ext2 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; + dz_ext2 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; + dw_ext2 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; + if ((c2 & 0x01) == 0) { + xsv_ext2 -= 2; + dx_ext2 += 2; + } else if ((c2 & 0x02) == 0) { + ysv_ext2 -= 2; + dy_ext2 += 2; + } else if ((c2 & 0x04) == 0) { + zsv_ext2 -= 2; + dz_ext2 += 2; + } else { + wsv_ext2 -= 2; + dw_ext2 += 2; + } + } + + /* Contribution (1,1,1,0) */ + dx4 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D; + dy4 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D; + dz4 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D; + dw4 = dw0 - 3 * SQUISH_CONSTANT_4D; + attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4 - dw4 * dw4; + if (attn4 > 0) { + attn4 *= attn4; + value += attn4 * attn4 * extrapolate4(ctx, xsb + 1, ysb + 1, zsb + 1, wsb + 0, dx4, dy4, dz4, dw4); + } + + /* Contribution (1,1,0,1) */ + dx3 = dx4; + dy3 = dy4; + dz3 = dz0 - 3 * SQUISH_CONSTANT_4D; + dw3 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D; + attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3 - dw3 * dw3; + if (attn3 > 0) { + attn3 *= attn3; + value += attn3 * attn3 * extrapolate4(ctx, xsb + 1, ysb + 1, zsb + 0, wsb + 1, dx3, dy3, dz3, dw3); + } + + /* Contribution (1,0,1,1) */ + dx2 = dx4; + dy2 = dy0 - 3 * SQUISH_CONSTANT_4D; + dz2 = dz4; + dw2 = dw3; + attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2 - dw2 * dw2; + if (attn2 > 0) { + attn2 *= attn2; + value += attn2 * attn2 * extrapolate4(ctx, xsb + 1, ysb + 0, zsb + 1, wsb + 1, dx2, dy2, dz2, dw2); + } + + /* Contribution (0,1,1,1) */ + dx1 = dx0 - 3 * SQUISH_CONSTANT_4D; + dz1 = dz4; + dy1 = dy4; + dw1 = dw3; + attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1 - dw1 * dw1; + if (attn1 > 0) { + attn1 *= attn1; + value += attn1 * attn1 * extrapolate4(ctx, xsb + 0, ysb + 1, zsb + 1, wsb + 1, dx1, dy1, dz1, dw1); + } + + /* Contribution (1,1,0,0) */ + dx5 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; + dy5 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; + dz5 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D; + dw5 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D; + attn5 = 2 - dx5 * dx5 - dy5 * dy5 - dz5 * dz5 - dw5 * dw5; + if (attn5 > 0) { + attn5 *= attn5; + value += attn5 * attn5 * extrapolate4(ctx, xsb + 1, ysb + 1, zsb + 0, wsb + 0, dx5, dy5, dz5, dw5); + } + + /* Contribution (1,0,1,0) */ + dx6 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; + dy6 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D; + dz6 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; + dw6 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D; + attn6 = 2 - dx6 * dx6 - dy6 * dy6 - dz6 * dz6 - dw6 * dw6; + if (attn6 > 0) { + attn6 *= attn6; + value += attn6 * attn6 * extrapolate4(ctx, xsb + 1, ysb + 0, zsb + 1, wsb + 0, dx6, dy6, dz6, dw6); + } + + /* Contribution (1,0,0,1) */ + dx7 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; + dy7 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D; + dz7 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D; + dw7 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; + attn7 = 2 - dx7 * dx7 - dy7 * dy7 - dz7 * dz7 - dw7 * dw7; + if (attn7 > 0) { + attn7 *= attn7; + value += attn7 * attn7 * extrapolate4(ctx, xsb + 1, ysb + 0, zsb + 0, wsb + 1, dx7, dy7, dz7, dw7); + } + + /* Contribution (0,1,1,0) */ + dx8 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D; + dy8 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; + dz8 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; + dw8 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D; + attn8 = 2 - dx8 * dx8 - dy8 * dy8 - dz8 * dz8 - dw8 * dw8; + if (attn8 > 0) { + attn8 *= attn8; + value += attn8 * attn8 * extrapolate4(ctx, xsb + 0, ysb + 1, zsb + 1, wsb + 0, dx8, dy8, dz8, dw8); + } + + /* Contribution (0,1,0,1) */ + dx9 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D; + dy9 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; + dz9 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D; + dw9 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; + attn9 = 2 - dx9 * dx9 - dy9 * dy9 - dz9 * dz9 - dw9 * dw9; + if (attn9 > 0) { + attn9 *= attn9; + value += attn9 * attn9 * extrapolate4(ctx, xsb + 0, ysb + 1, zsb + 0, wsb + 1, dx9, dy9, dz9, dw9); + } + + /* Contribution (0,0,1,1) */ + dx10 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D; + dy10 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D; + dz10 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; + dw10 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; + attn10 = 2 - dx10 * dx10 - dy10 * dy10 - dz10 * dz10 - dw10 * dw10; + if (attn10 > 0) { + attn10 *= attn10; + value += attn10 * attn10 * extrapolate4(ctx, xsb + 0, ysb + 0, zsb + 1, wsb + 1, dx10, dy10, dz10, dw10); + } + } + + /* First extra vertex */ + attn_ext0 = 2 - dx_ext0 * dx_ext0 - dy_ext0 * dy_ext0 - dz_ext0 * dz_ext0 - dw_ext0 * dw_ext0; + if (attn_ext0 > 0) + { + attn_ext0 *= attn_ext0; + value += attn_ext0 * attn_ext0 * extrapolate4(ctx, xsv_ext0, ysv_ext0, zsv_ext0, wsv_ext0, dx_ext0, dy_ext0, dz_ext0, dw_ext0); + } + + /* Second extra vertex */ + attn_ext1 = 2 - dx_ext1 * dx_ext1 - dy_ext1 * dy_ext1 - dz_ext1 * dz_ext1 - dw_ext1 * dw_ext1; + if (attn_ext1 > 0) + { + attn_ext1 *= attn_ext1; + value += attn_ext1 * attn_ext1 * extrapolate4(ctx, xsv_ext1, ysv_ext1, zsv_ext1, wsv_ext1, dx_ext1, dy_ext1, dz_ext1, dw_ext1); + } + + /* Third extra vertex */ + attn_ext2 = 2 - dx_ext2 * dx_ext2 - dy_ext2 * dy_ext2 - dz_ext2 * dz_ext2 - dw_ext2 * dw_ext2; + if (attn_ext2 > 0) + { + attn_ext2 *= attn_ext2; + value += attn_ext2 * attn_ext2 * extrapolate4(ctx, xsv_ext2, ysv_ext2, zsv_ext2, wsv_ext2, dx_ext2, dy_ext2, dz_ext2, dw_ext2); + } + + return value / NORM_CONSTANT_4D; +} + diff --git a/thirdparty/misc/open-simplex-noise.h b/thirdparty/misc/open-simplex-noise.h new file mode 100644 index 0000000000..89e0df8218 --- /dev/null +++ b/thirdparty/misc/open-simplex-noise.h @@ -0,0 +1,58 @@ +#ifndef OPEN_SIMPLEX_NOISE_H__ +#define OPEN_SIMPLEX_NOISE_H__ + +/* + * OpenSimplex (Simplectic) Noise in C. + * Ported to C from Kurt Spencer's java implementation by Stephen M. Cameron + * + * v1.1 (October 6, 2014) + * - Ported to C + * + * v1.1 (October 5, 2014) + * - Added 2D and 4D implementations. + * - Proper gradient sets for all dimensions, from a + * dimensionally-generalizable scheme with an actual + * rhyme and reason behind it. + * - Removed default permutation array in favor of + * default seed. + * - Changed seed-based constructor to be independent + * of any particular randomization library, so results + * will be the same when ported to other languages. + */ + +#if ((__GNUC_STDC_INLINE__) || (__STDC_VERSION__ >= 199901L)) + #include <stdint.h> + #define INLINE inline +#elif (defined (_MSC_VER) || defined (__GNUC_GNU_INLINE__)) + #include <stdint.h> + #define INLINE __inline +#else + /* ANSI C doesn't have inline or stdint.h. */ + #define INLINE +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +// -- GODOT start -- +// Modified to work without allocating memory, also removed some unused function. + +struct osn_context { + int16_t perm[256]; + int16_t permGradIndex3D[256]; +}; + +int open_simplex_noise(int64_t seed, struct osn_context *ctx); +//int open_simplex_noise_init_perm(struct osn_context *ctx, int16_t p[], int nelements); +// -- GODOT end -- +void open_simplex_noise_free(struct osn_context *ctx); +double open_simplex_noise2(struct osn_context *ctx, double x, double y); +double open_simplex_noise3(struct osn_context *ctx, double x, double y, double z); +double open_simplex_noise4(struct osn_context *ctx, double x, double y, double z, double w); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/thirdparty/misc/pcg.h b/thirdparty/misc/pcg.h index 81f4c9770e..e2d66d51d5 100644 --- a/thirdparty/misc/pcg.h +++ b/thirdparty/misc/pcg.h @@ -4,7 +4,7 @@ #ifndef RANDOM_H #define RANDOM_H -#include "typedefs.h" +#include "core/typedefs.h" #define PCG_DEFAULT_INC_64 1442695040888963407ULL diff --git a/thirdparty/misc/stb_vorbis.h b/thirdparty/misc/stb_vorbis.h new file mode 100644 index 0000000000..357efcd5fc --- /dev/null +++ b/thirdparty/misc/stb_vorbis.h @@ -0,0 +1,2 @@ +#define STB_VORBIS_HEADER_ONLY +#include "stb_vorbis.c" diff --git a/thirdparty/misc/triangulator.h b/thirdparty/misc/triangulator.h index d1538cfae5..c85792fd50 100644 --- a/thirdparty/misc/triangulator.h +++ b/thirdparty/misc/triangulator.h @@ -21,11 +21,11 @@ #ifndef TRIANGULATOR_H #define TRIANGULATOR_H -#include "list.h" -#include "set.h" -#include "vector2.h" -//2D point structure +#include "core/list.h" +#include "core/math/vector2.h" +#include "core/set.h" +//2D point structure #define TRIANGULATOR_CCW 1 #define TRIANGULATOR_CW -1 diff --git a/thirdparty/misc/yuv2rgb.h b/thirdparty/misc/yuv2rgb.h index d0c2813a75..3ec8388246 100644 --- a/thirdparty/misc/yuv2rgb.h +++ b/thirdparty/misc/yuv2rgb.h @@ -27,7 +27,7 @@ ship it. #ifndef YUV2RGB_H #define YUV2RGB_H -#include "typedefs.h" +#include "core/typedefs.h" static const uint32_t tables[256*3] = { /* y_table */ diff --git a/thirdparty/pvrtccompressor/BitScale.h b/thirdparty/pvrtccompressor/BitScale.h index 36613aeeee..3ea7962f55 100644 --- a/thirdparty/pvrtccompressor/BitScale.h +++ b/thirdparty/pvrtccompressor/BitScale.h @@ -2,7 +2,7 @@ #pragma once -#include "typedefs.h" +#include "core/typedefs.h" //============================================================================ diff --git a/thirdparty/tinyexr/tinyexr.h b/thirdparty/tinyexr/tinyexr.h index 107c22ffb3..990c8ee142 100644 --- a/thirdparty/tinyexr/tinyexr.h +++ b/thirdparty/tinyexr/tinyexr.h @@ -1,5 +1,5 @@ /* -Copyright (c) 2014 - 2017, Syoyo Fujita +Copyright (c) 2014 - 2018, Syoyo Fujita and many contributors. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -115,6 +115,7 @@ extern "C" { #define TINYEXR_ERROR_CANT_OPEN_FILE (-6) #define TINYEXR_ERROR_UNSUPPORTED_FORMAT (-7) #define TINYEXR_ERROR_INVALID_HEADER (-8) +#define TINYEXR_ERROR_UNSUPPORTED_FEATURE (-9) // @note { OpenEXR file format: http://www.openexr.com/openexrfilelayout.pdf } @@ -123,7 +124,8 @@ extern "C" { #define TINYEXR_PIXELTYPE_HALF (1) #define TINYEXR_PIXELTYPE_FLOAT (2) -#define TINYEXR_MAX_ATTRIBUTES (128) +#define TINYEXR_MAX_HEADER_ATTRIBUTES (1024) +#define TINYEXR_MAX_CUSTOM_ATTRIBUTES (128) #define TINYEXR_COMPRESSIONTYPE_NONE (0) #define TINYEXR_COMPRESSIONTYPE_RLE (1) @@ -205,7 +207,8 @@ typedef struct _EXRHeader { // Custom attributes(exludes required attributes(e.g. `channels`, // `compression`, etc) int num_custom_attributes; - EXRAttribute custom_attributes[TINYEXR_MAX_ATTRIBUTES]; + EXRAttribute *custom_attributes; // array of EXRAttribute. size = + // `num_custom_attributes`. EXRChannelInfo *channels; // [num_channels] @@ -292,6 +295,9 @@ extern int FreeEXRHeader(EXRHeader *exr_header); // Free's internal data of EXRImage struct extern int FreeEXRImage(EXRImage *exr_image); +// Free's error message +extern void FreeEXRErrorMessage(const char *msg); + // Parse EXR version header of a file. extern int ParseEXRVersionFromFile(EXRVersion *version, const char *filename); @@ -300,10 +306,14 @@ extern int ParseEXRVersionFromMemory(EXRVersion *version, const unsigned char *memory, size_t size); // Parse single-part OpenEXR header from a file and initialize `EXRHeader`. +// When there was an error message, Application must free `err` with +// FreeEXRErrorMessage() extern int ParseEXRHeaderFromFile(EXRHeader *header, const EXRVersion *version, const char *filename, const char **err); // Parse single-part OpenEXR header from a memory and initialize `EXRHeader`. +// When there was an error message, Application must free `err` with +// FreeEXRErrorMessage() extern int ParseEXRHeaderFromMemory(EXRHeader *header, const EXRVersion *version, const unsigned char *memory, size_t size, @@ -311,6 +321,8 @@ extern int ParseEXRHeaderFromMemory(EXRHeader *header, // Parse multi-part OpenEXR headers from a file and initialize `EXRHeader*` // array. +// When there was an error message, Application must free `err` with +// FreeEXRErrorMessage() extern int ParseEXRMultipartHeaderFromFile(EXRHeader ***headers, int *num_headers, const EXRVersion *version, @@ -319,6 +331,8 @@ extern int ParseEXRMultipartHeaderFromFile(EXRHeader ***headers, // Parse multi-part OpenEXR headers from a memory and initialize `EXRHeader*` // array +// When there was an error message, Application must free `err` with +// FreeEXRErrorMessage() extern int ParseEXRMultipartHeaderFromMemory(EXRHeader ***headers, int *num_headers, const EXRVersion *version, @@ -330,6 +344,8 @@ extern int ParseEXRMultipartHeaderFromMemory(EXRHeader ***headers, // Application can free EXRImage using `FreeEXRImage` // Returns negative value and may set error string in `err` when there's an // error +// When there was an error message, Application must free `err` with +// FreeEXRErrorMessage() extern int LoadEXRImageFromFile(EXRImage *image, const EXRHeader *header, const char *filename, const char **err); @@ -339,6 +355,8 @@ extern int LoadEXRImageFromFile(EXRImage *image, const EXRHeader *header, // Application can free EXRImage using `FreeEXRImage` // Returns negative value and may set error string in `err` when there's an // error +// When there was an error message, Application must free `err` with +// FreeEXRErrorMessage() extern int LoadEXRImageFromMemory(EXRImage *image, const EXRHeader *header, const unsigned char *memory, const size_t size, const char **err); @@ -349,6 +367,8 @@ extern int LoadEXRImageFromMemory(EXRImage *image, const EXRHeader *header, // Application can free EXRImage using `FreeEXRImage` // Returns negative value and may set error string in `err` when there's an // error +// When there was an error message, Application must free `err` with +// FreeEXRErrorMessage() extern int LoadEXRMultipartImageFromFile(EXRImage *images, const EXRHeader **headers, unsigned int num_parts, @@ -361,6 +381,8 @@ extern int LoadEXRMultipartImageFromFile(EXRImage *images, // Application can free EXRImage using `FreeEXRImage` // Returns negative value and may set error string in `err` when there's an // error +// When there was an error message, Application must free `err` with +// FreeEXRErrorMessage() extern int LoadEXRMultipartImageFromMemory(EXRImage *images, const EXRHeader **headers, unsigned int num_parts, @@ -370,6 +392,8 @@ extern int LoadEXRMultipartImageFromMemory(EXRImage *images, // Saves multi-channel, single-frame OpenEXR image to a file. // Returns negative value and may set error string in `err` when there's an // error +// When there was an error message, Application must free `err` with +// FreeEXRErrorMessage() extern int SaveEXRImageToFile(const EXRImage *image, const EXRHeader *exr_header, const char *filename, const char **err); @@ -379,6 +403,8 @@ extern int SaveEXRImageToFile(const EXRImage *image, // Return the number of bytes if succes. // Returns negative value and may set error string in `err` when there's an // error +// When there was an error message, Application must free `err` with +// FreeEXRErrorMessage() extern size_t SaveEXRImageToMemory(const EXRImage *image, const EXRHeader *exr_header, unsigned char **memory, const char **err); @@ -387,6 +413,8 @@ extern size_t SaveEXRImageToMemory(const EXRImage *image, // Application must free memory of variables in DeepImage(image, offset_table) // Returns negative value and may set error string in `err` when there's an // error +// When there was an error message, Application must free `err` with +// FreeEXRErrorMessage() extern int LoadDeepEXR(DeepImage *out_image, const char *filename, const char **err); @@ -409,6 +437,8 @@ extern int LoadDeepEXR(DeepImage *out_image, const char *filename, // RGB(A) channels. // Returns negative value and may set error string in `err` when there's an // error +// When there was an error message, Application must free `err` with +// FreeEXRErrorMessage() extern int LoadEXRFromMemory(float **out_rgba, int *width, int *height, const unsigned char *memory, size_t size, const char **err); @@ -428,8 +458,10 @@ extern int LoadEXRFromMemory(float **out_rgba, int *width, int *height, #include <cstdio> #include <cstdlib> #include <cstring> +#include <iostream> #include <sstream> +#include <limits> #include <string> #include <vector> @@ -486,6 +518,9 @@ namespace miniz { #pragma clang diagnostic ignored "-Wc++11-extensions" #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wunused-function" +#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#pragma clang diagnostic ignored "-Wundef" + #if __has_warning("-Wcomma") #pragma clang diagnostic ignored "-Wcomma" #endif @@ -495,6 +530,9 @@ namespace miniz { #if __has_warning("-Wcast-qual") #pragma clang diagnostic ignored "-Wcast-qual" #endif +#if __has_warning("-Wzero-as-null-pointer-constant") +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" +#endif #endif /* miniz.c v1.15 - public domain deflate/inflate, zlib-subset, ZIP @@ -2480,10 +2518,10 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf; - const mz_uint8 *pIn_buf_cur = pIn_buf_next, - *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; - mz_uint8 *pOut_buf_cur = pOut_buf_next, - *const pOut_buf_end = pOut_buf_next + *pOut_buf_size; + const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = + pIn_buf_next + *pIn_buf_size; + mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = + pOut_buf_next + *pOut_buf_size; size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 @@ -2955,9 +2993,8 @@ int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, - (flags & - ~(TINFL_FLAG_HAS_MORE_INPUT | - TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); + (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | + TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); in_buf_ofs += in_buf_size; if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) @@ -3082,7 +3119,9 @@ static const mz_uint8 s_tdefl_large_dist_extra[128] = { // Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted // values. -typedef struct { mz_uint16 m_key, m_sym_index; } tdefl_sym_freq; +typedef struct { + mz_uint16 m_key, m_sym_index; +} tdefl_sym_freq; static tdefl_sym_freq *tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq *pSyms0, tdefl_sym_freq *pSyms1) { @@ -3549,10 +3588,9 @@ static int tdefl_flush_block(tdefl_compressor *d, int flush) { mz_uint saved_bit_buf, saved_bits_in; mz_uint8 *pSaved_output_buf; mz_bool comp_block_succeeded = MZ_FALSE; - int n, - use_raw_block = - ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && - (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; + int n, use_raw_block = + ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && + (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) @@ -3582,9 +3620,8 @@ static int tdefl_flush_block(tdefl_compressor *d, int flush) { if (!use_raw_block) comp_block_succeeded = - tdefl_compress_block(d, - (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || - (d->m_total_lz_bytes < 48)); + tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || + (d->m_total_lz_bytes < 48)); // If the block gets expanded, forget the current contents of the output // buffer and send a raw block instead. @@ -4388,9 +4425,8 @@ mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, // C and C99, so no big deal) #pragma warning(disable : 4244) // 'initializing': conversion from '__int64' to // 'int', possible loss of data -#pragma warning( \ - disable : 4267) // 'argument': conversion from '__int64' to 'int', - // possible loss of data +#pragma warning(disable : 4267) // 'argument': conversion from '__int64' to + // 'int', possible loss of data #pragma warning(disable : 4996) // 'strdup': The POSIX name for this item is // deprecated. Instead, use the ISO C and C++ // conformant name: _strdup. @@ -6894,7 +6930,7 @@ void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, #ifdef _MSC_VER #pragma warning(pop) #endif -} +} // namespace miniz #else // Reuse MINIZ_LITTE_ENDIAN macro @@ -6919,8 +6955,26 @@ void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, // return bint.c[0] == 1; //} +static void SetErrorMessage(const std::string &msg, const char **err) { + if (err) { +#ifdef _WIN32 + (*err) = _strdup(msg.c_str()); +#else + (*err) = strdup(msg.c_str()); +#endif + } +} + static const int kEXRVersionSize = 8; +static void cpy2(unsigned short *dst_val, const unsigned short *src_val) { + unsigned char *dst = reinterpret_cast<unsigned char *>(dst_val); + const unsigned char *src = reinterpret_cast<const unsigned char *>(src_val); + + dst[0] = src[0]; + dst[1] = src[1]; +} + static void swap2(unsigned short *val) { #ifdef MINIZ_LITTLE_ENDIAN (void)val; @@ -6934,6 +6988,36 @@ static void swap2(unsigned short *val) { #endif } +static void cpy4(int *dst_val, const int *src_val) { + unsigned char *dst = reinterpret_cast<unsigned char *>(dst_val); + const unsigned char *src = reinterpret_cast<const unsigned char *>(src_val); + + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; +} + +static void cpy4(unsigned int *dst_val, const unsigned int *src_val) { + unsigned char *dst = reinterpret_cast<unsigned char *>(dst_val); + const unsigned char *src = reinterpret_cast<const unsigned char *>(src_val); + + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; +} + +static void cpy4(float *dst_val, const float *src_val) { + unsigned char *dst = reinterpret_cast<unsigned char *>(dst_val); + const unsigned char *src = reinterpret_cast<const unsigned char *>(src_val); + + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; +} + static void swap4(unsigned int *val) { #ifdef MINIZ_LITTLE_ENDIAN (void)val; @@ -6949,6 +7033,22 @@ static void swap4(unsigned int *val) { #endif } +#if 0 +static void cpy8(tinyexr::tinyexr_uint64 *dst_val, const tinyexr::tinyexr_uint64 *src_val) { + unsigned char *dst = reinterpret_cast<unsigned char *>(dst_val); + const unsigned char *src = reinterpret_cast<const unsigned char *>(src_val); + + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + dst[4] = src[4]; + dst[5] = src[5]; + dst[6] = src[6]; + dst[7] = src[7]; +} +#endif + static void swap8(tinyexr::tinyexr_uint64 *val) { #ifdef MINIZ_LITTLE_ENDIAN (void)val; @@ -7084,6 +7184,15 @@ static FP16 float_to_half_full(FP32 f) { // #define IMF_B44_COMPRESSION 6 // #define IMF_B44A_COMPRESSION 7 +#ifdef __clang__ +#pragma clang diagnostic push + +#if __has_warning("-Wzero-as-null-pointer-constant") +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" +#endif + +#endif + static const char *ReadString(std::string *s, const char *ptr, size_t len) { // Read untile NULL(\0). const char *p = ptr; @@ -7133,7 +7242,21 @@ static bool ReadAttribute(std::string *name, std::string *type, tinyexr::swap4(reinterpret_cast<unsigned int *>(&data_len)); if (data_len == 0) { - return false; + if ((*type).compare("string") == 0) { + // Accept empty string attribute. + + marker += sizeof(uint32_t); + size -= sizeof(uint32_t); + + *marker_size = name_len + 1 + type_len + 1 + sizeof(uint32_t); + + data->resize(1); + (*data)[0] = '\0'; + + return true; + } else { + return false; + } } marker += sizeof(uint32_t); @@ -7236,18 +7359,24 @@ static bool ReadChannelInfo(std::vector<ChannelInfo> &channels, } ChannelInfo info; - tinyexr_int64 data_len = static_cast<tinyexr_int64>(data.size()) - (p - reinterpret_cast<const char *>(data.data())); + tinyexr_int64 data_len = static_cast<tinyexr_int64>(data.size()) - + (p - reinterpret_cast<const char *>(data.data())); if (data_len < 0) { return false; } - p = ReadString( - &info.name, p, size_t(data_len)); + p = ReadString(&info.name, p, size_t(data_len)); if ((p == NULL) && (info.name.empty())) { // Buffer overrun. Issue #51. return false; } + const unsigned char *data_end = + reinterpret_cast<const unsigned char *>(p) + 16; + if (data_end >= (data.data() + data.size())) { + return false; + } + memcpy(&info.pixel_type, p, sizeof(int)); p += 4; info.p_linear = static_cast<unsigned char>(p[0]); // uchar @@ -7468,9 +7597,8 @@ static bool DecompressZip(unsigned char *dst, // C and C99, so no big deal) #pragma warning(disable : 4244) // 'initializing': conversion from '__int64' to // 'int', possible loss of data -#pragma warning( \ - disable : 4267) // 'argument': conversion from '__int64' to 'int', - // possible loss of data +#pragma warning(disable : 4267) // 'argument': conversion from '__int64' to + // 'int', possible loss of data #pragma warning(disable : 4996) // 'strdup': The POSIX name for this item is // deprecated. Instead, use the ISO C and C++ // conformant name: _strdup. @@ -7705,6 +7833,7 @@ static void DecompressRle(unsigned char *dst, #pragma clang diagnostic ignored "-Wsign-conversion" #pragma clang diagnostic ignored "-Wc++11-extensions" #pragma clang diagnostic ignored "-Wconversion" +#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" #if __has_warning("-Wcast-qual") #pragma clang diagnostic ignored "-Wcast-qual" @@ -8187,8 +8316,8 @@ static void hufBuildEncTable( // for all array entries. // - int hlink[HUF_ENCSIZE]; - long long *fHeap[HUF_ENCSIZE]; + std::vector<int> hlink(HUF_ENCSIZE); + std::vector<long long *> fHeap(HUF_ENCSIZE); *im = 0; @@ -8247,8 +8376,8 @@ static void hufBuildEncTable( std::make_heap(&fHeap[0], &fHeap[nf], FHeapCompare()); - long long scode[HUF_ENCSIZE]; - memset(scode, 0, sizeof(long long) * HUF_ENCSIZE); + std::vector<long long> scode(HUF_ENCSIZE); + memset(scode.data(), 0, sizeof(long long) * HUF_ENCSIZE); while (nf > 1) { // @@ -8320,8 +8449,8 @@ static void hufBuildEncTable( // code table from scode into frq. // - hufCanonicalCodeTable(scode); - memcpy(frq, scode, sizeof(long long) * HUF_ENCSIZE); + hufCanonicalCodeTable(scode.data()); + memcpy(frq, scode.data(), sizeof(long long) * HUF_ENCSIZE); } // @@ -8657,26 +8786,62 @@ static int hufEncode // return: output size (in bits) lc += 8; \ } -#define getCode(po, rlc, c, lc, in, out, oe) \ - { \ - if (po == rlc) { \ - if (lc < 8) getChar(c, lc, in); \ - \ - lc -= 8; \ - \ - unsigned char cs = (c >> lc); \ - \ - if (out + cs > oe) return false; \ - \ - unsigned short s = out[-1]; \ - \ - while (cs-- > 0) *out++ = s; \ - } else if (out < oe) { \ - *out++ = po; \ - } else { \ - return false; \ - } \ +#if 0 +#define getCode(po, rlc, c, lc, in, out, ob, oe) \ + { \ + if (po == rlc) { \ + if (lc < 8) getChar(c, lc, in); \ + \ + lc -= 8; \ + \ + unsigned char cs = (c >> lc); \ + \ + if (out + cs > oe) return false; \ + \ + /* TinyEXR issue 78 */ \ + unsigned short s = out[-1]; \ + \ + while (cs-- > 0) *out++ = s; \ + } else if (out < oe) { \ + *out++ = po; \ + } else { \ + return false; \ + } \ + } +#else +static bool getCode(int po, int rlc, long long &c, int &lc, const char *&in, + const char *in_end, unsigned short *&out, + const unsigned short *ob, const unsigned short *oe) { + (void)ob; + if (po == rlc) { + if (lc < 8) { + /* TinyEXR issue 78 */ + if ((in + 1) >= in_end) { + return false; + } + + getChar(c, lc, in); + } + + lc -= 8; + + unsigned char cs = (c >> lc); + + if (out + cs > oe) return false; + + // Bounds check for safety + if ((out - 1) <= ob) return false; + unsigned short s = out[-1]; + + while (cs-- > 0) *out++ = s; + } else if (out < oe) { + *out++ = po; + } else { + return false; } + return true; +} +#endif // // Decode (uncompress) ni bits based on encoding & decoding tables: @@ -8692,8 +8857,8 @@ static bool hufDecode(const long long *hcode, // i : encoding table { long long c = 0; int lc = 0; - unsigned short *outb = out; - unsigned short *oe = out + no; + unsigned short *outb = out; // begin + unsigned short *oe = out + no; // end const char *ie = in + (ni + 7) / 8; // input byte size // @@ -8716,7 +8881,16 @@ static bool hufDecode(const long long *hcode, // i : encoding table // lc -= pl.len; - getCode(pl.lit, rlc, c, lc, in, out, oe); + // std::cout << "lit = " << pl.lit << std::endl; + // std::cout << "rlc = " << rlc << std::endl; + // std::cout << "c = " << c << std::endl; + // std::cout << "lc = " << lc << std::endl; + // std::cout << "in = " << in << std::endl; + // std::cout << "out = " << out << std::endl; + // std::cout << "oe = " << oe << std::endl; + if (!getCode(pl.lit, rlc, c, lc, in, ie, out, outb, oe)) { + return false; + } } else { if (!pl.p) { return false; @@ -8743,7 +8917,9 @@ static bool hufDecode(const long long *hcode, // i : encoding table // lc -= l; - getCode(pl.p[j], rlc, c, lc, in, out, oe); + if (!getCode(pl.p[j], rlc, c, lc, in, ie, out, outb, oe)) { + return false; + } break; } } @@ -8770,7 +8946,9 @@ static bool hufDecode(const long long *hcode, // i : encoding table if (pl.len) { lc -= pl.len; - getCode(pl.lit, rlc, c, lc, in, out, oe); + if (!getCode(pl.lit, rlc, c, lc, in, ie, out, outb, oe)) { + return false; + } } else { return false; // invalidCode(); // wrong (long) code @@ -8785,7 +8963,7 @@ static bool hufDecode(const long long *hcode, // i : encoding table return true; } -static void countFrequencies(long long freq[HUF_ENCSIZE], +static void countFrequencies(std::vector<long long> &freq, const unsigned short data[/*n*/], int n) { for (int i = 0; i < HUF_ENCSIZE; ++i) freq[i] = 0; @@ -8816,21 +8994,21 @@ static int hufCompress(const unsigned short raw[], int nRaw, char compressed[]) { if (nRaw == 0) return 0; - long long freq[HUF_ENCSIZE]; + std::vector<long long> freq(HUF_ENCSIZE); countFrequencies(freq, raw, nRaw); int im = 0; int iM = 0; - hufBuildEncTable(freq, &im, &iM); + hufBuildEncTable(freq.data(), &im, &iM); char *tableStart = compressed + 20; char *tableEnd = tableStart; - hufPackEncTable(freq, im, iM, &tableEnd); + hufPackEncTable(freq.data(), im, iM, &tableEnd); int tableLength = tableEnd - tableStart; char *dataStart = tableEnd; - int nBits = hufEncode(freq, raw, nRaw, iM, dataStart); + int nBits = hufEncode(freq.data(), raw, nRaw, iM, dataStart); int data_length = (nBits + 7) / 8; writeUInt(compressed, im); @@ -8843,9 +9021,9 @@ static int hufCompress(const unsigned short raw[], int nRaw, } static bool hufUncompress(const char compressed[], int nCompressed, - unsigned short raw[], int nRaw) { + std::vector<unsigned short> *raw) { if (nCompressed == 0) { - if (nRaw != 0) return false; + if (raw->size() != 0) return false; return false; } @@ -8886,7 +9064,8 @@ static bool hufUncompress(const char compressed[], int nCompressed, } hufBuildDecTable(&freq.at(0), im, iM, &hdec.at(0)); - hufDecode(&freq.at(0), &hdec.at(0), ptr, nBits, iM, nRaw, raw); + hufDecode(&freq.at(0), &hdec.at(0), ptr, nBits, iM, raw->size(), + raw->data()); } // catch (...) //{ @@ -8975,7 +9154,7 @@ static bool CompressPiz(unsigned char *outPtr, unsigned int *outSize, const unsigned char *inPtr, size_t inSize, const std::vector<ChannelInfo> &channelInfo, int data_width, int num_lines) { - unsigned char bitmap[BITMAP_SIZE]; + std::vector<unsigned char> bitmap(BITMAP_SIZE); unsigned short minNonZero; unsigned short maxNonZero; @@ -9026,12 +9205,12 @@ static bool CompressPiz(unsigned char *outPtr, unsigned int *outSize, } } - bitmapFromData(&tmpBuffer.at(0), static_cast<int>(tmpBuffer.size()), bitmap, - minNonZero, maxNonZero); + bitmapFromData(&tmpBuffer.at(0), static_cast<int>(tmpBuffer.size()), + bitmap.data(), minNonZero, maxNonZero); - unsigned short lut[USHORT_RANGE]; - unsigned short maxValue = forwardLutFromBitmap(bitmap, lut); - applyLut(lut, &tmpBuffer.at(0), static_cast<int>(tmpBuffer.size())); + std::vector<unsigned short> lut(USHORT_RANGE); + unsigned short maxValue = forwardLutFromBitmap(bitmap.data(), lut.data()); + applyLut(lut.data(), &tmpBuffer.at(0), static_cast<int>(tmpBuffer.size())); // // Store range compression info in _outBuffer @@ -9101,7 +9280,7 @@ static bool DecompressPiz(unsigned char *outPtr, const unsigned char *inPtr, return true; } - unsigned char bitmap[BITMAP_SIZE]; + std::vector<unsigned char> bitmap(BITMAP_SIZE); unsigned short minNonZero; unsigned short maxNonZero; @@ -9111,11 +9290,13 @@ static bool DecompressPiz(unsigned char *outPtr, const unsigned char *inPtr, return false; #endif - memset(bitmap, 0, BITMAP_SIZE); + memset(bitmap.data(), 0, BITMAP_SIZE); const unsigned char *ptr = inPtr; - minNonZero = *(reinterpret_cast<const unsigned short *>(ptr)); - maxNonZero = *(reinterpret_cast<const unsigned short *>(ptr + 2)); + // minNonZero = *(reinterpret_cast<const unsigned short *>(ptr)); + tinyexr::cpy2(&minNonZero, reinterpret_cast<const unsigned short *>(ptr)); + // maxNonZero = *(reinterpret_cast<const unsigned short *>(ptr + 2)); + tinyexr::cpy2(&maxNonZero, reinterpret_cast<const unsigned short *>(ptr + 2)); ptr += 4; if (maxNonZero >= BITMAP_SIZE) { @@ -9128,9 +9309,9 @@ static bool DecompressPiz(unsigned char *outPtr, const unsigned char *inPtr, ptr += maxNonZero - minNonZero + 1; } - unsigned short lut[USHORT_RANGE]; - memset(lut, 0, sizeof(unsigned short) * USHORT_RANGE); - unsigned short maxValue = reverseLutFromBitmap(bitmap, lut); + std::vector<unsigned short> lut(USHORT_RANGE); + memset(lut.data(), 0, sizeof(unsigned short) * USHORT_RANGE); + unsigned short maxValue = reverseLutFromBitmap(bitmap.data(), lut.data()); // // Huffman decoding @@ -9138,12 +9319,12 @@ static bool DecompressPiz(unsigned char *outPtr, const unsigned char *inPtr, int length; - length = *(reinterpret_cast<const int *>(ptr)); + // length = *(reinterpret_cast<const int *>(ptr)); + tinyexr::cpy4(&length, reinterpret_cast<const int *>(ptr)); ptr += sizeof(int); std::vector<unsigned short> tmpBuffer(tmpBufSize); - hufUncompress(reinterpret_cast<const char *>(ptr), length, &tmpBuffer.at(0), - static_cast<int>(tmpBufSize)); + hufUncompress(reinterpret_cast<const char *>(ptr), length, &tmpBuffer); // // Wavelet decoding @@ -9184,7 +9365,7 @@ static bool DecompressPiz(unsigned char *outPtr, const unsigned char *inPtr, // Expand the pixel data to their original range // - applyLut(lut, &tmpBuffer.at(0), static_cast<int>(tmpBufSize)); + applyLut(lut.data(), &tmpBuffer.at(0), static_cast<int>(tmpBufSize)); for (int y = 0; y < num_lines; y++) { for (size_t i = 0; i < channelData.size(); ++i) { @@ -9409,6 +9590,7 @@ bool CompressZfp(std::vector<unsigned char> *outBuf, unsigned int *outSize, // ----------------------------------------------------------------- // +// TODO(syoyo): Refactor function arguments. static bool DecodePixelData(/* out */ unsigned char **out_images, const int *requested_pixel_types, const unsigned char *data_ptr, size_t data_len, @@ -9421,6 +9603,11 @@ static bool DecodePixelData(/* out */ unsigned char **out_images, const std::vector<size_t> &channel_offset_list) { if (compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) { // PIZ #if TINYEXR_USE_PIZ + if ((width == 0) || (num_lines == 0) || (pixel_data_size == 0)) { + // Invalid input #90 + return false; + } + // Allocate original data size. std::vector<unsigned char> outBuf(static_cast<size_t>( static_cast<size_t>(width * num_lines) * pixel_data_size)); @@ -9452,7 +9639,10 @@ static bool DecodePixelData(/* out */ unsigned char **out_images, for (size_t u = 0; u < static_cast<size_t>(width); u++) { FP16 hf; - hf.u = line_ptr[u]; + // hf.u = line_ptr[u]; + // use `cpy` to avoid unaligned memory access when compiler's + // optimization is on. + tinyexr::cpy2(&(hf.u), line_ptr + u); tinyexr::swap2(reinterpret_cast<unsigned short *>(&hf.u)); @@ -9495,7 +9685,9 @@ static bool DecodePixelData(/* out */ unsigned char **out_images, &outBuf.at(v * pixel_data_size * static_cast<size_t>(width) + channel_offset_list[c] * static_cast<size_t>(width))); for (size_t u = 0; u < static_cast<size_t>(width); u++) { - unsigned int val = line_ptr[u]; + unsigned int val; + // val = line_ptr[u]; + tinyexr::cpy4(&val, line_ptr + u); tinyexr::swap4(&val); @@ -9521,7 +9713,9 @@ static bool DecodePixelData(/* out */ unsigned char **out_images, v * pixel_data_size * static_cast<size_t>(x_stride) + channel_offset_list[c] * static_cast<size_t>(x_stride))); for (size_t u = 0; u < static_cast<size_t>(width); u++) { - float val = line_ptr[u]; + float val; + // val = line_ptr[u]; + tinyexr::cpy4(&val, line_ptr + u); tinyexr::swap4(reinterpret_cast<unsigned int *>(&val)); @@ -9557,9 +9751,9 @@ static bool DecodePixelData(/* out */ unsigned char **out_images, unsigned long dstLen = static_cast<unsigned long>(outBuf.size()); assert(dstLen > 0); - if (!tinyexr::DecompressZip(reinterpret_cast<unsigned char *>(&outBuf.at(0)), - &dstLen, data_ptr, - static_cast<unsigned long>(data_len))) { + if (!tinyexr::DecompressZip( + reinterpret_cast<unsigned char *>(&outBuf.at(0)), &dstLen, data_ptr, + static_cast<unsigned long>(data_len))) { return false; } @@ -9583,7 +9777,8 @@ static bool DecodePixelData(/* out */ unsigned char **out_images, for (size_t u = 0; u < static_cast<size_t>(width); u++) { tinyexr::FP16 hf; - hf.u = line_ptr[u]; + // hf.u = line_ptr[u]; + tinyexr::cpy2(&(hf.u), line_ptr + u); tinyexr::swap2(reinterpret_cast<unsigned short *>(&hf.u)); @@ -9626,7 +9821,9 @@ static bool DecodePixelData(/* out */ unsigned char **out_images, &outBuf.at(v * pixel_data_size * static_cast<size_t>(width) + channel_offset_list[c] * static_cast<size_t>(width))); for (size_t u = 0; u < static_cast<size_t>(width); u++) { - unsigned int val = line_ptr[u]; + unsigned int val; + // val = line_ptr[u]; + tinyexr::cpy4(&val, line_ptr + u); tinyexr::swap4(&val); @@ -9652,7 +9849,9 @@ static bool DecodePixelData(/* out */ unsigned char **out_images, &outBuf.at(v * pixel_data_size * static_cast<size_t>(width) + channel_offset_list[c] * static_cast<size_t>(width))); for (size_t u = 0; u < static_cast<size_t>(width); u++) { - float val = line_ptr[u]; + float val; + // val = line_ptr[u]; + tinyexr::cpy4(&val, line_ptr + u); tinyexr::swap4(reinterpret_cast<unsigned int *>(&val)); @@ -9707,7 +9906,8 @@ static bool DecodePixelData(/* out */ unsigned char **out_images, for (size_t u = 0; u < static_cast<size_t>(width); u++) { tinyexr::FP16 hf; - hf.u = line_ptr[u]; + // hf.u = line_ptr[u]; + tinyexr::cpy2(&(hf.u), line_ptr + u); tinyexr::swap2(reinterpret_cast<unsigned short *>(&hf.u)); @@ -9750,7 +9950,9 @@ static bool DecodePixelData(/* out */ unsigned char **out_images, &outBuf.at(v * pixel_data_size * static_cast<size_t>(width) + channel_offset_list[c] * static_cast<size_t>(width))); for (size_t u = 0; u < static_cast<size_t>(width); u++) { - unsigned int val = line_ptr[u]; + unsigned int val; + // val = line_ptr[u]; + tinyexr::cpy4(&val, line_ptr + u); tinyexr::swap4(&val); @@ -9776,7 +9978,9 @@ static bool DecodePixelData(/* out */ unsigned char **out_images, &outBuf.at(v * pixel_data_size * static_cast<size_t>(width) + channel_offset_list[c] * static_cast<size_t>(width))); for (size_t u = 0; u < static_cast<size_t>(width); u++) { - float val = line_ptr[u]; + float val; + // val = line_ptr[u]; + tinyexr::cpy4(&val, line_ptr + u); tinyexr::swap4(reinterpret_cast<unsigned int *>(&val)); @@ -9839,7 +10043,8 @@ static bool DecodePixelData(/* out */ unsigned char **out_images, &outBuf.at(v * pixel_data_size * static_cast<size_t>(width) + channel_offset_list[c] * static_cast<size_t>(width))); for (size_t u = 0; u < static_cast<size_t>(width); u++) { - float val = line_ptr[u]; + float val; + tinyexr::cpy4(&val, line_ptr + u); tinyexr::swap4(reinterpret_cast<unsigned int *>(&val)); @@ -9871,88 +10076,116 @@ static bool DecodePixelData(/* out */ unsigned char **out_images, #endif } else if (compression_type == TINYEXR_COMPRESSIONTYPE_NONE) { for (size_t c = 0; c < num_channels; c++) { - if (channels[c].pixel_type == TINYEXR_PIXELTYPE_HALF) { - const unsigned short *line_ptr = - reinterpret_cast<const unsigned short *>( - data_ptr + - c * static_cast<size_t>(width) * sizeof(unsigned short)); - - if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_HALF) { - unsigned short *outLine = - reinterpret_cast<unsigned short *>(out_images[c]); - if (line_order == 0) { - outLine += y * x_stride; - } else { - outLine += (height - 1 - y) * x_stride; - } + for (size_t v = 0; v < static_cast<size_t>(num_lines); v++) { + if (channels[c].pixel_type == TINYEXR_PIXELTYPE_HALF) { + const unsigned short *line_ptr = + reinterpret_cast<const unsigned short *>( + data_ptr + v * pixel_data_size * size_t(width) + + channel_offset_list[c] * static_cast<size_t>(width)); + + if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_HALF) { + unsigned short *outLine = + reinterpret_cast<unsigned short *>(out_images[c]); + if (line_order == 0) { + outLine += (y + v) * x_stride; + } else { + outLine += (height - 1 - (y + v)) * x_stride; + } - for (int u = 0; u < width; u++) { - tinyexr::FP16 hf; + for (int u = 0; u < width; u++) { + tinyexr::FP16 hf; - hf.u = line_ptr[u]; + // hf.u = line_ptr[u]; + tinyexr::cpy2(&(hf.u), line_ptr + u); - tinyexr::swap2(reinterpret_cast<unsigned short *>(&hf.u)); + tinyexr::swap2(reinterpret_cast<unsigned short *>(&hf.u)); + + outLine[u] = hf.u; + } + } else if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT) { + float *outLine = reinterpret_cast<float *>(out_images[c]); + if (line_order == 0) { + outLine += (y + v) * x_stride; + } else { + outLine += (height - 1 - (y + v)) * x_stride; + } - outLine[u] = hf.u; + if (reinterpret_cast<const unsigned char *>(line_ptr + width) > + (data_ptr + data_len)) { + // Insufficient data size + return false; + } + + for (int u = 0; u < width; u++) { + tinyexr::FP16 hf; + + // address may not be aliged. use byte-wise copy for safety.#76 + // hf.u = line_ptr[u]; + tinyexr::cpy2(&(hf.u), line_ptr + u); + + tinyexr::swap2(reinterpret_cast<unsigned short *>(&hf.u)); + + tinyexr::FP32 f32 = half_to_float(hf); + + outLine[u] = f32.f; + } + } else { + assert(0); + return false; } - } else if (requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT) { + } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) { + const float *line_ptr = reinterpret_cast<const float *>( + data_ptr + v * pixel_data_size * size_t(width) + + channel_offset_list[c] * static_cast<size_t>(width)); + float *outLine = reinterpret_cast<float *>(out_images[c]); if (line_order == 0) { - outLine += y * x_stride; + outLine += (y + v) * x_stride; } else { - outLine += (height - 1 - y) * x_stride; + outLine += (height - 1 - (y + v)) * x_stride; } - for (int u = 0; u < width; u++) { - tinyexr::FP16 hf; - - hf.u = line_ptr[u]; - - tinyexr::swap2(reinterpret_cast<unsigned short *>(&hf.u)); - - tinyexr::FP32 f32 = half_to_float(hf); - - outLine[u] = f32.f; + if (reinterpret_cast<const unsigned char *>(line_ptr + width) > + (data_ptr + data_len)) { + // Insufficient data size + return false; } - } else { - assert(0); - return false; - } - } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) { - const float *line_ptr = reinterpret_cast<const float *>( - data_ptr + c * static_cast<size_t>(width) * sizeof(float)); - float *outLine = reinterpret_cast<float *>(out_images[c]); - if (line_order == 0) { - outLine += y * x_stride; - } else { - outLine += (height - 1 - y) * x_stride; - } + for (int u = 0; u < width; u++) { + float val; + tinyexr::cpy4(&val, line_ptr + u); - for (int u = 0; u < width; u++) { - float val = line_ptr[u]; + tinyexr::swap4(reinterpret_cast<unsigned int *>(&val)); - tinyexr::swap4(reinterpret_cast<unsigned int *>(&val)); + outLine[u] = val; + } + } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) { + const unsigned int *line_ptr = reinterpret_cast<const unsigned int *>( + data_ptr + v * pixel_data_size * size_t(width) + + channel_offset_list[c] * static_cast<size_t>(width)); - outLine[u] = val; - } - } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) { - const unsigned int *line_ptr = reinterpret_cast<const unsigned int *>( - data_ptr + c * static_cast<size_t>(width) * sizeof(unsigned int)); + unsigned int *outLine = + reinterpret_cast<unsigned int *>(out_images[c]); + if (line_order == 0) { + outLine += (y + v) * x_stride; + } else { + outLine += (height - 1 - (y + v)) * x_stride; + } - unsigned int *outLine = reinterpret_cast<unsigned int *>(out_images[c]); - if (line_order == 0) { - outLine += y * x_stride; - } else { - outLine += (height - 1 - y) * x_stride; - } + for (int u = 0; u < width; u++) { + if (reinterpret_cast<const unsigned char *>(line_ptr + u) >= + (data_ptr + data_len)) { + // Corrupsed data? + return false; + } - for (int u = 0; u < width; u++) { - unsigned int val = line_ptr[u]; + unsigned int val; + tinyexr::cpy4(&val, line_ptr + u); - tinyexr::swap4(reinterpret_cast<unsigned int *>(&val)); + tinyexr::swap4(reinterpret_cast<unsigned int *>(&val)); - outLine[u] = val; + outLine[u] = val; + } } } } @@ -9994,7 +10227,7 @@ static void DecodeTiledPixelData( num_channels, channels, channel_offset_list); } -static void ComputeChannelLayout(std::vector<size_t> *channel_offset_list, +static bool ComputeChannelLayout(std::vector<size_t> *channel_offset_list, int *pixel_data_size, size_t *channel_offset, int num_channels, const EXRChannelInfo *channels) { @@ -10015,9 +10248,11 @@ static void ComputeChannelLayout(std::vector<size_t> *channel_offset_list, (*pixel_data_size) += sizeof(unsigned int); (*channel_offset) += sizeof(unsigned int); } else { - assert(0); + // ??? + return false; } } + return true; } static unsigned char **AllocateImage(int num_channels, @@ -10125,8 +10360,11 @@ static int ParseEXRHeader(HeaderInfo *info, bool *empty_header, // Read attributes size_t orig_size = size; - for (;;) { + for (size_t nattr = 0; nattr < TINYEXR_MAX_HEADER_ATTRIBUTES; nattr++) { if (0 == size) { + if (err) { + (*err) += "Insufficient data size for attributes.\n"; + } return TINYEXR_ERROR_INVALID_DATA; } else if (marker[0] == '\0') { size--; @@ -10139,6 +10377,9 @@ static int ParseEXRHeader(HeaderInfo *info, bool *empty_header, size_t marker_size; if (!tinyexr::ReadAttribute(&attr_name, &attr_type, &data, &marker_size, marker, size)) { + if (err) { + (*err) += "Failed to read attribute.\n"; + } return TINYEXR_ERROR_INVALID_DATA; } marker += marker_size; @@ -10209,14 +10450,14 @@ static int ParseEXRHeader(HeaderInfo *info, bool *empty_header, if (!ReadChannelInfo(info->channels, data)) { if (err) { - (*err) = "Failed to parse channel info."; + (*err) += "Failed to parse channel info.\n"; } return TINYEXR_ERROR_INVALID_DATA; } if (info->channels.size() < 1) { if (err) { - (*err) = "# of channels is zero."; + (*err) += "# of channels is zero.\n"; } return TINYEXR_ERROR_INVALID_DATA; } @@ -10224,9 +10465,7 @@ static int ParseEXRHeader(HeaderInfo *info, bool *empty_header, has_channels = true; } else if (attr_name.compare("dataWindow") == 0) { - if (data.size() < 16) { - // Corrupsed file(Issue #50). - } else { + if (data.size() >= 16) { memcpy(&info->data_window[0], &data.at(0), sizeof(int)); memcpy(&info->data_window[1], &data.at(4), sizeof(int)); memcpy(&info->data_window[2], &data.at(8), sizeof(int)); @@ -10238,48 +10477,60 @@ static int ParseEXRHeader(HeaderInfo *info, bool *empty_header, has_data_window = true; } } else if (attr_name.compare("displayWindow") == 0) { - memcpy(&info->display_window[0], &data.at(0), sizeof(int)); - memcpy(&info->display_window[1], &data.at(4), sizeof(int)); - memcpy(&info->display_window[2], &data.at(8), sizeof(int)); - memcpy(&info->display_window[3], &data.at(12), sizeof(int)); - tinyexr::swap4( - reinterpret_cast<unsigned int *>(&info->display_window[0])); - tinyexr::swap4( - reinterpret_cast<unsigned int *>(&info->display_window[1])); - tinyexr::swap4( - reinterpret_cast<unsigned int *>(&info->display_window[2])); - tinyexr::swap4( - reinterpret_cast<unsigned int *>(&info->display_window[3])); - - has_display_window = true; + if (data.size() >= 16) { + memcpy(&info->display_window[0], &data.at(0), sizeof(int)); + memcpy(&info->display_window[1], &data.at(4), sizeof(int)); + memcpy(&info->display_window[2], &data.at(8), sizeof(int)); + memcpy(&info->display_window[3], &data.at(12), sizeof(int)); + tinyexr::swap4( + reinterpret_cast<unsigned int *>(&info->display_window[0])); + tinyexr::swap4( + reinterpret_cast<unsigned int *>(&info->display_window[1])); + tinyexr::swap4( + reinterpret_cast<unsigned int *>(&info->display_window[2])); + tinyexr::swap4( + reinterpret_cast<unsigned int *>(&info->display_window[3])); + + has_display_window = true; + } } else if (attr_name.compare("lineOrder") == 0) { - info->line_order = static_cast<int>(data[0]); - has_line_order = true; + if (data.size() >= 1) { + info->line_order = static_cast<int>(data[0]); + has_line_order = true; + } } else if (attr_name.compare("pixelAspectRatio") == 0) { - memcpy(&info->pixel_aspect_ratio, &data.at(0), sizeof(float)); - tinyexr::swap4( - reinterpret_cast<unsigned int *>(&info->pixel_aspect_ratio)); - has_pixel_aspect_ratio = true; + if (data.size() >= sizeof(float)) { + memcpy(&info->pixel_aspect_ratio, &data.at(0), sizeof(float)); + tinyexr::swap4( + reinterpret_cast<unsigned int *>(&info->pixel_aspect_ratio)); + has_pixel_aspect_ratio = true; + } } else if (attr_name.compare("screenWindowCenter") == 0) { - memcpy(&info->screen_window_center[0], &data.at(0), sizeof(float)); - memcpy(&info->screen_window_center[1], &data.at(4), sizeof(float)); - tinyexr::swap4( - reinterpret_cast<unsigned int *>(&info->screen_window_center[0])); - tinyexr::swap4( - reinterpret_cast<unsigned int *>(&info->screen_window_center[1])); - has_screen_window_center = true; + if (data.size() >= 8) { + memcpy(&info->screen_window_center[0], &data.at(0), sizeof(float)); + memcpy(&info->screen_window_center[1], &data.at(4), sizeof(float)); + tinyexr::swap4( + reinterpret_cast<unsigned int *>(&info->screen_window_center[0])); + tinyexr::swap4( + reinterpret_cast<unsigned int *>(&info->screen_window_center[1])); + has_screen_window_center = true; + } } else if (attr_name.compare("screenWindowWidth") == 0) { - memcpy(&info->screen_window_width, &data.at(0), sizeof(float)); - tinyexr::swap4( - reinterpret_cast<unsigned int *>(&info->screen_window_width)); + if (data.size() >= sizeof(float)) { + memcpy(&info->screen_window_width, &data.at(0), sizeof(float)); + tinyexr::swap4( + reinterpret_cast<unsigned int *>(&info->screen_window_width)); - has_screen_window_width = true; + has_screen_window_width = true; + } } else if (attr_name.compare("chunkCount") == 0) { - memcpy(&info->chunk_count, &data.at(0), sizeof(int)); - tinyexr::swap4(reinterpret_cast<unsigned int *>(&info->chunk_count)); + if (data.size() >= sizeof(int)) { + memcpy(&info->chunk_count, &data.at(0), sizeof(int)); + tinyexr::swap4(reinterpret_cast<unsigned int *>(&info->chunk_count)); + } } else { - // Custom attribute(up to TINYEXR_MAX_ATTRIBUTES) - if (info->attributes.size() < TINYEXR_MAX_ATTRIBUTES) { + // Custom attribute(up to TINYEXR_MAX_CUSTOM_ATTRIBUTES) + if (info->attributes.size() < TINYEXR_MAX_CUSTOM_ATTRIBUTES) { EXRAttribute attrib; #ifdef _MSC_VER strncpy_s(attrib.name, attr_name.c_str(), 255); @@ -10409,15 +10660,30 @@ static void ConvertHeader(EXRHeader *exr_header, const HeaderInfo &info) { exr_header->requested_pixel_types[c] = info.channels[c].pixel_type; } - assert(info.attributes.size() < TINYEXR_MAX_ATTRIBUTES); exr_header->num_custom_attributes = static_cast<int>(info.attributes.size()); - for (size_t i = 0; i < info.attributes.size(); i++) { - memcpy(exr_header->custom_attributes[i].name, info.attributes[i].name, 256); - memcpy(exr_header->custom_attributes[i].type, info.attributes[i].type, 256); - exr_header->custom_attributes[i].size = info.attributes[i].size; - // Just copy poiner - exr_header->custom_attributes[i].value = info.attributes[i].value; + if (exr_header->num_custom_attributes > 0) { + // TODO(syoyo): Report warning when # of attributes exceeds + // `TINYEXR_MAX_CUSTOM_ATTRIBUTES` + if (exr_header->num_custom_attributes > TINYEXR_MAX_CUSTOM_ATTRIBUTES) { + exr_header->num_custom_attributes = TINYEXR_MAX_CUSTOM_ATTRIBUTES; + } + + exr_header->custom_attributes = static_cast<EXRAttribute *>(malloc( + sizeof(EXRAttribute) * size_t(exr_header->num_custom_attributes))); + + for (size_t i = 0; i < info.attributes.size(); i++) { + memcpy(exr_header->custom_attributes[i].name, info.attributes[i].name, + 256); + memcpy(exr_header->custom_attributes[i].type, info.attributes[i].type, + 256); + exr_header->custom_attributes[i].size = info.attributes[i].size; + // Just copy poiner + exr_header->custom_attributes[i].value = info.attributes[i].value; + } + + } else { + exr_header->custom_attributes = NULL; } exr_header->header_len = info.header_len; @@ -10425,7 +10691,8 @@ static void ConvertHeader(EXRHeader *exr_header, const HeaderInfo &info) { static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header, const std::vector<tinyexr::tinyexr_uint64> &offsets, - const unsigned char *head, const size_t size) { + const unsigned char *head, const size_t size, + std::string *err) { int num_channels = exr_header->num_channels; int num_scanline_blocks = 1; @@ -10445,32 +10712,40 @@ static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header, std::vector<size_t> channel_offset_list; int pixel_data_size = 0; size_t channel_offset = 0; - tinyexr::ComputeChannelLayout(&channel_offset_list, &pixel_data_size, - &channel_offset, num_channels, - exr_header->channels); + if (!tinyexr::ComputeChannelLayout(&channel_offset_list, &pixel_data_size, + &channel_offset, num_channels, + exr_header->channels)) { + if (err) { + (*err) += "Failed to compute channel layout.\n"; + } + return TINYEXR_ERROR_INVALID_DATA; + } - bool invalid_data = false; + bool invalid_data = false; // TODO(LTE): Use atomic lock for MT safety. if (exr_header->tiled) { size_t num_tiles = offsets.size(); // = # of blocks exr_image->tiles = static_cast<EXRTile *>( - malloc(sizeof(EXRTile) * static_cast<size_t>(num_tiles))); + calloc(sizeof(EXRTile), static_cast<size_t>(num_tiles))); for (size_t tile_idx = 0; tile_idx < num_tiles; tile_idx++) { // Allocate memory for each tile. exr_image->tiles[tile_idx].images = tinyexr::AllocateImage( num_channels, exr_header->channels, exr_header->requested_pixel_types, - data_width, data_height); + exr_header->tile_size_x, exr_header->tile_size_y); // 16 byte: tile coordinates // 4 byte : data size // ~ : data(uncompressed or compressed) if (offsets[tile_idx] + sizeof(int) * 5 > size) { + if (err) { + (*err) += "Insufficient data size.\n"; + } return TINYEXR_ERROR_INVALID_DATA; } - size_t data_size = size - (offsets[tile_idx] + sizeof(int) * 5); + size_t data_size = size_t(size - (offsets[tile_idx] + sizeof(int) * 5)); const unsigned char *data_ptr = reinterpret_cast<const unsigned char *>(head + offsets[tile_idx]); @@ -10482,8 +10757,12 @@ static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header, tinyexr::swap4(reinterpret_cast<unsigned int *>(&tile_coordinates[3])); // @todo{ LoD } - assert(tile_coordinates[2] == 0); - assert(tile_coordinates[3] == 0); + if (tile_coordinates[2] != 0) { + return TINYEXR_ERROR_UNSUPPORTED_FEATURE; + } + if (tile_coordinates[3] != 0) { + return TINYEXR_ERROR_UNSUPPORTED_FEATURE; + } int data_len; memcpy(&data_len, data_ptr + 16, @@ -10491,6 +10770,9 @@ static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header, tinyexr::swap4(reinterpret_cast<unsigned int *>(&data_len)); if (data_len < 4 || size_t(data_len) > data_size) { + if (err) { + (*err) += "Insufficient data length.\n"; + } return TINYEXR_ERROR_INVALID_DATA; } @@ -10531,56 +10813,56 @@ static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header, size_t y_idx = static_cast<size_t>(y); if (offsets[y_idx] + sizeof(int) * 2 > size) { - return TINYEXR_ERROR_INVALID_DATA; - } - - // 4 byte: scan line - // 4 byte: data size - // ~ : pixel data(uncompressed or compressed) - size_t data_size = size - (offsets[y_idx] + sizeof(int) * 2); - const unsigned char *data_ptr = - reinterpret_cast<const unsigned char *>(head + offsets[y_idx]); - - int line_no; - memcpy(&line_no, data_ptr, sizeof(int)); - int data_len; - memcpy(&data_len, data_ptr + 4, sizeof(int)); - tinyexr::swap4(reinterpret_cast<unsigned int *>(&line_no)); - tinyexr::swap4(reinterpret_cast<unsigned int *>(&data_len)); - - if (size_t(data_len) > data_size) { - return TINYEXR_ERROR_INVALID_DATA; - } - - int end_line_no = (std::min)(line_no + num_scanline_blocks, - (exr_header->data_window[3] + 1)); - - int num_lines = end_line_no - line_no; - //assert(num_lines > 0); - - if (num_lines <= 0) { invalid_data = true; } else { - - // Move to data addr: 8 = 4 + 4; - data_ptr += 8; - - // Adjust line_no with data_window.bmin.y - line_no -= exr_header->data_window[1]; - - if (line_no < 0) { + // 4 byte: scan line + // 4 byte: data size + // ~ : pixel data(uncompressed or compressed) + size_t data_size = size_t(size - (offsets[y_idx] + sizeof(int) * 2)); + const unsigned char *data_ptr = + reinterpret_cast<const unsigned char *>(head + offsets[y_idx]); + + int line_no; + memcpy(&line_no, data_ptr, sizeof(int)); + int data_len; + memcpy(&data_len, data_ptr + 4, sizeof(int)); + tinyexr::swap4(reinterpret_cast<unsigned int *>(&line_no)); + tinyexr::swap4(reinterpret_cast<unsigned int *>(&data_len)); + + if (size_t(data_len) > data_size) { invalid_data = true; } else { - if (!tinyexr::DecodePixelData( - exr_image->images, exr_header->requested_pixel_types, data_ptr, - static_cast<size_t>(data_len), exr_header->compression_type, - exr_header->line_order, data_width, data_height, data_width, y, - line_no, num_lines, static_cast<size_t>(pixel_data_size), - static_cast<size_t>(exr_header->num_custom_attributes), - exr_header->custom_attributes, - static_cast<size_t>(exr_header->num_channels), exr_header->channels, - channel_offset_list)) { + int end_line_no = (std::min)(line_no + num_scanline_blocks, + (exr_header->data_window[3] + 1)); + + int num_lines = end_line_no - line_no; + // assert(num_lines > 0); + + if (num_lines <= 0) { invalid_data = true; + } else { + // Move to data addr: 8 = 4 + 4; + data_ptr += 8; + + // Adjust line_no with data_window.bmin.y + line_no -= exr_header->data_window[1]; + + if (line_no < 0) { + invalid_data = true; + } else { + if (!tinyexr::DecodePixelData( + exr_image->images, exr_header->requested_pixel_types, + data_ptr, static_cast<size_t>(data_len), + exr_header->compression_type, exr_header->line_order, + data_width, data_height, data_width, y, line_no, + num_lines, static_cast<size_t>(pixel_data_size), + static_cast<size_t>(exr_header->num_custom_attributes), + exr_header->custom_attributes, + static_cast<size_t>(exr_header->num_channels), + exr_header->channels, channel_offset_list)) { + invalid_data = true; + } + } } } } @@ -10648,9 +10930,7 @@ static int DecodeEXRImage(EXRImage *exr_image, const EXRHeader *exr_header, const char **err) { if (exr_image == NULL || exr_header == NULL || head == NULL || marker == NULL || (size <= tinyexr::kEXRVersionSize)) { - if (err) { - (*err) = "Invalid argument."; - } + tinyexr::SetErrorMessage("Invalid argument for DecodeEXRImage().", err); return TINYEXR_ERROR_INVALID_ARGUMENT; } @@ -10663,13 +10943,23 @@ static int DecodeEXRImage(EXRImage *exr_image, const EXRHeader *exr_header, num_scanline_blocks = 16; } - int data_width = exr_header->data_window[2] - exr_header->data_window[0] + 1; - int data_height = exr_header->data_window[3] - exr_header->data_window[1] + 1; + int data_width = exr_header->data_window[2] - exr_header->data_window[0]; + if (data_width >= std::numeric_limits<int>::max()) { + // Issue 63 + tinyexr::SetErrorMessage("Invalid data window value", err); + return TINYEXR_ERROR_INVALID_DATA; + } + data_width++; + + int data_height = exr_header->data_window[3] - exr_header->data_window[1]; + if (data_height >= std::numeric_limits<int>::max()) { + tinyexr::SetErrorMessage("Invalid data height value", err); + return TINYEXR_ERROR_INVALID_DATA; + } + data_height++; if ((data_width < 0) || (data_height < 0)) { - if (err) { - (*err) = "Invalid data window value."; - } + tinyexr::SetErrorMessage("data window or data height is negative.", err); return TINYEXR_ERROR_INVALID_DATA; } @@ -10708,12 +10998,16 @@ static int DecodeEXRImage(EXRImage *exr_image, const EXRHeader *exr_header, for (size_t y = 0; y < num_blocks; y++) { tinyexr::tinyexr_uint64 offset; + // Issue #81 + if ((marker + sizeof(tinyexr_uint64)) >= (head + size)) { + tinyexr::SetErrorMessage("Insufficient data size in offset table.", err); + return TINYEXR_ERROR_INVALID_DATA; + } + memcpy(&offset, marker, sizeof(tinyexr::tinyexr_uint64)); tinyexr::swap8(&offset); if (offset >= size) { - if (err) { - (*err) = "Invalid offset value."; - } + tinyexr::SetErrorMessage("Invalid offset value in DecodeEXRImage.", err); return TINYEXR_ERROR_INVALID_DATA; } marker += sizeof(tinyexr::tinyexr_uint64); // = 8 @@ -10736,15 +11030,37 @@ static int DecodeEXRImage(EXRImage *exr_image, const EXRHeader *exr_header, // OK break; } else { - if (err) { - (*err) = "Cannot reconstruct lineOffset table."; - } + tinyexr::SetErrorMessage( + "Cannot reconstruct lineOffset table in DecodeEXRImage.", err); return TINYEXR_ERROR_INVALID_DATA; } } } - return DecodeChunk(exr_image, exr_header, offsets, head, size); + { + std::string e; + int ret = DecodeChunk(exr_image, exr_header, offsets, head, size, &e); + + if (ret != TINYEXR_SUCCESS) { + if (!e.empty()) { + tinyexr::SetErrorMessage(e, err); + } + + // release memory(if exists) + if ((exr_header->num_channels > 0) && exr_image && exr_image->images) { + for (size_t c = 0; c < size_t(exr_header->num_channels); c++) { + if (exr_image->images[c]) { + free(exr_image->images[c]); + exr_image->images[c] = NULL; + } + } + free(exr_image->images); + exr_image->images = NULL; + } + } + + return ret; + } } } // namespace tinyexr @@ -10752,9 +11068,7 @@ static int DecodeEXRImage(EXRImage *exr_image, const EXRHeader *exr_header, int LoadEXR(float **out_rgba, int *width, int *height, const char *filename, const char **err) { if (out_rgba == NULL) { - if (err) { - (*err) = "Invalid argument.\n"; - } + tinyexr::SetErrorMessage("Invalid argument for LoadEXR()", err); return TINYEXR_ERROR_INVALID_ARGUMENT; } @@ -10767,13 +11081,14 @@ int LoadEXR(float **out_rgba, int *width, int *height, const char *filename, { int ret = ParseEXRVersionFromFile(&exr_version, filename); if (ret != TINYEXR_SUCCESS) { + tinyexr::SetErrorMessage("Invalid EXR header.", err); return ret; } if (exr_version.multipart || exr_version.non_image) { - if (err) { - (*err) = "Loading multipart or DeepImage is not supported yet.\n"; - } + tinyexr::SetErrorMessage( + "Loading multipart or DeepImage is not supported in LoadEXR() API", + err); return TINYEXR_ERROR_INVALID_DATA; // @fixme. } } @@ -10781,6 +11096,7 @@ int LoadEXR(float **out_rgba, int *width, int *height, const char *filename, { int ret = ParseEXRHeaderFromFile(&exr_header, &exr_version, filename, err); if (ret != TINYEXR_SUCCESS) { + FreeEXRHeader(&exr_header); return ret; } } @@ -10795,6 +11111,7 @@ int LoadEXR(float **out_rgba, int *width, int *height, const char *filename, { int ret = LoadEXRImageFromFile(&exr_image, &exr_header, filename, err); if (ret != TINYEXR_SUCCESS) { + FreeEXRHeader(&exr_header); return ret; } } @@ -10819,6 +11136,9 @@ int LoadEXR(float **out_rgba, int *width, int *height, const char *filename, if ((idxA == 0) && (idxR == -1) && (idxG == -1) && (idxB == -1)) { // Alpha channel only. + if (exr_header.tiled) { + // todo.implement this + } (*out_rgba) = reinterpret_cast<float *>( malloc(4 * sizeof(float) * static_cast<size_t>(exr_image.width) * static_cast<size_t>(exr_image.height))); @@ -10833,45 +11153,77 @@ int LoadEXR(float **out_rgba, int *width, int *height, const char *filename, // Assume RGB(A) if (idxR == -1) { - if (err) { - (*err) = "R channel not found\n"; - } + tinyexr::SetErrorMessage("R channel not found", err); // @todo { free exr_image } + FreeEXRHeader(&exr_header); return TINYEXR_ERROR_INVALID_DATA; } if (idxG == -1) { - if (err) { - (*err) = "G channel not found\n"; - } + tinyexr::SetErrorMessage("G channel not found", err); // @todo { free exr_image } + FreeEXRHeader(&exr_header); return TINYEXR_ERROR_INVALID_DATA; } if (idxB == -1) { - if (err) { - (*err) = "B channel not found\n"; - } + tinyexr::SetErrorMessage("B channel not found", err); // @todo { free exr_image } + FreeEXRHeader(&exr_header); return TINYEXR_ERROR_INVALID_DATA; } (*out_rgba) = reinterpret_cast<float *>( malloc(4 * sizeof(float) * static_cast<size_t>(exr_image.width) * static_cast<size_t>(exr_image.height))); - for (int i = 0; i < exr_image.width * exr_image.height; i++) { - (*out_rgba)[4 * i + 0] = - reinterpret_cast<float **>(exr_image.images)[idxR][i]; - (*out_rgba)[4 * i + 1] = - reinterpret_cast<float **>(exr_image.images)[idxG][i]; - (*out_rgba)[4 * i + 2] = - reinterpret_cast<float **>(exr_image.images)[idxB][i]; - if (idxA != -1) { - (*out_rgba)[4 * i + 3] = - reinterpret_cast<float **>(exr_image.images)[idxA][i]; - } else { - (*out_rgba)[4 * i + 3] = 1.0; + if (exr_header.tiled) { + for (int it = 0; it < exr_image.num_tiles; it++) { + for (int j = 0; j < exr_header.tile_size_y; j++) + for (int i = 0; i < exr_header.tile_size_x; i++) { + const int ii = + exr_image.tiles[it].offset_x * exr_header.tile_size_x + i; + const int jj = + exr_image.tiles[it].offset_y * exr_header.tile_size_y + j; + const int idx = ii + jj * exr_image.width; + + // out of region check. + if (ii >= exr_image.width) { + continue; + } + if (jj >= exr_image.height) { + continue; + } + const int srcIdx = i + j * exr_header.tile_size_x; + unsigned char **src = exr_image.tiles[it].images; + (*out_rgba)[4 * idx + 0] = + reinterpret_cast<float **>(src)[idxR][srcIdx]; + (*out_rgba)[4 * idx + 1] = + reinterpret_cast<float **>(src)[idxG][srcIdx]; + (*out_rgba)[4 * idx + 2] = + reinterpret_cast<float **>(src)[idxB][srcIdx]; + if (idxA != -1) { + (*out_rgba)[4 * idx + 3] = + reinterpret_cast<float **>(src)[idxA][srcIdx]; + } else { + (*out_rgba)[4 * idx + 3] = 1.0; + } + } + } + } else { + for (int i = 0; i < exr_image.width * exr_image.height; i++) { + (*out_rgba)[4 * i + 0] = + reinterpret_cast<float **>(exr_image.images)[idxR][i]; + (*out_rgba)[4 * i + 1] = + reinterpret_cast<float **>(exr_image.images)[idxG][i]; + (*out_rgba)[4 * i + 2] = + reinterpret_cast<float **>(exr_image.images)[idxB][i]; + if (idxA != -1) { + (*out_rgba)[4 * i + 3] = + reinterpret_cast<float **>(exr_image.images)[idxA][i]; + } else { + (*out_rgba)[4 * i + 3] = 1.0; + } } } } @@ -10889,15 +11241,17 @@ int ParseEXRHeaderFromMemory(EXRHeader *exr_header, const EXRVersion *version, const unsigned char *memory, size_t size, const char **err) { if (memory == NULL || exr_header == NULL) { - if (err) { - (*err) = "Invalid argument.\n"; - } + tinyexr::SetErrorMessage( + "Invalid argument. `memory` or `exr_header` argument is null in " + "ParseEXRHeaderFromMemory()", + err); // Invalid argument return TINYEXR_ERROR_INVALID_ARGUMENT; } if (size < tinyexr::kEXRVersionSize) { + tinyexr::SetErrorMessage("Insufficient header/data size.\n", err); return TINYEXR_ERROR_INVALID_DATA; } @@ -10912,11 +11266,7 @@ int ParseEXRHeaderFromMemory(EXRHeader *exr_header, const EXRVersion *version, if (ret != TINYEXR_SUCCESS) { if (err && !err_str.empty()) { -#ifdef _WIN32 - (*err) = _strdup(err_str.c_str()); // May leak -#else - (*err) = strdup(err_str.c_str()); // May leak -#endif + tinyexr::SetErrorMessage(err_str, err); } } @@ -10932,9 +11282,7 @@ int LoadEXRFromMemory(float **out_rgba, int *width, int *height, const unsigned char *memory, size_t size, const char **err) { if (out_rgba == NULL || memory == NULL) { - if (err) { - (*err) = "Invalid argument.\n"; - } + tinyexr::SetErrorMessage("Invalid argument for LoadEXRFromMemory", err); return TINYEXR_ERROR_INVALID_ARGUMENT; } @@ -10946,6 +11294,7 @@ int LoadEXRFromMemory(float **out_rgba, int *width, int *height, int ret = ParseEXRVersionFromMemory(&exr_version, memory, size); if (ret != TINYEXR_SUCCESS) { + tinyexr::SetErrorMessage("Failed to parse EXR version", err); return ret; } @@ -10985,26 +11334,20 @@ int LoadEXRFromMemory(float **out_rgba, int *width, int *height, } if (idxR == -1) { - if (err) { - (*err) = "R channel not found\n"; - } + tinyexr::SetErrorMessage("R channel not found", err); // @todo { free exr_image } return TINYEXR_ERROR_INVALID_DATA; } if (idxG == -1) { - if (err) { - (*err) = "G channel not found\n"; - } + tinyexr::SetErrorMessage("G channel not found", err); // @todo { free exr_image } return TINYEXR_ERROR_INVALID_DATA; } if (idxB == -1) { - if (err) { - (*err) = "B channel not found\n"; - } + tinyexr::SetErrorMessage("B channel not found", err); // @todo { free exr_image } return TINYEXR_ERROR_INVALID_DATA; } @@ -11040,9 +11383,7 @@ int LoadEXRFromMemory(float **out_rgba, int *width, int *height, int LoadEXRImageFromFile(EXRImage *exr_image, const EXRHeader *exr_header, const char *filename, const char **err) { if (exr_image == NULL) { - if (err) { - (*err) = "Invalid argument."; - } + tinyexr::SetErrorMessage("Invalid argument for LoadEXRImageFromFile", err); return TINYEXR_ERROR_INVALID_ARGUMENT; } @@ -11053,9 +11394,7 @@ int LoadEXRImageFromFile(EXRImage *exr_image, const EXRHeader *exr_header, FILE *fp = fopen(filename, "rb"); #endif if (!fp) { - if (err) { - (*err) = "Cannot read file."; - } + tinyexr::SetErrorMessage("Cannot read file " + std::string(filename), err); return TINYEXR_ERROR_CANT_OPEN_FILE; } @@ -11065,6 +11404,12 @@ int LoadEXRImageFromFile(EXRImage *exr_image, const EXRHeader *exr_header, filesize = static_cast<size_t>(ftell(fp)); fseek(fp, 0, SEEK_SET); + if (filesize < 16) { + tinyexr::SetErrorMessage("File size too short " + std::string(filename), + err); + return TINYEXR_ERROR_INVALID_FILE; + } + std::vector<unsigned char> buf(filesize); // @todo { use mmap } { size_t ret; @@ -11083,16 +11428,13 @@ int LoadEXRImageFromMemory(EXRImage *exr_image, const EXRHeader *exr_header, const char **err) { if (exr_image == NULL || memory == NULL || (size < tinyexr::kEXRVersionSize)) { - if (err) { - (*err) = "Invalid argument."; - } + tinyexr::SetErrorMessage("Invalid argument for LoadEXRImageFromMemory", + err); return TINYEXR_ERROR_INVALID_ARGUMENT; } if (exr_header->header_len == 0) { - if (err) { - (*err) = "EXRHeader is not initialized."; - } + tinyexr::SetErrorMessage("EXRHeader variable is not initialized.", err); return TINYEXR_ERROR_INVALID_ARGUMENT; } @@ -11109,26 +11451,22 @@ size_t SaveEXRImageToMemory(const EXRImage *exr_image, unsigned char **memory_out, const char **err) { if (exr_image == NULL || memory_out == NULL || exr_header->compression_type < 0) { - if (err) { - (*err) = "Invalid argument."; - } + tinyexr::SetErrorMessage("Invalid argument for SaveEXRImageToMemory", err); return 0; // @fixme } #if !TINYEXR_USE_PIZ if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) { - if (err) { - (*err) = "PIZ compression is not supported in this build."; - } + tinyexr::SetErrorMessage("PIZ compression is not supported in this build", + err); return 0; } #endif #if !TINYEXR_USE_ZFP if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_ZFP) { - if (err) { - (*err) = "ZFP compression is not supported in this build."; - } + tinyexr::SetErrorMessage("ZFP compression is not supported in this build", + err); return 0; } #endif @@ -11136,9 +11474,8 @@ size_t SaveEXRImageToMemory(const EXRImage *exr_image, #if TINYEXR_USE_ZFP for (size_t i = 0; i < static_cast<size_t>(exr_header->num_channels); i++) { if (exr_header->requested_pixel_types[i] != TINYEXR_PIXELTYPE_FLOAT) { - if (err) { - (*err) = "Pixel type must be FLOAT for ZFP compression."; - } + tinyexr::SetErrorMessage("Pixel type must be FLOAT for ZFP compression", + err); return 0; } } @@ -11348,6 +11685,11 @@ size_t SaveEXRImageToMemory(const EXRImage *exr_image, if (exr_header->pixel_types[c] == TINYEXR_PIXELTYPE_HALF) { if (exr_header->requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT) { for (int y = 0; y < h; y++) { + // Assume increasing Y + float *line_ptr = reinterpret_cast<float *>(&buf.at( + static_cast<size_t>(pixel_data_size * y * exr_image->width) + + channel_offset_list[c] * + static_cast<size_t>(exr_image->width))); for (int x = 0; x < exr_image->width; x++) { tinyexr::FP16 h16; h16.u = reinterpret_cast<unsigned short **>( @@ -11357,30 +11699,27 @@ size_t SaveEXRImageToMemory(const EXRImage *exr_image, tinyexr::swap4(reinterpret_cast<unsigned int *>(&f32.f)); - // Assume increasing Y - float *line_ptr = reinterpret_cast<float *>(&buf.at( - static_cast<size_t>(pixel_data_size * y * exr_image->width) + - channel_offset_list[c] * - static_cast<size_t>(exr_image->width))); - line_ptr[x] = f32.f; + // line_ptr[x] = f32.f; + tinyexr::cpy4(line_ptr + x, &(f32.f)); } } } else if (exr_header->requested_pixel_types[c] == TINYEXR_PIXELTYPE_HALF) { for (int y = 0; y < h; y++) { + // Assume increasing Y + unsigned short *line_ptr = reinterpret_cast<unsigned short *>( + &buf.at(static_cast<size_t>(pixel_data_size * y * + exr_image->width) + + channel_offset_list[c] * + static_cast<size_t>(exr_image->width))); for (int x = 0; x < exr_image->width; x++) { unsigned short val = reinterpret_cast<unsigned short **>( exr_image->images)[c][(y + start_y) * exr_image->width + x]; tinyexr::swap2(&val); - // Assume increasing Y - unsigned short *line_ptr = reinterpret_cast<unsigned short *>( - &buf.at(static_cast<size_t>(pixel_data_size * y * - exr_image->width) + - channel_offset_list[c] * - static_cast<size_t>(exr_image->width))); - line_ptr[x] = val; + // line_ptr[x] = val; + tinyexr::cpy2(line_ptr + x, &val); } } } else { @@ -11390,6 +11729,12 @@ size_t SaveEXRImageToMemory(const EXRImage *exr_image, } else if (exr_header->pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT) { if (exr_header->requested_pixel_types[c] == TINYEXR_PIXELTYPE_HALF) { for (int y = 0; y < h; y++) { + // Assume increasing Y + unsigned short *line_ptr = reinterpret_cast<unsigned short *>( + &buf.at(static_cast<size_t>(pixel_data_size * y * + exr_image->width) + + channel_offset_list[c] * + static_cast<size_t>(exr_image->width))); for (int x = 0; x < exr_image->width; x++) { tinyexr::FP32 f32; f32.f = reinterpret_cast<float **>( @@ -11400,30 +11745,26 @@ size_t SaveEXRImageToMemory(const EXRImage *exr_image, tinyexr::swap2(reinterpret_cast<unsigned short *>(&h16.u)); - // Assume increasing Y - unsigned short *line_ptr = reinterpret_cast<unsigned short *>( - &buf.at(static_cast<size_t>(pixel_data_size * y * - exr_image->width) + - channel_offset_list[c] * - static_cast<size_t>(exr_image->width))); - line_ptr[x] = h16.u; + // line_ptr[x] = h16.u; + tinyexr::cpy2(line_ptr + x, &(h16.u)); } } } else if (exr_header->requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT) { for (int y = 0; y < h; y++) { + // Assume increasing Y + float *line_ptr = reinterpret_cast<float *>(&buf.at( + static_cast<size_t>(pixel_data_size * y * exr_image->width) + + channel_offset_list[c] * + static_cast<size_t>(exr_image->width))); for (int x = 0; x < exr_image->width; x++) { float val = reinterpret_cast<float **>( exr_image->images)[c][(y + start_y) * exr_image->width + x]; tinyexr::swap4(reinterpret_cast<unsigned int *>(&val)); - // Assume increasing Y - float *line_ptr = reinterpret_cast<float *>(&buf.at( - static_cast<size_t>(pixel_data_size * y * exr_image->width) + - channel_offset_list[c] * - static_cast<size_t>(exr_image->width))); - line_ptr[x] = val; + // line_ptr[x] = val; + tinyexr::cpy4(line_ptr + x, &val); } } } else { @@ -11431,18 +11772,18 @@ size_t SaveEXRImageToMemory(const EXRImage *exr_image, } } else if (exr_header->pixel_types[c] == TINYEXR_PIXELTYPE_UINT) { for (int y = 0; y < h; y++) { + // Assume increasing Y + unsigned int *line_ptr = reinterpret_cast<unsigned int *>(&buf.at( + static_cast<size_t>(pixel_data_size * y * exr_image->width) + + channel_offset_list[c] * static_cast<size_t>(exr_image->width))); for (int x = 0; x < exr_image->width; x++) { unsigned int val = reinterpret_cast<unsigned int **>( exr_image->images)[c][(y + start_y) * exr_image->width + x]; tinyexr::swap4(&val); - // Assume increasing Y - unsigned int *line_ptr = reinterpret_cast<unsigned int *>(&buf.at( - static_cast<size_t>(pixel_data_size * y * exr_image->width) + - channel_offset_list[c] * - static_cast<size_t>(exr_image->width))); - line_ptr[x] = val; + // line_ptr[x] = val; + tinyexr::cpy4(line_ptr + x, &val); } } } @@ -11611,26 +11952,22 @@ int SaveEXRImageToFile(const EXRImage *exr_image, const EXRHeader *exr_header, const char *filename, const char **err) { if (exr_image == NULL || filename == NULL || exr_header->compression_type < 0) { - if (err) { - (*err) = "Invalid argument."; - } + tinyexr::SetErrorMessage("Invalid argument for SaveEXRImageToFile", err); return TINYEXR_ERROR_INVALID_ARGUMENT; } #if !TINYEXR_USE_PIZ if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_PIZ) { - if (err) { - (*err) = "PIZ compression is not supported in this build."; - } + tinyexr::SetErrorMessage("PIZ compression is not supported in this build", + err); return 0; } #endif #if !TINYEXR_USE_ZFP if (exr_header->compression_type == TINYEXR_COMPRESSIONTYPE_ZFP) { - if (err) { - (*err) = "ZFP compression is not supported in this build."; - } + tinyexr::SetErrorMessage("ZFP compression is not supported in this build", + err); return 0; } #endif @@ -11642,9 +11979,7 @@ int SaveEXRImageToFile(const EXRImage *exr_image, const EXRHeader *exr_header, FILE *fp = fopen(filename, "wb"); #endif if (!fp) { - if (err) { - (*err) = "Cannot write a file."; - } + tinyexr::SetErrorMessage("Cannot write a file", err); return TINYEXR_ERROR_CANT_OPEN_FILE; } @@ -11663,27 +11998,23 @@ int SaveEXRImageToFile(const EXRImage *exr_image, const EXRHeader *exr_header, int LoadDeepEXR(DeepImage *deep_image, const char *filename, const char **err) { if (deep_image == NULL) { - if (err) { - (*err) = "Invalid argument."; - } + tinyexr::SetErrorMessage("Invalid argument for LoadDeepEXR", err); return TINYEXR_ERROR_INVALID_ARGUMENT; } #ifdef _MSC_VER FILE *fp = NULL; errno_t errcode = fopen_s(&fp, filename, "rb"); - if ((!errcode) || (!fp)) { - if (err) { - (*err) = "Cannot read file."; - } + if ((0 != errcode) || (!fp)) { + tinyexr::SetErrorMessage("Cannot read a file " + std::string(filename), + err); return TINYEXR_ERROR_CANT_OPEN_FILE; } #else FILE *fp = fopen(filename, "rb"); if (!fp) { - if (err) { - (*err) = "Cannot read file."; - } + tinyexr::SetErrorMessage("Cannot read a file " + std::string(filename), + err); return TINYEXR_ERROR_CANT_OPEN_FILE; } #endif @@ -11696,9 +12027,8 @@ int LoadDeepEXR(DeepImage *deep_image, const char *filename, const char **err) { if (filesize == 0) { fclose(fp); - if (err) { - (*err) = "File size is zero."; - } + tinyexr::SetErrorMessage("File size is zero : " + std::string(filename), + err); return TINYEXR_ERROR_INVALID_FILE; } @@ -11719,9 +12049,7 @@ int LoadDeepEXR(DeepImage *deep_image, const char *filename, const char **err) { const char header[] = {0x76, 0x2f, 0x31, 0x01}; if (memcmp(marker, header, 4) != 0) { - if (err) { - (*err) = "Invalid magic number."; - } + tinyexr::SetErrorMessage("Invalid magic number", err); return TINYEXR_ERROR_INVALID_MAGIC_NUMBER; } marker += 4; @@ -11732,9 +12060,7 @@ int LoadDeepEXR(DeepImage *deep_image, const char *filename, const char **err) { // ver 2.0, scanline, deep bit on(0x800) // must be [2, 0, 0, 0] if (marker[0] != 2 || marker[1] != 8 || marker[2] != 0 || marker[3] != 0) { - if (err) { - (*err) = "Unsupported version or scanline."; - } + tinyexr::SetErrorMessage("Unsupported version or scanline", err); return TINYEXR_ERROR_UNSUPPORTED_FORMAT; } @@ -11775,9 +12101,9 @@ int LoadDeepEXR(DeepImage *deep_image, const char *filename, const char **err) { if (attr_name.compare("compression") == 0) { compression_type = data[0]; if (compression_type > TINYEXR_COMPRESSIONTYPE_PIZ) { - if (err) { - (*err) = "Unsupported compression type."; - } + std::stringstream ss; + ss << "Unsupported compression type : " << compression_type; + tinyexr::SetErrorMessage(ss.str(), err); return TINYEXR_ERROR_UNSUPPORTED_FORMAT; } @@ -11794,18 +12120,14 @@ int LoadDeepEXR(DeepImage *deep_image, const char *filename, const char **err) { // ySampling: int if (!tinyexr::ReadChannelInfo(channels, data)) { - if (err) { - (*err) = "Failed to parse channel info."; - } + tinyexr::SetErrorMessage("Failed to parse channel info", err); return TINYEXR_ERROR_INVALID_DATA; } num_channels = static_cast<int>(channels.size()); if (num_channels < 1) { - if (err) { - (*err) = "Invalid channels format."; - } + tinyexr::SetErrorMessage("Invalid channels format", err); return TINYEXR_ERROR_INVALID_DATA; } @@ -11877,9 +12199,7 @@ int LoadDeepEXR(DeepImage *deep_image, const char *filename, const char **err) { #endif // OK } else { - if (err) { - (*err) = "Unsupported format."; - } + tinyexr::SetErrorMessage("Unsupported compression format", err); return TINYEXR_ERROR_UNSUPPORTED_FORMAT; } @@ -11936,8 +12256,9 @@ int LoadDeepEXR(DeepImage *deep_image, const char *filename, const char **err) { unsigned long dstLen = static_cast<unsigned long>(pixelOffsetTable.size() * sizeof(int)); if (!tinyexr::DecompressZip( - reinterpret_cast<unsigned char *>(&pixelOffsetTable.at(0)), &dstLen, - data_ptr + 28, static_cast<unsigned long>(packedOffsetTableSize))) { + reinterpret_cast<unsigned char *>(&pixelOffsetTable.at(0)), + &dstLen, data_ptr + 28, + static_cast<unsigned long>(packedOffsetTableSize))) { return false; } @@ -11955,9 +12276,9 @@ int LoadDeepEXR(DeepImage *deep_image, const char *filename, const char **err) { unsigned long dstLen = static_cast<unsigned long>(unpackedSampleDataSize); if (dstLen) { if (!tinyexr::DecompressZip( - reinterpret_cast<unsigned char *>(&sample_data.at(0)), &dstLen, - data_ptr + 28 + packedOffsetTableSize, - static_cast<unsigned long>(packedSampleDataSize))) { + reinterpret_cast<unsigned char *>(&sample_data.at(0)), &dstLen, + data_ptr + 28 + packedOffsetTableSize, + static_cast<unsigned long>(packedSampleDataSize))) { return false; } assert(dstLen == static_cast<unsigned long>(unpackedSampleDataSize)); @@ -12006,8 +12327,10 @@ int LoadDeepEXR(DeepImage *deep_image, const char *filename, const char **err) { if (channels[c].pixel_type == 0) { // UINT for (size_t x = 0; x < static_cast<size_t>(samples_per_line); x++) { - unsigned int ui = *reinterpret_cast<unsigned int *>( + unsigned int ui; + unsigned int *src_ptr = reinterpret_cast<unsigned int *>( &sample_data.at(size_t(data_offset) + x * sizeof(int))); + tinyexr::cpy4(&ui, src_ptr); deep_image->image[c][y][x] = static_cast<float>(ui); // @fixme } data_offset += @@ -12015,16 +12338,19 @@ int LoadDeepEXR(DeepImage *deep_image, const char *filename, const char **err) { } else if (channels[c].pixel_type == 1) { // half for (size_t x = 0; x < static_cast<size_t>(samples_per_line); x++) { tinyexr::FP16 f16; - f16.u = *reinterpret_cast<unsigned short *>( + const unsigned short *src_ptr = reinterpret_cast<unsigned short *>( &sample_data.at(size_t(data_offset) + x * sizeof(short))); + tinyexr::cpy2(&(f16.u), src_ptr); tinyexr::FP32 f32 = half_to_float(f16); deep_image->image[c][y][x] = f32.f; } data_offset += sizeof(short) * static_cast<size_t>(samples_per_line); } else { // float for (size_t x = 0; x < static_cast<size_t>(samples_per_line); x++) { - float f = *reinterpret_cast<float *>( + float f; + const float *src_ptr = reinterpret_cast<float *>( &sample_data.at(size_t(data_offset) + x * sizeof(float))); + tinyexr::cpy4(&f, src_ptr); deep_image->image[c][y][x] = f; } data_offset += sizeof(float) * static_cast<size_t>(samples_per_line); @@ -12065,6 +12391,13 @@ void InitEXRImage(EXRImage *exr_image) { exr_image->num_tiles = 0; } +void FreeEXRErrorMessage(const char *msg) { + if (msg) { + free(reinterpret_cast<void *>(const_cast<char *>(msg))); + } + return; +} + void InitEXRHeader(EXRHeader *exr_header) { if (exr_header == NULL) { return; @@ -12096,6 +12429,10 @@ int FreeEXRHeader(EXRHeader *exr_header) { } } + if (exr_header->custom_attributes) { + free(exr_header->custom_attributes); + } + return TINYEXR_SUCCESS; } @@ -12125,6 +12462,7 @@ int FreeEXRImage(EXRImage *exr_image) { free(exr_image->tiles[tid].images); } } + free(exr_image->tiles); } return TINYEXR_SUCCESS; @@ -12133,9 +12471,8 @@ int FreeEXRImage(EXRImage *exr_image) { int ParseEXRHeaderFromFile(EXRHeader *exr_header, const EXRVersion *exr_version, const char *filename, const char **err) { if (exr_header == NULL || exr_version == NULL || filename == NULL) { - if (err) { - (*err) = "Invalid argument."; - } + tinyexr::SetErrorMessage("Invalid argument for ParseEXRHeaderFromFile", + err); return TINYEXR_ERROR_INVALID_ARGUMENT; } @@ -12146,9 +12483,7 @@ int ParseEXRHeaderFromFile(EXRHeader *exr_header, const EXRVersion *exr_version, FILE *fp = fopen(filename, "rb"); #endif if (!fp) { - if (err) { - (*err) = "Cannot read file."; - } + tinyexr::SetErrorMessage("Cannot read file " + std::string(filename), err); return TINYEXR_ERROR_CANT_OPEN_FILE; } @@ -12166,9 +12501,8 @@ int ParseEXRHeaderFromFile(EXRHeader *exr_header, const EXRVersion *exr_version, fclose(fp); if (ret != filesize) { - if (err) { - (*err) = "fread error."; - } + tinyexr::SetErrorMessage("fread() error on " + std::string(filename), + err); return TINYEXR_ERROR_INVALID_FILE; } } @@ -12185,10 +12519,13 @@ int ParseEXRMultipartHeaderFromMemory(EXRHeader ***exr_headers, if (memory == NULL || exr_headers == NULL || num_headers == NULL || exr_version == NULL) { // Invalid argument + tinyexr::SetErrorMessage( + "Invalid argument for ParseEXRMultipartHeaderFromMemory", err); return TINYEXR_ERROR_INVALID_ARGUMENT; } if (size < tinyexr::kEXRVersionSize) { + tinyexr::SetErrorMessage("Data size too short", err); return TINYEXR_ERROR_INVALID_DATA; } @@ -12207,13 +12544,7 @@ int ParseEXRMultipartHeaderFromMemory(EXRHeader ***exr_headers, marker, marker_size); if (ret != TINYEXR_SUCCESS) { - if (err) { -#ifdef _WIN32 - (*err) = _strdup(err_str.c_str()); // may leak -#else - (*err) = strdup(err_str.c_str()); // may leak -#endif - } + tinyexr::SetErrorMessage(err_str, err); return ret; } @@ -12224,9 +12555,8 @@ int ParseEXRMultipartHeaderFromMemory(EXRHeader ***exr_headers, // `chunkCount` must exist in the header. if (info.chunk_count == 0) { - if (err) { - (*err) = "`chunkCount' attribute is not found in the header."; - } + tinyexr::SetErrorMessage( + "`chunkCount' attribute is not found in the header.", err); return TINYEXR_ERROR_INVALID_DATA; } @@ -12261,9 +12591,8 @@ int ParseEXRMultipartHeaderFromFile(EXRHeader ***exr_headers, int *num_headers, const char *filename, const char **err) { if (exr_headers == NULL || num_headers == NULL || exr_version == NULL || filename == NULL) { - if (err) { - (*err) = "Invalid argument."; - } + tinyexr::SetErrorMessage( + "Invalid argument for ParseEXRMultipartHeaderFromFile()", err); return TINYEXR_ERROR_INVALID_ARGUMENT; } @@ -12274,9 +12603,7 @@ int ParseEXRMultipartHeaderFromFile(EXRHeader ***exr_headers, int *num_headers, FILE *fp = fopen(filename, "rb"); #endif if (!fp) { - if (err) { - (*err) = "Cannot read file."; - } + tinyexr::SetErrorMessage("Cannot read file " + std::string(filename), err); return TINYEXR_ERROR_CANT_OPEN_FILE; } @@ -12294,9 +12621,7 @@ int ParseEXRMultipartHeaderFromFile(EXRHeader ***exr_headers, int *num_headers, fclose(fp); if (ret != filesize) { - if (err) { - (*err) = "fread error."; - } + tinyexr::SetErrorMessage("`fread' error. file may be corrupted.", err); return TINYEXR_ERROR_INVALID_FILE; } } @@ -12405,9 +12730,8 @@ int LoadEXRMultipartImageFromMemory(EXRImage *exr_images, const size_t size, const char **err) { if (exr_images == NULL || exr_headers == NULL || num_parts == 0 || memory == NULL || (size <= tinyexr::kEXRVersionSize)) { - if (err) { - (*err) = "Invalid argument."; - } + tinyexr::SetErrorMessage( + "Invalid argument for LoadEXRMultipartImageFromMemory()", err); return TINYEXR_ERROR_INVALID_ARGUMENT; } @@ -12415,9 +12739,7 @@ int LoadEXRMultipartImageFromMemory(EXRImage *exr_images, size_t total_header_size = 0; for (unsigned int i = 0; i < num_parts; i++) { if (exr_headers[i]->header_len == 0) { - if (err) { - (*err) = "EXRHeader is not initialized."; - } + tinyexr::SetErrorMessage("EXRHeader variable is not initialized.", err); return TINYEXR_ERROR_INVALID_ARGUMENT; } @@ -12452,9 +12774,8 @@ int LoadEXRMultipartImageFromMemory(EXRImage *exr_images, tinyexr::swap8(&offset); if (offset >= size) { - if (err) { - (*err) = "Invalid offset size."; - } + tinyexr::SetErrorMessage("Invalid offset size in EXR header chunks.", + err); return TINYEXR_ERROR_INVALID_DATA; } @@ -12479,14 +12800,19 @@ int LoadEXRMultipartImageFromMemory(EXRImage *exr_images, tinyexr::swap4(&part_no); if (part_no != i) { - assert(0); + tinyexr::SetErrorMessage("Invalid `part number' in EXR header chunks.", + err); return TINYEXR_ERROR_INVALID_DATA; } } + std::string e; int ret = tinyexr::DecodeChunk(&exr_images[i], exr_headers[i], offset_table, - memory, size); + memory, size, &e); if (ret != TINYEXR_SUCCESS) { + if (!e.empty()) { + tinyexr::SetErrorMessage(e, err); + } return ret; } } @@ -12499,9 +12825,8 @@ int LoadEXRMultipartImageFromFile(EXRImage *exr_images, unsigned int num_parts, const char *filename, const char **err) { if (exr_images == NULL || exr_headers == NULL || num_parts == 0) { - if (err) { - (*err) = "Invalid argument."; - } + tinyexr::SetErrorMessage( + "Invalid argument for LoadEXRMultipartImageFromFile", err); return TINYEXR_ERROR_INVALID_ARGUMENT; } @@ -12512,9 +12837,7 @@ int LoadEXRMultipartImageFromFile(EXRImage *exr_images, FILE *fp = fopen(filename, "rb"); #endif if (!fp) { - if (err) { - (*err) = "Cannot read file."; - } + tinyexr::SetErrorMessage("Cannot read file " + std::string(filename), err); return TINYEXR_ERROR_CANT_OPEN_FILE; } @@ -12670,5 +12993,10 @@ int SaveEXR(const float *data, int width, int height, int components, return ret; } +#ifdef __clang__ +// zero-as-null-ppinter-constant +#pragma clang diagnostic pop +#endif + #endif // TINYEXR_IMPLEMENTATION_DEIFNED #endif // TINYEXR_IMPLEMENTATION diff --git a/thirdparty/xatlas/xatlas.cpp b/thirdparty/xatlas/xatlas.cpp new file mode 100644 index 0000000000..f6a9ce64dc --- /dev/null +++ b/thirdparty/xatlas/xatlas.cpp @@ -0,0 +1,7384 @@ +// This code is in the public domain -- castanyo@yahoo.es +#include "xatlas.h" +#include <assert.h> +#include <float.h> +#include <math.h> +#include <stdarg.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <algorithm> +#include <cmath> +#include <memory> +#include <unordered_map> +#include <vector> + +#undef min +#undef max + +#ifndef xaAssert +#define xaAssert(exp) \ + if (!(exp)) { \ + xaPrint("%s %s %s\n", #exp, __FILE__, __LINE__); \ + } +#endif +#ifndef xaDebugAssert +#define xaDebugAssert(exp) assert(exp) +#endif +#ifndef xaPrint +#define xaPrint(...) \ + if (xatlas::internal::s_print) { \ + xatlas::internal::s_print(__VA_ARGS__); \ + } +#endif + +#ifdef _MSC_VER +// Ignore gcc attributes. +#define __attribute__(X) +#endif + +#ifdef _MSC_VER +#define restrict +#define NV_FORCEINLINE __forceinline +#else +#define restrict __restrict__ +#define NV_FORCEINLINE __attribute__((always_inline)) inline +#endif + +#define NV_UINT32_MAX 0xffffffff +#define NV_FLOAT_MAX 3.402823466e+38F + +#ifndef PI +#define PI float(3.1415926535897932384626433833) +#endif + +#define NV_EPSILON (0.0001f) +#define NV_NORMAL_EPSILON (0.001f) + +namespace xatlas { +namespace internal { + +static PrintFunc s_print = NULL; + +static int align(int x, int a) { + return (x + a - 1) & ~(a - 1); +} + +static bool isAligned(int x, int a) { + return (x & (a - 1)) == 0; +} + +/// Return the maximum of the three arguments. +template <typename T> +static T max3(const T &a, const T &b, const T &c) { + return std::max(a, std::max(b, c)); +} + +/// Return the maximum of the three arguments. +template <typename T> +static T min3(const T &a, const T &b, const T &c) { + return std::min(a, std::min(b, c)); +} + +/// Clamp between two values. +template <typename T> +static T clamp(const T &x, const T &a, const T &b) { + return std::min(std::max(x, a), b); +} + +static float saturate(float f) { + return clamp(f, 0.0f, 1.0f); +} + +// Robust floating point comparisons: +// http://realtimecollisiondetection.net/blog/?p=89 +static bool equal(const float f0, const float f1, const float epsilon = NV_EPSILON) { + //return fabs(f0-f1) <= epsilon; + return fabs(f0 - f1) <= epsilon * max3(1.0f, fabsf(f0), fabsf(f1)); +} + +NV_FORCEINLINE static int ftoi_floor(float val) { + return (int)val; +} + +NV_FORCEINLINE static int ftoi_ceil(float val) { + return (int)ceilf(val); +} + +NV_FORCEINLINE static int ftoi_round(float f) { + return int(floorf(f + 0.5f)); +} + +static bool isZero(const float f, const float epsilon = NV_EPSILON) { + return fabs(f) <= epsilon; +} + +static float lerp(float f0, float f1, float t) { + const float s = 1.0f - t; + return f0 * s + f1 * t; +} + +static float square(float f) { + return f * f; +} + +static int square(int i) { + return i * i; +} + +/** Return the next power of two. +* @see http://graphics.stanford.edu/~seander/bithacks.html +* @warning Behaviour for 0 is undefined. +* @note isPowerOfTwo(x) == true -> nextPowerOfTwo(x) == x +* @note nextPowerOfTwo(x) = 2 << log2(x-1) +*/ +static uint32_t nextPowerOfTwo(uint32_t x) { + xaDebugAssert(x != 0); + // On modern CPUs this is supposed to be as fast as using the bsr instruction. + x--; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + return x + 1; +} + +static uint64_t nextPowerOfTwo(uint64_t x) { + xaDebugAssert(x != 0); + uint32_t p = 1; + while (x > p) { + p += p; + } + return p; +} + +static uint32_t sdbmHash(const void *data_in, uint32_t size, uint32_t h = 5381) { + const uint8_t *data = (const uint8_t *)data_in; + uint32_t i = 0; + while (i < size) { + h = (h << 16) + (h << 6) - h + (uint32_t)data[i++]; + } + return h; +} + +// Note that this hash does not handle NaN properly. +static uint32_t sdbmFloatHash(const float *f, uint32_t count, uint32_t h = 5381) { + for (uint32_t i = 0; i < count; i++) { + union { + float f; + uint32_t i; + } x = { f[i] }; + if (x.i == 0x80000000) x.i = 0; + h = sdbmHash(&x, 4, h); + } + return h; +} + +template <typename T> +static uint32_t hash(const T &t, uint32_t h = 5381) { + return sdbmHash(&t, sizeof(T), h); +} + +static uint32_t hash(const float &f, uint32_t h) { + return sdbmFloatHash(&f, 1, h); +} + +// Functors for hash table: +template <typename Key> +struct Hash { + uint32_t operator()(const Key &k) const { return hash(k); } +}; + +template <typename Key> +struct Equal { + bool operator()(const Key &k0, const Key &k1) const { return k0 == k1; } +}; + +class Vector2 { +public: + typedef Vector2 const &Arg; + + Vector2() {} + explicit Vector2(float f) : + x(f), + y(f) {} + Vector2(float x, float y) : + x(x), + y(y) {} + Vector2(Vector2::Arg v) : + x(v.x), + y(v.y) {} + + const Vector2 &operator=(Vector2::Arg v) { + x = v.x; + y = v.y; + return *this; + } + const float *ptr() const { return &x; } + + void set(float _x, float _y) { + x = _x; + y = _y; + } + + Vector2 operator-() const { + return Vector2(-x, -y); + } + + void operator+=(Vector2::Arg v) { + x += v.x; + y += v.y; + } + + void operator-=(Vector2::Arg v) { + x -= v.x; + y -= v.y; + } + + void operator*=(float s) { + x *= s; + y *= s; + } + + void operator*=(Vector2::Arg v) { + x *= v.x; + y *= v.y; + } + + friend bool operator==(Vector2::Arg a, Vector2::Arg b) { + return a.x == b.x && a.y == b.y; + } + + friend bool operator!=(Vector2::Arg a, Vector2::Arg b) { + return a.x != b.x || a.y != b.y; + } + + union { +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4201) +#endif + struct + { + float x, y; + }; +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + float component[2]; + }; +}; + +Vector2 operator+(Vector2::Arg a, Vector2::Arg b) { + return Vector2(a.x + b.x, a.y + b.y); +} + +Vector2 operator-(Vector2::Arg a, Vector2::Arg b) { + return Vector2(a.x - b.x, a.y - b.y); +} + +Vector2 operator*(Vector2::Arg v, float s) { + return Vector2(v.x * s, v.y * s); +} + +Vector2 operator*(Vector2::Arg v1, Vector2::Arg v2) { + return Vector2(v1.x * v2.x, v1.y * v2.y); +} + +Vector2 operator/(Vector2::Arg v, float s) { + return Vector2(v.x / s, v.y / s); +} + +Vector2 lerp(Vector2::Arg v1, Vector2::Arg v2, float t) { + const float s = 1.0f - t; + return Vector2(v1.x * s + t * v2.x, v1.y * s + t * v2.y); +} + +float dot(Vector2::Arg a, Vector2::Arg b) { + return a.x * b.x + a.y * b.y; +} + +float lengthSquared(Vector2::Arg v) { + return v.x * v.x + v.y * v.y; +} + +float length(Vector2::Arg v) { + return sqrtf(lengthSquared(v)); +} + +float distance(Vector2::Arg a, Vector2::Arg b) { + return length(a - b); +} + +bool isNormalized(Vector2::Arg v, float epsilon = NV_NORMAL_EPSILON) { + return equal(length(v), 1, epsilon); +} + +Vector2 normalize(Vector2::Arg v, float epsilon = NV_EPSILON) { + float l = length(v); + xaDebugAssert(!isZero(l, epsilon)); +#ifdef NDEBUG + epsilon = 0; // silence unused parameter warning +#endif + Vector2 n = v * (1.0f / l); + xaDebugAssert(isNormalized(n)); + return n; +} + +Vector2 normalizeSafe(Vector2::Arg v, Vector2::Arg fallback, float epsilon = NV_EPSILON) { + float l = length(v); + if (isZero(l, epsilon)) { + return fallback; + } + return v * (1.0f / l); +} + +bool equal(Vector2::Arg v1, Vector2::Arg v2, float epsilon = NV_EPSILON) { + return equal(v1.x, v2.x, epsilon) && equal(v1.y, v2.y, epsilon); +} + +Vector2 max(Vector2::Arg a, Vector2::Arg b) { + return Vector2(std::max(a.x, b.x), std::max(a.y, b.y)); +} + +bool isFinite(Vector2::Arg v) { + return std::isfinite(v.x) && std::isfinite(v.y); +} + +// Note, this is the area scaled by 2! +float triangleArea(Vector2::Arg v0, Vector2::Arg v1) { + return (v0.x * v1.y - v0.y * v1.x); // * 0.5f; +} +float triangleArea(Vector2::Arg a, Vector2::Arg b, Vector2::Arg c) { + // IC: While it may be appealing to use the following expression: + //return (c.x * a.y + a.x * b.y + b.x * c.y - b.x * a.y - c.x * b.y - a.x * c.y); // * 0.5f; + // That's actually a terrible idea. Small triangles far from the origin can end up producing fairly large floating point + // numbers and the results becomes very unstable and dependent on the order of the factors. + // Instead, it's preferable to subtract the vertices first, and multiply the resulting small values together. The result + // in this case is always much more accurate (as long as the triangle is small) and less dependent of the location of + // the triangle. + //return ((a.x - c.x) * (b.y - c.y) - (a.y - c.y) * (b.x - c.x)); // * 0.5f; + return triangleArea(a - c, b - c); +} + +float triangleArea2(Vector2::Arg v1, Vector2::Arg v2, Vector2::Arg v3) { + return 0.5f * (v3.x * v1.y + v1.x * v2.y + v2.x * v3.y - v2.x * v1.y - v3.x * v2.y - v1.x * v3.y); +} + +static uint32_t hash(const Vector2 &v, uint32_t h) { + return sdbmFloatHash(v.component, 2, h); +} + +class Vector3 { +public: + typedef Vector3 const &Arg; + + Vector3() {} + explicit Vector3(float f) : + x(f), + y(f), + z(f) {} + Vector3(float x, float y, float z) : + x(x), + y(y), + z(z) {} + Vector3(Vector2::Arg v, float z) : + x(v.x), + y(v.y), + z(z) {} + Vector3(Vector3::Arg v) : + x(v.x), + y(v.y), + z(v.z) {} + + const Vector3 &operator=(Vector3::Arg v) { + x = v.x; + y = v.y; + z = v.z; + return *this; + } + + Vector2 xy() const { + return Vector2(x, y); + } + + const float *ptr() const { return &x; } + + void set(float _x, float _y, float _z) { + x = _x; + y = _y; + z = _z; + } + + Vector3 operator-() const { + return Vector3(-x, -y, -z); + } + + void operator+=(Vector3::Arg v) { + x += v.x; + y += v.y; + z += v.z; + } + + void operator-=(Vector3::Arg v) { + x -= v.x; + y -= v.y; + z -= v.z; + } + + void operator*=(float s) { + x *= s; + y *= s; + z *= s; + } + + void operator/=(float s) { + float is = 1.0f / s; + x *= is; + y *= is; + z *= is; + } + + void operator*=(Vector3::Arg v) { + x *= v.x; + y *= v.y; + z *= v.z; + } + + void operator/=(Vector3::Arg v) { + x /= v.x; + y /= v.y; + z /= v.z; + } + + friend bool operator==(Vector3::Arg a, Vector3::Arg b) { + return a.x == b.x && a.y == b.y && a.z == b.z; + } + + friend bool operator!=(Vector3::Arg a, Vector3::Arg b) { + return a.x != b.x || a.y != b.y || a.z != b.z; + } + + union { +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4201) +#endif + struct + { + float x, y, z; + }; +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + float component[3]; + }; +}; + +Vector3 add(Vector3::Arg a, Vector3::Arg b) { + return Vector3(a.x + b.x, a.y + b.y, a.z + b.z); +} +Vector3 add(Vector3::Arg a, float b) { + return Vector3(a.x + b, a.y + b, a.z + b); +} +Vector3 operator+(Vector3::Arg a, Vector3::Arg b) { + return add(a, b); +} +Vector3 operator+(Vector3::Arg a, float b) { + return add(a, b); +} + +Vector3 sub(Vector3::Arg a, Vector3::Arg b) { + return Vector3(a.x - b.x, a.y - b.y, a.z - b.z); +} + +Vector3 sub(Vector3::Arg a, float b) { + return Vector3(a.x - b, a.y - b, a.z - b); +} + +Vector3 operator-(Vector3::Arg a, Vector3::Arg b) { + return sub(a, b); +} + +Vector3 operator-(Vector3::Arg a, float b) { + return sub(a, b); +} + +Vector3 cross(Vector3::Arg a, Vector3::Arg b) { + return Vector3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x); +} + +Vector3 operator*(Vector3::Arg v, float s) { + return Vector3(v.x * s, v.y * s, v.z * s); +} + +Vector3 operator*(float s, Vector3::Arg v) { + return Vector3(v.x * s, v.y * s, v.z * s); +} + +Vector3 operator*(Vector3::Arg v, Vector3::Arg s) { + return Vector3(v.x * s.x, v.y * s.y, v.z * s.z); +} + +Vector3 operator/(Vector3::Arg v, float s) { + return v * (1.0f / s); +} + +Vector3 lerp(Vector3::Arg v1, Vector3::Arg v2, float t) { + const float s = 1.0f - t; + return Vector3(v1.x * s + t * v2.x, v1.y * s + t * v2.y, v1.z * s + t * v2.z); +} + +float dot(Vector3::Arg a, Vector3::Arg b) { + return a.x * b.x + a.y * b.y + a.z * b.z; +} + +float lengthSquared(Vector3::Arg v) { + return v.x * v.x + v.y * v.y + v.z * v.z; +} + +float length(Vector3::Arg v) { + return sqrtf(lengthSquared(v)); +} + +float distance(Vector3::Arg a, Vector3::Arg b) { + return length(a - b); +} + +float distanceSquared(Vector3::Arg a, Vector3::Arg b) { + return lengthSquared(a - b); +} + +bool isNormalized(Vector3::Arg v, float epsilon = NV_NORMAL_EPSILON) { + return equal(length(v), 1, epsilon); +} + +Vector3 normalize(Vector3::Arg v, float epsilon = NV_EPSILON) { + float l = length(v); + xaDebugAssert(!isZero(l, epsilon)); +#ifdef NDEBUG + epsilon = 0; // silence unused parameter warning +#endif + Vector3 n = v * (1.0f / l); + xaDebugAssert(isNormalized(n)); + return n; +} + +Vector3 normalizeSafe(Vector3::Arg v, Vector3::Arg fallback, float epsilon = NV_EPSILON) { + float l = length(v); + if (isZero(l, epsilon)) { + return fallback; + } + return v * (1.0f / l); +} + +bool equal(Vector3::Arg v1, Vector3::Arg v2, float epsilon = NV_EPSILON) { + return equal(v1.x, v2.x, epsilon) && equal(v1.y, v2.y, epsilon) && equal(v1.z, v2.z, epsilon); +} + +Vector3 min(Vector3::Arg a, Vector3::Arg b) { + return Vector3(std::min(a.x, b.x), std::min(a.y, b.y), std::min(a.z, b.z)); +} + +Vector3 max(Vector3::Arg a, Vector3::Arg b) { + return Vector3(std::max(a.x, b.x), std::max(a.y, b.y), std::max(a.z, b.z)); +} + +Vector3 clamp(Vector3::Arg v, float min, float max) { + return Vector3(clamp(v.x, min, max), clamp(v.y, min, max), clamp(v.z, min, max)); +} + +Vector3 saturate(Vector3::Arg v) { + return Vector3(saturate(v.x), saturate(v.y), saturate(v.z)); +} + +Vector3 floor(Vector3::Arg v) { + return Vector3(floorf(v.x), floorf(v.y), floorf(v.z)); +} + +bool isFinite(Vector3::Arg v) { + return std::isfinite(v.x) && std::isfinite(v.y) && std::isfinite(v.z); +} + +static uint32_t hash(const Vector3 &v, uint32_t h) { + return sdbmFloatHash(v.component, 3, h); +} + +/// Basis class to compute tangent space basis, ortogonalizations and to +/// transform vectors from one space to another. +class Basis { +public: + /// Create a null basis. + Basis() : + tangent(0, 0, 0), + bitangent(0, 0, 0), + normal(0, 0, 0) {} + + void buildFrameForDirection(Vector3::Arg d, float angle = 0) { + xaAssert(isNormalized(d)); + normal = d; + // Choose minimum axis. + if (fabsf(normal.x) < fabsf(normal.y) && fabsf(normal.x) < fabsf(normal.z)) { + tangent = Vector3(1, 0, 0); + } else if (fabsf(normal.y) < fabsf(normal.z)) { + tangent = Vector3(0, 1, 0); + } else { + tangent = Vector3(0, 0, 1); + } + // Ortogonalize + tangent -= normal * dot(normal, tangent); + tangent = normalize(tangent); + bitangent = cross(normal, tangent); + // Rotate frame around normal according to angle. + if (angle != 0.0f) { + float c = cosf(angle); + float s = sinf(angle); + Vector3 tmp = c * tangent - s * bitangent; + bitangent = s * tangent + c * bitangent; + tangent = tmp; + } + } + + Vector3 tangent; + Vector3 bitangent; + Vector3 normal; +}; + +// Simple bit array. +class BitArray { +public: + BitArray() : + m_size(0) {} + BitArray(uint32_t sz) { + resize(sz); + } + + uint32_t size() const { + return m_size; + } + + void clear() { + resize(0); + } + + void resize(uint32_t new_size) { + m_size = new_size; + m_wordArray.resize((m_size + 31) >> 5); + } + + /// Get bit. + bool bitAt(uint32_t b) const { + xaDebugAssert(b < m_size); + return (m_wordArray[b >> 5] & (1 << (b & 31))) != 0; + } + + // Set a bit. + void setBitAt(uint32_t idx) { + xaDebugAssert(idx < m_size); + m_wordArray[idx >> 5] |= (1 << (idx & 31)); + } + + // Toggle a bit. + void toggleBitAt(uint32_t idx) { + xaDebugAssert(idx < m_size); + m_wordArray[idx >> 5] ^= (1 << (idx & 31)); + } + + // Set a bit to the given value. @@ Rename modifyBitAt? + void setBitAt(uint32_t idx, bool b) { + xaDebugAssert(idx < m_size); + m_wordArray[idx >> 5] = setBits(m_wordArray[idx >> 5], 1 << (idx & 31), b); + xaDebugAssert(bitAt(idx) == b); + } + + // Clear all the bits. + void clearAll() { + memset(m_wordArray.data(), 0, m_wordArray.size() * sizeof(uint32_t)); + } + + // Set all the bits. + void setAll() { + memset(m_wordArray.data(), 0xFF, m_wordArray.size() * sizeof(uint32_t)); + } + +private: + // See "Conditionally set or clear bits without branching" at http://graphics.stanford.edu/~seander/bithacks.html + uint32_t setBits(uint32_t w, uint32_t m, bool b) { + return (w & ~m) | (-int(b) & m); + } + + // Number of bits stored. + uint32_t m_size; + + // Array of bits. + std::vector<uint32_t> m_wordArray; +}; + +/// Bit map. This should probably be called BitImage. +class BitMap { +public: + BitMap() : + m_width(0), + m_height(0) {} + BitMap(uint32_t w, uint32_t h) : + m_width(w), + m_height(h), + m_bitArray(w * h) {} + + uint32_t width() const { + return m_width; + } + uint32_t height() const { + return m_height; + } + + void resize(uint32_t w, uint32_t h, bool initValue) { + BitArray tmp(w * h); + if (initValue) + tmp.setAll(); + else + tmp.clearAll(); + // @@ Copying one bit at a time. This could be much faster. + for (uint32_t y = 0; y < m_height; y++) { + for (uint32_t x = 0; x < m_width; x++) { + //tmp.setBitAt(y*w + x, bitAt(x, y)); + if (bitAt(x, y) != initValue) tmp.toggleBitAt(y * w + x); + } + } + std::swap(m_bitArray, tmp); + m_width = w; + m_height = h; + } + + bool bitAt(uint32_t x, uint32_t y) const { + xaDebugAssert(x < m_width && y < m_height); + return m_bitArray.bitAt(y * m_width + x); + } + + void setBitAt(uint32_t x, uint32_t y) { + xaDebugAssert(x < m_width && y < m_height); + m_bitArray.setBitAt(y * m_width + x); + } + + void clearAll() { + m_bitArray.clearAll(); + } + +private: + uint32_t m_width; + uint32_t m_height; + BitArray m_bitArray; +}; + +// Axis Aligned Bounding Box. +class Box { +public: + Box() {} + Box(const Box &b) : + minCorner(b.minCorner), + maxCorner(b.maxCorner) {} + Box(const Vector3 &mins, const Vector3 &maxs) : + minCorner(mins), + maxCorner(maxs) {} + + operator const float *() const { + return reinterpret_cast<const float *>(this); + } + + // Clear the bounds. + void clearBounds() { + minCorner.set(FLT_MAX, FLT_MAX, FLT_MAX); + maxCorner.set(-FLT_MAX, -FLT_MAX, -FLT_MAX); + } + + // Return extents of the box. + Vector3 extents() const { + return (maxCorner - minCorner) * 0.5f; + } + + // Add a point to this box. + void addPointToBounds(const Vector3 &p) { + minCorner = min(minCorner, p); + maxCorner = max(maxCorner, p); + } + + // Get the volume of the box. + float volume() const { + Vector3 d = extents(); + return 8.0f * (d.x * d.y * d.z); + } + + Vector3 minCorner; + Vector3 maxCorner; +}; + +class Fit { +public: + static Vector3 computeCentroid(int n, const Vector3 *__restrict points) { + Vector3 centroid(0.0f); + for (int i = 0; i < n; i++) { + centroid += points[i]; + } + centroid /= float(n); + return centroid; + } + + static Vector3 computeCovariance(int n, const Vector3 *__restrict points, float *__restrict covariance) { + // compute the centroid + Vector3 centroid = computeCentroid(n, points); + // compute covariance matrix + for (int i = 0; i < 6; i++) { + covariance[i] = 0.0f; + } + for (int i = 0; i < n; i++) { + Vector3 v = points[i] - centroid; + covariance[0] += v.x * v.x; + covariance[1] += v.x * v.y; + covariance[2] += v.x * v.z; + covariance[3] += v.y * v.y; + covariance[4] += v.y * v.z; + covariance[5] += v.z * v.z; + } + return centroid; + } + + static bool isPlanar(int n, const Vector3 *points, float epsilon = NV_EPSILON) { + // compute the centroid and covariance + float matrix[6]; + computeCovariance(n, points, matrix); + float eigenValues[3]; + Vector3 eigenVectors[3]; + if (!eigenSolveSymmetric3(matrix, eigenValues, eigenVectors)) { + return false; + } + return eigenValues[2] < epsilon; + } + + // Tridiagonal solver from Charles Bloom. + // Householder transforms followed by QL decomposition. + // Seems to be based on the code from Numerical Recipes in C. + static bool eigenSolveSymmetric3(const float matrix[6], float eigenValues[3], Vector3 eigenVectors[3]) { + xaDebugAssert(matrix != NULL && eigenValues != NULL && eigenVectors != NULL); + float subd[3]; + float diag[3]; + float work[3][3]; + work[0][0] = matrix[0]; + work[0][1] = work[1][0] = matrix[1]; + work[0][2] = work[2][0] = matrix[2]; + work[1][1] = matrix[3]; + work[1][2] = work[2][1] = matrix[4]; + work[2][2] = matrix[5]; + EigenSolver3_Tridiagonal(work, diag, subd); + if (!EigenSolver3_QLAlgorithm(work, diag, subd)) { + for (int i = 0; i < 3; i++) { + eigenValues[i] = 0; + eigenVectors[i] = Vector3(0); + } + return false; + } + for (int i = 0; i < 3; i++) { + eigenValues[i] = (float)diag[i]; + } + // eigenvectors are the columns; make them the rows : + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + eigenVectors[j].component[i] = (float)work[i][j]; + } + } + // shuffle to sort by singular value : + if (eigenValues[2] > eigenValues[0] && eigenValues[2] > eigenValues[1]) { + std::swap(eigenValues[0], eigenValues[2]); + std::swap(eigenVectors[0], eigenVectors[2]); + } + if (eigenValues[1] > eigenValues[0]) { + std::swap(eigenValues[0], eigenValues[1]); + std::swap(eigenVectors[0], eigenVectors[1]); + } + if (eigenValues[2] > eigenValues[1]) { + std::swap(eigenValues[1], eigenValues[2]); + std::swap(eigenVectors[1], eigenVectors[2]); + } + xaDebugAssert(eigenValues[0] >= eigenValues[1] && eigenValues[0] >= eigenValues[2]); + xaDebugAssert(eigenValues[1] >= eigenValues[2]); + return true; + } + +private: + static void EigenSolver3_Tridiagonal(float mat[3][3], float *diag, float *subd) { + // Householder reduction T = Q^t M Q + // Input: + // mat, symmetric 3x3 matrix M + // Output: + // mat, orthogonal matrix Q + // diag, diagonal entries of T + // subd, subdiagonal entries of T (T is symmetric) + const float epsilon = 1e-08f; + float a = mat[0][0]; + float b = mat[0][1]; + float c = mat[0][2]; + float d = mat[1][1]; + float e = mat[1][2]; + float f = mat[2][2]; + diag[0] = a; + subd[2] = 0.f; + if (fabsf(c) >= epsilon) { + const float ell = sqrtf(b * b + c * c); + b /= ell; + c /= ell; + const float q = 2 * b * e + c * (f - d); + diag[1] = d + c * q; + diag[2] = f - c * q; + subd[0] = ell; + subd[1] = e - b * q; + mat[0][0] = 1; + mat[0][1] = 0; + mat[0][2] = 0; + mat[1][0] = 0; + mat[1][1] = b; + mat[1][2] = c; + mat[2][0] = 0; + mat[2][1] = c; + mat[2][2] = -b; + } else { + diag[1] = d; + diag[2] = f; + subd[0] = b; + subd[1] = e; + mat[0][0] = 1; + mat[0][1] = 0; + mat[0][2] = 0; + mat[1][0] = 0; + mat[1][1] = 1; + mat[1][2] = 0; + mat[2][0] = 0; + mat[2][1] = 0; + mat[2][2] = 1; + } + } + + static bool EigenSolver3_QLAlgorithm(float mat[3][3], float *diag, float *subd) { + // QL iteration with implicit shifting to reduce matrix from tridiagonal + // to diagonal + const int maxiter = 32; + for (int ell = 0; ell < 3; ell++) { + int iter; + for (iter = 0; iter < maxiter; iter++) { + int m; + for (m = ell; m <= 1; m++) { + float dd = fabsf(diag[m]) + fabsf(diag[m + 1]); + if (fabsf(subd[m]) + dd == dd) + break; + } + if (m == ell) + break; + float g = (diag[ell + 1] - diag[ell]) / (2 * subd[ell]); + float r = sqrtf(g * g + 1); + if (g < 0) + g = diag[m] - diag[ell] + subd[ell] / (g - r); + else + g = diag[m] - diag[ell] + subd[ell] / (g + r); + float s = 1, c = 1, p = 0; + for (int i = m - 1; i >= ell; i--) { + float f = s * subd[i], b = c * subd[i]; + if (fabsf(f) >= fabsf(g)) { + c = g / f; + r = sqrtf(c * c + 1); + subd[i + 1] = f * r; + c *= (s = 1 / r); + } else { + s = f / g; + r = sqrtf(s * s + 1); + subd[i + 1] = g * r; + s *= (c = 1 / r); + } + g = diag[i + 1] - p; + r = (diag[i] - g) * s + 2 * b * c; + p = s * r; + diag[i + 1] = g + p; + g = c * r - b; + for (int k = 0; k < 3; k++) { + f = mat[k][i + 1]; + mat[k][i + 1] = s * mat[k][i] + c * f; + mat[k][i] = c * mat[k][i] - s * f; + } + } + diag[ell] -= p; + subd[ell] = g; + subd[m] = 0; + } + if (iter == maxiter) + // should not get here under normal circumstances + return false; + } + return true; + } +}; + +/// Fixed size vector class. +class FullVector { +public: + FullVector(uint32_t dim) { m_array.resize(dim); } + FullVector(const FullVector &v) : + m_array(v.m_array) {} + + const FullVector &operator=(const FullVector &v) { + xaAssert(dimension() == v.dimension()); + m_array = v.m_array; + return *this; + } + + uint32_t dimension() const { return m_array.size(); } + const float &operator[](uint32_t index) const { return m_array[index]; } + float &operator[](uint32_t index) { return m_array[index]; } + + void fill(float f) { + const uint32_t dim = dimension(); + for (uint32_t i = 0; i < dim; i++) { + m_array[i] = f; + } + } + + void operator+=(const FullVector &v) { + xaDebugAssert(dimension() == v.dimension()); + const uint32_t dim = dimension(); + for (uint32_t i = 0; i < dim; i++) { + m_array[i] += v.m_array[i]; + } + } + + void operator-=(const FullVector &v) { + xaDebugAssert(dimension() == v.dimension()); + const uint32_t dim = dimension(); + for (uint32_t i = 0; i < dim; i++) { + m_array[i] -= v.m_array[i]; + } + } + + void operator*=(const FullVector &v) { + xaDebugAssert(dimension() == v.dimension()); + const uint32_t dim = dimension(); + for (uint32_t i = 0; i < dim; i++) { + m_array[i] *= v.m_array[i]; + } + } + + void operator+=(float f) { + const uint32_t dim = dimension(); + for (uint32_t i = 0; i < dim; i++) { + m_array[i] += f; + } + } + + void operator-=(float f) { + const uint32_t dim = dimension(); + for (uint32_t i = 0; i < dim; i++) { + m_array[i] -= f; + } + } + + void operator*=(float f) { + const uint32_t dim = dimension(); + for (uint32_t i = 0; i < dim; i++) { + m_array[i] *= f; + } + } + +private: + std::vector<float> m_array; +}; + +namespace halfedge { +class Face; +class Vertex; + +class Edge { +public: + uint32_t id; + Edge *next; + Edge *prev; // This is not strictly half-edge, but makes algorithms easier and faster. + Edge *pair; + Vertex *vertex; + Face *face; + + // Default constructor. + Edge(uint32_t id) : + id(id), + next(NULL), + prev(NULL), + pair(NULL), + vertex(NULL), + face(NULL) {} + + // Vertex queries. + const Vertex *from() const { + return vertex; + } + + Vertex *from() { + return vertex; + } + + const Vertex *to() const { + return pair->vertex; // This used to be 'next->vertex', but that changed often when the connectivity of the mesh changes. + } + + Vertex *to() { + return pair->vertex; + } + + // Edge queries. + void setNext(Edge *e) { + next = e; + if (e != NULL) e->prev = this; + } + void setPrev(Edge *e) { + prev = e; + if (e != NULL) e->next = this; + } + + // @@ It would be more simple to only check m_pair == NULL + // Face queries. + bool isBoundary() const { + return !(face && pair->face); + } + + // @@ This is not exactly accurate, we should compare the texture coordinates... + bool isSeam() const { + return vertex != pair->next->vertex || next->vertex != pair->vertex; + } + + bool isNormalSeam() const; + bool isTextureSeam() const; + + bool isValid() const { + // null face is OK. + if (next == NULL || prev == NULL || pair == NULL || vertex == NULL) return false; + if (next->prev != this) return false; + if (prev->next != this) return false; + if (pair->pair != this) return false; + return true; + } + + float length() const; + + // Return angle between this edge and the previous one. + float angle() const; +}; + +class Vertex { +public: + uint32_t id; + uint32_t original_id; + Edge *edge; + Vertex *next; + Vertex *prev; + Vector3 pos; + Vector3 nor; + Vector2 tex; + + Vertex(uint32_t id) : + id(id), + original_id(id), + edge(NULL), + pos(0.0f), + nor(0.0f), + tex(0.0f) { + next = this; + prev = this; + } + + // Set first edge of all colocals. + void setEdge(Edge *e) { + for (VertexIterator it(colocals()); !it.isDone(); it.advance()) { + it.current()->edge = e; + } + } + + // Update position of all colocals. + void setPos(const Vector3 &p) { + for (VertexIterator it(colocals()); !it.isDone(); it.advance()) { + it.current()->pos = p; + } + } + + bool isFirstColocal() const { + return firstColocal() == this; + } + + const Vertex *firstColocal() const { + uint32_t firstId = id; + const Vertex *vertex = this; + for (ConstVertexIterator it(colocals()); !it.isDone(); it.advance()) { + if (it.current()->id < firstId) { + firstId = vertex->id; + vertex = it.current(); + } + } + return vertex; + } + + Vertex *firstColocal() { + Vertex *vertex = this; + uint32_t firstId = id; + for (VertexIterator it(colocals()); !it.isDone(); it.advance()) { + if (it.current()->id < firstId) { + firstId = vertex->id; + vertex = it.current(); + } + } + return vertex; + } + + bool isColocal(const Vertex *v) const { + if (this == v) return true; + if (pos != v->pos) return false; + for (ConstVertexIterator it(colocals()); !it.isDone(); it.advance()) { + if (v == it.current()) { + return true; + } + } + return false; + } + + void linkColocal(Vertex *v) { + next->prev = v; + v->next = next; + next = v; + v->prev = this; + } + void unlinkColocal() { + next->prev = prev; + prev->next = next; + next = this; + prev = this; + } + + // @@ Note: This only works if linkBoundary has been called. + bool isBoundary() const { + return (edge && !edge->face); + } + + // Iterator that visits the edges around this vertex in counterclockwise order. + class EdgeIterator //: public Iterator<Edge *> + { + public: + EdgeIterator(Edge *e) : + m_end(NULL), + m_current(e) {} + + virtual void advance() { + if (m_end == NULL) m_end = m_current; + m_current = m_current->pair->next; + //m_current = m_current->prev->pair; + } + + virtual bool isDone() const { + return m_end == m_current; + } + virtual Edge *current() const { + return m_current; + } + Vertex *vertex() const { + return m_current->vertex; + } + + private: + Edge *m_end; + Edge *m_current; + }; + + EdgeIterator edges() { + return EdgeIterator(edge); + } + EdgeIterator edges(Edge *e) { + return EdgeIterator(e); + } + + // Iterator that visits the edges around this vertex in counterclockwise order. + class ConstEdgeIterator //: public Iterator<Edge *> + { + public: + ConstEdgeIterator(const Edge *e) : + m_end(NULL), + m_current(e) {} + ConstEdgeIterator(EdgeIterator it) : + m_end(NULL), + m_current(it.current()) {} + + virtual void advance() { + if (m_end == NULL) m_end = m_current; + m_current = m_current->pair->next; + //m_current = m_current->prev->pair; + } + + virtual bool isDone() const { + return m_end == m_current; + } + virtual const Edge *current() const { + return m_current; + } + const Vertex *vertex() const { + return m_current->to(); + } + + private: + const Edge *m_end; + const Edge *m_current; + }; + + ConstEdgeIterator edges() const { + return ConstEdgeIterator(edge); + } + ConstEdgeIterator edges(const Edge *e) const { + return ConstEdgeIterator(e); + } + + // Iterator that visits all the colocal vertices. + class VertexIterator //: public Iterator<Edge *> + { + public: + VertexIterator(Vertex *v) : + m_end(NULL), + m_current(v) {} + + virtual void advance() { + if (m_end == NULL) m_end = m_current; + m_current = m_current->next; + } + + virtual bool isDone() const { + return m_end == m_current; + } + virtual Vertex *current() const { + return m_current; + } + + private: + Vertex *m_end; + Vertex *m_current; + }; + + VertexIterator colocals() { + return VertexIterator(this); + } + + // Iterator that visits all the colocal vertices. + class ConstVertexIterator //: public Iterator<Edge *> + { + public: + ConstVertexIterator(const Vertex *v) : + m_end(NULL), + m_current(v) {} + + virtual void advance() { + if (m_end == NULL) m_end = m_current; + m_current = m_current->next; + } + + virtual bool isDone() const { + return m_end == m_current; + } + virtual const Vertex *current() const { + return m_current; + } + + private: + const Vertex *m_end; + const Vertex *m_current; + }; + + ConstVertexIterator colocals() const { + return ConstVertexIterator(this); + } +}; + +bool Edge::isNormalSeam() const { + return (vertex->nor != pair->next->vertex->nor || next->vertex->nor != pair->vertex->nor); +} + +bool Edge::isTextureSeam() const { + return (vertex->tex != pair->next->vertex->tex || next->vertex->tex != pair->vertex->tex); +} + +float Edge::length() const { + return internal::length(to()->pos - from()->pos); +} + +float Edge::angle() const { + Vector3 p = vertex->pos; + Vector3 a = prev->vertex->pos; + Vector3 b = next->vertex->pos; + Vector3 v0 = a - p; + Vector3 v1 = b - p; + return acosf(dot(v0, v1) / (internal::length(v0) * internal::length(v1))); +} + +class Face { +public: + uint32_t id; + uint16_t group; + uint16_t material; + Edge *edge; + + Face(uint32_t id) : + id(id), + group(uint16_t(~0)), + material(uint16_t(~0)), + edge(NULL) {} + + float area() const { + float area = 0; + const Vector3 &v0 = edge->from()->pos; + for (ConstEdgeIterator it(edges(edge->next)); it.current() != edge->prev; it.advance()) { + const Edge *e = it.current(); + const Vector3 &v1 = e->vertex->pos; + const Vector3 &v2 = e->next->vertex->pos; + area += length(cross(v1 - v0, v2 - v0)); + } + return area * 0.5f; + } + + float parametricArea() const { + float area = 0; + const Vector2 &v0 = edge->from()->tex; + for (ConstEdgeIterator it(edges(edge->next)); it.current() != edge->prev; it.advance()) { + const Edge *e = it.current(); + const Vector2 &v1 = e->vertex->tex; + const Vector2 &v2 = e->next->vertex->tex; + area += triangleArea(v0, v1, v2); + } + return area * 0.5f; + } + + Vector3 normal() const { + Vector3 n(0); + const Vertex *vertex0 = NULL; + for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) { + const Edge *e = it.current(); + xaAssert(e != NULL); + if (vertex0 == NULL) { + vertex0 = e->vertex; + } else if (e->next->vertex != vertex0) { + const halfedge::Vertex *vertex1 = e->from(); + const halfedge::Vertex *vertex2 = e->to(); + const Vector3 &p0 = vertex0->pos; + const Vector3 &p1 = vertex1->pos; + const Vector3 &p2 = vertex2->pos; + Vector3 v10 = p1 - p0; + Vector3 v20 = p2 - p0; + n += cross(v10, v20); + } + } + return normalizeSafe(n, Vector3(0, 0, 1), 0.0f); + } + + Vector3 centroid() const { + Vector3 sum(0.0f); + uint32_t count = 0; + for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) { + const Edge *e = it.current(); + sum += e->from()->pos; + count++; + } + return sum / float(count); + } + + // Unnormalized face normal assuming it's a triangle. + Vector3 triangleNormal() const { + Vector3 p0 = edge->vertex->pos; + Vector3 p1 = edge->next->vertex->pos; + Vector3 p2 = edge->next->next->vertex->pos; + Vector3 e0 = p2 - p0; + Vector3 e1 = p1 - p0; + return normalizeSafe(cross(e0, e1), Vector3(0), 0.0f); + } + + Vector3 triangleNormalAreaScaled() const { + Vector3 p0 = edge->vertex->pos; + Vector3 p1 = edge->next->vertex->pos; + Vector3 p2 = edge->next->next->vertex->pos; + Vector3 e0 = p2 - p0; + Vector3 e1 = p1 - p0; + return cross(e0, e1); + } + + // Average of the edge midpoints weighted by the edge length. + // I want a point inside the triangle, but closer to the cirumcenter. + Vector3 triangleCenter() const { + Vector3 p0 = edge->vertex->pos; + Vector3 p1 = edge->next->vertex->pos; + Vector3 p2 = edge->next->next->vertex->pos; + float l0 = length(p1 - p0); + float l1 = length(p2 - p1); + float l2 = length(p0 - p2); + Vector3 m0 = (p0 + p1) * l0 / (l0 + l1 + l2); + Vector3 m1 = (p1 + p2) * l1 / (l0 + l1 + l2); + Vector3 m2 = (p2 + p0) * l2 / (l0 + l1 + l2); + return m0 + m1 + m2; + } + + bool isValid() const { + uint32_t count = 0; + for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) { + const Edge *e = it.current(); + if (e->face != this) return false; + if (!e->isValid()) return false; + if (!e->pair->isValid()) return false; + count++; + } + if (count < 3) return false; + return true; + } + + bool contains(const Edge *e) const { + for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) { + if (it.current() == e) return true; + } + return false; + } + + uint32_t edgeCount() const { + uint32_t count = 0; + for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) { + ++count; + } + return count; + } + + // The iterator that visits the edges of this face in clockwise order. + class EdgeIterator //: public Iterator<Edge *> + { + public: + EdgeIterator(Edge *e) : + m_end(NULL), + m_current(e) {} + + virtual void advance() { + if (m_end == NULL) m_end = m_current; + m_current = m_current->next; + } + + virtual bool isDone() const { + return m_end == m_current; + } + virtual Edge *current() const { + return m_current; + } + Vertex *vertex() const { + return m_current->vertex; + } + + private: + Edge *m_end; + Edge *m_current; + }; + + EdgeIterator edges() { + return EdgeIterator(edge); + } + EdgeIterator edges(Edge *e) { + xaDebugAssert(contains(e)); + return EdgeIterator(e); + } + + // The iterator that visits the edges of this face in clockwise order. + class ConstEdgeIterator //: public Iterator<const Edge *> + { + public: + ConstEdgeIterator(const Edge *e) : + m_end(NULL), + m_current(e) {} + ConstEdgeIterator(const EdgeIterator &it) : + m_end(NULL), + m_current(it.current()) {} + + virtual void advance() { + if (m_end == NULL) m_end = m_current; + m_current = m_current->next; + } + + virtual bool isDone() const { + return m_end == m_current; + } + virtual const Edge *current() const { + return m_current; + } + const Vertex *vertex() const { + return m_current->vertex; + } + + private: + const Edge *m_end; + const Edge *m_current; + }; + + ConstEdgeIterator edges() const { + return ConstEdgeIterator(edge); + } + ConstEdgeIterator edges(const Edge *e) const { + xaDebugAssert(contains(e)); + return ConstEdgeIterator(e); + } +}; + +/// Simple half edge mesh designed for dynamic mesh manipulation. +class Mesh { +public: + Mesh() : + m_colocalVertexCount(0) {} + + Mesh(const Mesh *mesh) { + // Copy mesh vertices. + const uint32_t vertexCount = mesh->vertexCount(); + m_vertexArray.resize(vertexCount); + for (uint32_t v = 0; v < vertexCount; v++) { + const Vertex *vertex = mesh->vertexAt(v); + xaDebugAssert(vertex->id == v); + m_vertexArray[v] = new Vertex(v); + m_vertexArray[v]->pos = vertex->pos; + m_vertexArray[v]->nor = vertex->nor; + m_vertexArray[v]->tex = vertex->tex; + } + m_colocalVertexCount = vertexCount; + // Copy mesh faces. + const uint32_t faceCount = mesh->faceCount(); + std::vector<uint32_t> indexArray; + indexArray.reserve(3); + for (uint32_t f = 0; f < faceCount; f++) { + const Face *face = mesh->faceAt(f); + for (Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const Vertex *vertex = it.current()->from(); + indexArray.push_back(vertex->id); + } + addFace(indexArray); + indexArray.clear(); + } + } + + ~Mesh() { + clear(); + } + + void clear() { + for (size_t i = 0; i < m_vertexArray.size(); i++) + delete m_vertexArray[i]; + m_vertexArray.clear(); + for (auto it = m_edgeMap.begin(); it != m_edgeMap.end(); it++) + delete it->second; + m_edgeArray.clear(); + m_edgeMap.clear(); + for (size_t i = 0; i < m_faceArray.size(); i++) + delete m_faceArray[i]; + m_faceArray.clear(); + } + + Vertex *addVertex(const Vector3 &pos) { + xaDebugAssert(isFinite(pos)); + Vertex *v = new Vertex(m_vertexArray.size()); + v->pos = pos; + m_vertexArray.push_back(v); + return v; + } + + /// Link colocal vertices based on geometric location only. + void linkColocals() { + xaPrint("--- Linking colocals:\n"); + const uint32_t vertexCount = this->vertexCount(); + std::unordered_map<Vector3, Vertex *, Hash<Vector3>, Equal<Vector3> > vertexMap; + vertexMap.reserve(vertexCount); + for (uint32_t v = 0; v < vertexCount; v++) { + Vertex *vertex = vertexAt(v); + Vertex *colocal = vertexMap[vertex->pos]; + if (colocal) { + colocal->linkColocal(vertex); + } else { + vertexMap[vertex->pos] = vertex; + } + } + m_colocalVertexCount = vertexMap.size(); + xaPrint("--- %d vertex positions.\n", m_colocalVertexCount); + // @@ Remove duplicated vertices? or just leave them as colocals? + } + + void linkColocalsWithCanonicalMap(const std::vector<uint32_t> &canonicalMap) { + xaPrint("--- Linking colocals:\n"); + uint32_t vertexMapSize = 0; + for (uint32_t i = 0; i < canonicalMap.size(); i++) { + vertexMapSize = std::max(vertexMapSize, canonicalMap[i] + 1); + } + std::vector<Vertex *> vertexMap; + vertexMap.resize(vertexMapSize, NULL); + m_colocalVertexCount = 0; + const uint32_t vertexCount = this->vertexCount(); + for (uint32_t v = 0; v < vertexCount; v++) { + Vertex *vertex = vertexAt(v); + Vertex *colocal = vertexMap[canonicalMap[v]]; + if (colocal != NULL) { + xaDebugAssert(vertex->pos == colocal->pos); + colocal->linkColocal(vertex); + } else { + vertexMap[canonicalMap[v]] = vertex; + m_colocalVertexCount++; + } + } + xaPrint("--- %d vertex positions.\n", m_colocalVertexCount); + } + + Face *addFace() { + Face *f = new Face(m_faceArray.size()); + m_faceArray.push_back(f); + return f; + } + + Face *addFace(uint32_t v0, uint32_t v1, uint32_t v2) { + uint32_t indexArray[3]; + indexArray[0] = v0; + indexArray[1] = v1; + indexArray[2] = v2; + return addFace(indexArray, 3, 0, 3); + } + + Face *addUniqueFace(uint32_t v0, uint32_t v1, uint32_t v2) { + + int base_vertex = m_vertexArray.size(); + + uint32_t ids[3] = { v0, v1, v2 }; + + Vector3 base[3] = { + m_vertexArray[v0]->pos, + m_vertexArray[v1]->pos, + m_vertexArray[v2]->pos, + }; + + //make sure its not a degenerate + bool degenerate = distanceSquared(base[0], base[1]) < NV_EPSILON || distanceSquared(base[0], base[2]) < NV_EPSILON || distanceSquared(base[1], base[2]) < NV_EPSILON; + xaDebugAssert(!degenerate); + + float min_x = 0; + + for (int i = 0; i < 3; i++) { + if (i == 0 || m_vertexArray[v0]->pos.x < min_x) { + min_x = m_vertexArray[v0]->pos.x; + } + } + + float max_x = 0; + + for (int j = 0; j < m_vertexArray.size(); j++) { + if (j == 0 || m_vertexArray[j]->pos.x > max_x) { //vertex already exists + max_x = m_vertexArray[j]->pos.x; + } + } + + //separate from everything else, in x axis + for (int i = 0; i < 3; i++) { + + base[i].x -= min_x; + base[i].x += max_x + 10.0; + } + + for (int i = 0; i < 3; i++) { + Vertex *v = new Vertex(m_vertexArray.size()); + v->pos = base[i]; + v->nor = m_vertexArray[ids[i]]->nor, + v->tex = m_vertexArray[ids[i]]->tex, + + v->original_id = ids[i]; + m_vertexArray.push_back(v); + } + + uint32_t indexArray[3]; + indexArray[0] = base_vertex + 0; + indexArray[1] = base_vertex + 1; + indexArray[2] = base_vertex + 2; + return addFace(indexArray, 3, 0, 3); + } + + Face *addFace(uint32_t v0, uint32_t v1, uint32_t v2, uint32_t v3) { + uint32_t indexArray[4]; + indexArray[0] = v0; + indexArray[1] = v1; + indexArray[2] = v2; + indexArray[3] = v3; + return addFace(indexArray, 4, 0, 4); + } + + Face *addFace(const std::vector<uint32_t> &indexArray) { + return addFace(indexArray, 0, indexArray.size()); + } + + Face *addFace(const std::vector<uint32_t> &indexArray, uint32_t first, uint32_t num) { + return addFace(indexArray.data(), (uint32_t)indexArray.size(), first, num); + } + + Face *addFace(const uint32_t *indexArray, uint32_t indexCount, uint32_t first, uint32_t num) { + xaDebugAssert(first < indexCount); + xaDebugAssert(num <= indexCount - first); + xaDebugAssert(num > 2); + if (!canAddFace(indexArray, first, num)) { + return NULL; + } + Face *f = new Face(m_faceArray.size()); + Edge *firstEdge = NULL; + Edge *last = NULL; + Edge *current = NULL; + for (uint32_t i = 0; i < num - 1; i++) { + current = addEdge(indexArray[first + i], indexArray[first + i + 1]); + xaAssert(current != NULL && current->face == NULL); + current->face = f; + if (last != NULL) + last->setNext(current); + else + firstEdge = current; + last = current; + } + current = addEdge(indexArray[first + num - 1], indexArray[first]); + xaAssert(current != NULL && current->face == NULL); + current->face = f; + last->setNext(current); + current->setNext(firstEdge); + f->edge = firstEdge; + m_faceArray.push_back(f); + return f; + } + + // These functions disconnect the given element from the mesh and delete it. + + // @@ We must always disconnect edge pairs simultaneously. + void disconnect(Edge *edge) { + xaDebugAssert(edge != NULL); + // Remove from edge list. + if ((edge->id & 1) == 0) { + xaDebugAssert(m_edgeArray[edge->id / 2] == edge); + m_edgeArray[edge->id / 2] = NULL; + } + // Remove edge from map. @@ Store map key inside edge? + xaDebugAssert(edge->from() != NULL && edge->to() != NULL); + size_t removed = m_edgeMap.erase(Key(edge->from()->id, edge->to()->id)); + xaDebugAssert(removed == 1); +#ifdef NDEBUG + removed = 0; // silence unused parameter warning +#endif + // Disconnect from vertex. + if (edge->vertex != NULL) { + if (edge->vertex->edge == edge) { + if (edge->prev && edge->prev->pair) { + edge->vertex->edge = edge->prev->pair; + } else if (edge->pair && edge->pair->next) { + edge->vertex->edge = edge->pair->next; + } else { + edge->vertex->edge = NULL; + // @@ Remove disconnected vertex? + } + } + } + // Disconnect from face. + if (edge->face != NULL) { + if (edge->face->edge == edge) { + if (edge->next != NULL && edge->next != edge) { + edge->face->edge = edge->next; + } else if (edge->prev != NULL && edge->prev != edge) { + edge->face->edge = edge->prev; + } else { + edge->face->edge = NULL; + // @@ Remove disconnected face? + } + } + } + // Disconnect from previous. + if (edge->prev) { + if (edge->prev->next == edge) { + edge->prev->setNext(NULL); + } + //edge->setPrev(NULL); + } + // Disconnect from next. + if (edge->next) { + if (edge->next->prev == edge) { + edge->next->setPrev(NULL); + } + //edge->setNext(NULL); + } + } + + void remove(Edge *edge) { + xaDebugAssert(edge != NULL); + disconnect(edge); + delete edge; + } + + void remove(Vertex *vertex) { + xaDebugAssert(vertex != NULL); + // Remove from vertex list. + m_vertexArray[vertex->id] = NULL; + // Disconnect from colocals. + vertex->unlinkColocal(); + // Disconnect from edges. + if (vertex->edge != NULL) { + // @@ Removing a connected vertex is asking for trouble... + if (vertex->edge->vertex == vertex) { + // @@ Connect edge to a colocal? + vertex->edge->vertex = NULL; + } + vertex->setEdge(NULL); + } + delete vertex; + } + + void remove(Face *face) { + xaDebugAssert(face != NULL); + // Remove from face list. + m_faceArray[face->id] = NULL; + // Disconnect from edges. + if (face->edge != NULL) { + xaDebugAssert(face->edge->face == face); + face->edge->face = NULL; + face->edge = NULL; + } + delete face; + } + + // Triangulate in place. + void triangulate() { + bool all_triangles = true; + const uint32_t faceCount = m_faceArray.size(); + for (uint32_t f = 0; f < faceCount; f++) { + Face *face = m_faceArray[f]; + if (face->edgeCount() != 3) { + all_triangles = false; + break; + } + } + if (all_triangles) { + return; + } + // Do not touch vertices, but rebuild edges and faces. + std::vector<Edge *> edgeArray; + std::vector<Face *> faceArray; + std::swap(edgeArray, m_edgeArray); + std::swap(faceArray, m_faceArray); + m_edgeMap.clear(); + for (uint32_t f = 0; f < faceCount; f++) { + Face *face = faceArray[f]; + // Trivial fan-like triangulation. + const uint32_t v0 = face->edge->vertex->id; + uint32_t v2, v1 = (uint32_t)-1; + for (Face::EdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + Edge *edge = it.current(); + v2 = edge->to()->id; + if (v2 == v0) break; + if (v1 != -1) addFace(v0, v1, v2); + v1 = v2; + } + } + xaDebugAssert(m_faceArray.size() > faceCount); // triangle count > face count + linkBoundary(); + for (size_t i = 0; i < edgeArray.size(); i++) + delete edgeArray[i]; + for (size_t i = 0; i < faceArray.size(); i++) + delete faceArray[i]; + } + + /// Link boundary edges once the mesh has been created. + void linkBoundary() { + xaPrint("--- Linking boundaries:\n"); + int num = 0; + // Create boundary edges. + uint32_t edgeCount = this->edgeCount(); + for (uint32_t e = 0; e < edgeCount; e++) { + Edge *edge = edgeAt(e); + if (edge != NULL && edge->pair == NULL) { + Edge *pair = new Edge(edge->id + 1); + uint32_t i = edge->from()->id; + uint32_t j = edge->next->from()->id; + Key key(j, i); + xaAssert(m_edgeMap.find(key) == m_edgeMap.end()); + pair->vertex = m_vertexArray[j]; + m_edgeMap[key] = pair; + edge->pair = pair; + pair->pair = edge; + num++; + } + } + // Link boundary edges. + for (uint32_t e = 0; e < edgeCount; e++) { + Edge *edge = edgeAt(e); + if (edge != NULL && edge->pair->face == NULL) { + linkBoundaryEdge(edge->pair); + } + } + xaPrint("--- %d boundary edges.\n", num); + } + + /* + Fixing T-junctions. + + - Find T-junctions. Find vertices that are on an edge. + - This test is approximate. + - Insert edges on a spatial index to speedup queries. + - Consider only open edges, that is edges that have no pairs. + - Consider only vertices on boundaries. + - Close T-junction. + - Split edge. + + */ + bool splitBoundaryEdges() // Returns true if any split was made. + { + std::vector<Vertex *> boundaryVertices; + for (uint32_t i = 0; i < m_vertexArray.size(); i++) { + Vertex *v = m_vertexArray[i]; + if (v->isBoundary()) { + boundaryVertices.push_back(v); + } + } + xaPrint("Fixing T-junctions:\n"); + int splitCount = 0; + for (uint32_t v = 0; v < boundaryVertices.size(); v++) { + Vertex *vertex = boundaryVertices[v]; + Vector3 x0 = vertex->pos; + // Find edges that this vertex overlaps with. + for (uint32_t e = 0; e < m_edgeArray.size(); e++) { + Edge *edge = m_edgeArray[e]; + if (edge != NULL && edge->isBoundary()) { + if (edge->from() == vertex || edge->to() == vertex) { + continue; + } + Vector3 x1 = edge->from()->pos; + Vector3 x2 = edge->to()->pos; + Vector3 v01 = x0 - x1; + Vector3 v21 = x2 - x1; + float l = length(v21); + float d = length(cross(v01, v21)) / l; + if (isZero(d)) { + float t = dot(v01, v21) / (l * l); + if (t > 0.0f + NV_EPSILON && t < 1.0f - NV_EPSILON) { + xaDebugAssert(equal(lerp(x1, x2, t), x0)); + Vertex *splitVertex = splitBoundaryEdge(edge, t, x0); + vertex->linkColocal(splitVertex); // @@ Should we do this here? + splitCount++; + } + } + } + } + } + xaPrint(" - %d edges split.\n", splitCount); + xaDebugAssert(isValid()); + return splitCount != 0; + } + + // Vertices + uint32_t vertexCount() const { + return m_vertexArray.size(); + } + const Vertex *vertexAt(int i) const { + return m_vertexArray[i]; + } + Vertex *vertexAt(int i) { + return m_vertexArray[i]; + } + + uint32_t colocalVertexCount() const { + return m_colocalVertexCount; + } + + // Faces + uint32_t faceCount() const { + return m_faceArray.size(); + } + const Face *faceAt(int i) const { + return m_faceArray[i]; + } + Face *faceAt(int i) { + return m_faceArray[i]; + } + + // Edges + uint32_t edgeCount() const { + return m_edgeArray.size(); + } + const Edge *edgeAt(int i) const { + return m_edgeArray[i]; + } + Edge *edgeAt(int i) { + return m_edgeArray[i]; + } + + class ConstVertexIterator; + + class VertexIterator { + friend class ConstVertexIterator; + + public: + VertexIterator(Mesh *mesh) : + m_mesh(mesh), + m_current(0) {} + + virtual void advance() { + m_current++; + } + virtual bool isDone() const { + return m_current == m_mesh->vertexCount(); + } + virtual Vertex *current() const { + return m_mesh->vertexAt(m_current); + } + + private: + halfedge::Mesh *m_mesh; + uint32_t m_current; + }; + VertexIterator vertices() { + return VertexIterator(this); + } + + class ConstVertexIterator { + public: + ConstVertexIterator(const Mesh *mesh) : + m_mesh(mesh), + m_current(0) {} + ConstVertexIterator(class VertexIterator &it) : + m_mesh(it.m_mesh), + m_current(it.m_current) {} + + virtual void advance() { + m_current++; + } + virtual bool isDone() const { + return m_current == m_mesh->vertexCount(); + } + virtual const Vertex *current() const { + return m_mesh->vertexAt(m_current); + } + + private: + const halfedge::Mesh *m_mesh; + uint32_t m_current; + }; + ConstVertexIterator vertices() const { + return ConstVertexIterator(this); + } + + class ConstFaceIterator; + + class FaceIterator { + friend class ConstFaceIterator; + + public: + FaceIterator(Mesh *mesh) : + m_mesh(mesh), + m_current(0) {} + + virtual void advance() { + m_current++; + } + virtual bool isDone() const { + return m_current == m_mesh->faceCount(); + } + virtual Face *current() const { + return m_mesh->faceAt(m_current); + } + + private: + halfedge::Mesh *m_mesh; + uint32_t m_current; + }; + FaceIterator faces() { + return FaceIterator(this); + } + + class ConstFaceIterator { + public: + ConstFaceIterator(const Mesh *mesh) : + m_mesh(mesh), + m_current(0) {} + ConstFaceIterator(const FaceIterator &it) : + m_mesh(it.m_mesh), + m_current(it.m_current) {} + + virtual void advance() { + m_current++; + } + virtual bool isDone() const { + return m_current == m_mesh->faceCount(); + } + virtual const Face *current() const { + return m_mesh->faceAt(m_current); + } + + private: + const halfedge::Mesh *m_mesh; + uint32_t m_current; + }; + ConstFaceIterator faces() const { + return ConstFaceIterator(this); + } + + class ConstEdgeIterator; + + class EdgeIterator { + friend class ConstEdgeIterator; + + public: + EdgeIterator(Mesh *mesh) : + m_mesh(mesh), + m_current(0) {} + + virtual void advance() { + m_current++; + } + virtual bool isDone() const { + return m_current == m_mesh->edgeCount(); + } + virtual Edge *current() const { + return m_mesh->edgeAt(m_current); + } + + private: + halfedge::Mesh *m_mesh; + uint32_t m_current; + }; + EdgeIterator edges() { + return EdgeIterator(this); + } + + class ConstEdgeIterator { + public: + ConstEdgeIterator(const Mesh *mesh) : + m_mesh(mesh), + m_current(0) {} + ConstEdgeIterator(const EdgeIterator &it) : + m_mesh(it.m_mesh), + m_current(it.m_current) {} + + virtual void advance() { + m_current++; + } + virtual bool isDone() const { + return m_current == m_mesh->edgeCount(); + } + virtual const Edge *current() const { + return m_mesh->edgeAt(m_current); + } + + private: + const halfedge::Mesh *m_mesh; + uint32_t m_current; + }; + ConstEdgeIterator edges() const { + return ConstEdgeIterator(this); + } + + // @@ Add half-edge iterator. + + bool isValid() const { + // Make sure all edges are valid. + const uint32_t edgeCount = m_edgeArray.size(); + for (uint32_t e = 0; e < edgeCount; e++) { + Edge *edge = m_edgeArray[e]; + if (edge != NULL) { + if (edge->id != 2 * e) { + return false; + } + if (!edge->isValid()) { + return false; + } + if (edge->pair->id != 2 * e + 1) { + return false; + } + if (!edge->pair->isValid()) { + return false; + } + } + } + // @@ Make sure all faces are valid. + // @@ Make sure all vertices are valid. + return true; + } + + // Error status: + + struct ErrorCode { + enum Enum { + AlreadyAddedEdge, + DegenerateColocalEdge, + DegenerateEdge, + DuplicateEdge + }; + }; + + mutable ErrorCode::Enum errorCode; + mutable uint32_t errorIndex0; + mutable uint32_t errorIndex1; + +private: + // Return true if the face can be added to the manifold mesh. + bool canAddFace(const std::vector<uint32_t> &indexArray, uint32_t first, uint32_t num) const { + return canAddFace(indexArray.data(), first, num); + } + + bool canAddFace(const uint32_t *indexArray, uint32_t first, uint32_t num) const { + for (uint32_t j = num - 1, i = 0; i < num; j = i++) { + if (!canAddEdge(indexArray[first + j], indexArray[first + i])) { + errorIndex0 = indexArray[first + j]; + errorIndex1 = indexArray[first + i]; + return false; + } + } + // We also have to make sure the face does not have any duplicate edge! + for (uint32_t i = 0; i < num; i++) { + int i0 = indexArray[first + i + 0]; + int i1 = indexArray[first + (i + 1) % num]; + for (uint32_t j = i + 1; j < num; j++) { + int j0 = indexArray[first + j + 0]; + int j1 = indexArray[first + (j + 1) % num]; + if (i0 == j0 && i1 == j1) { + errorCode = ErrorCode::DuplicateEdge; + errorIndex0 = i0; + errorIndex1 = i1; + return false; + } + } + } + return true; + } + + // Return true if the edge doesn't exist or doesn't have any adjacent face. + bool canAddEdge(uint32_t i, uint32_t j) const { + if (i == j) { + // Skip degenerate edges. + errorCode = ErrorCode::DegenerateEdge; + return false; + } + // Same check, but taking into account colocal vertices. + const Vertex *v0 = vertexAt(i); + const Vertex *v1 = vertexAt(j); + for (Vertex::ConstVertexIterator it(v0->colocals()); !it.isDone(); it.advance()) { + if (it.current() == v1) { + // Skip degenerate edges. + errorCode = ErrorCode::DegenerateColocalEdge; + return false; + } + } + // Make sure edge has not been added yet. + Edge *edge = findEdge(i, j); + // We ignore edges that don't have an adjacent face yet, since this face could become the edge's face. + if (!(edge == NULL || edge->face == NULL)) { + errorCode = ErrorCode::AlreadyAddedEdge; + return false; + } + return true; + } + + Edge *addEdge(uint32_t i, uint32_t j) { + xaAssert(i != j); + Edge *edge = findEdge(i, j); + if (edge != NULL) { + // Edge may already exist, but its face must not be set. + xaDebugAssert(edge->face == NULL); + // Nothing else to do! + } else { + // Add new edge. + // Lookup pair. + Edge *pair = findEdge(j, i); + if (pair != NULL) { + // Create edge with same id. + edge = new Edge(pair->id + 1); + // Link edge pairs. + edge->pair = pair; + pair->pair = edge; + // @@ I'm not sure this is necessary! + pair->vertex->setEdge(pair); + } else { + // Create edge. + edge = new Edge(2 * m_edgeArray.size()); + // Add only unpaired edges. + m_edgeArray.push_back(edge); + } + edge->vertex = m_vertexArray[i]; + m_edgeMap[Key(i, j)] = edge; + } + // Face and Next are set by addFace. + return edge; + } + + /// Find edge, test all colocals. + Edge *findEdge(uint32_t i, uint32_t j) const { + Edge *edge = NULL; + const Vertex *v0 = vertexAt(i); + const Vertex *v1 = vertexAt(j); + // Test all colocal pairs. + for (Vertex::ConstVertexIterator it0(v0->colocals()); !it0.isDone(); it0.advance()) { + for (Vertex::ConstVertexIterator it1(v1->colocals()); !it1.isDone(); it1.advance()) { + Key key(it0.current()->id, it1.current()->id); + if (edge == NULL) { + auto edgeIt = m_edgeMap.find(key); + if (edgeIt != m_edgeMap.end()) + edge = (*edgeIt).second; +#if !defined(_DEBUG) + if (edge != NULL) return edge; +#endif + } else { + // Make sure that only one edge is found. + xaDebugAssert(m_edgeMap.find(key) == m_edgeMap.end()); + } + } + } + return edge; + } + + /// Link this boundary edge. + void linkBoundaryEdge(Edge *edge) { + xaAssert(edge->face == NULL); + // Make sure next pointer has not been set. @@ We want to be able to relink boundary edges after mesh changes. + Edge *next = edge; + while (next->pair->face != NULL) { + // Get pair prev + Edge *e = next->pair->next; + while (e->next != next->pair) { + e = e->next; + } + next = e; + } + edge->setNext(next->pair); + // Adjust vertex edge, so that it's the boundary edge. (required for isBoundary()) + if (edge->vertex->edge != edge) { + // Multiple boundaries in the same edge. + edge->vertex->edge = edge; + } + } + + Vertex *splitBoundaryEdge(Edge *edge, float t, const Vector3 &pos) { + /* + We want to go from this configuration: + + + + + | ^ + edge |<->| pair + v | + + + + + To this one: + + + + + | ^ + e0 |<->| p0 + v | + vertex + + + | ^ + e1 |<->| p1 + v | + + + + + */ + Edge *pair = edge->pair; + // Make sure boundaries are linked. + xaDebugAssert(pair != NULL); + // Make sure edge is a boundary edge. + xaDebugAssert(pair->face == NULL); + // Add new vertex. + Vertex *vertex = addVertex(pos); + vertex->nor = lerp(edge->from()->nor, edge->to()->nor, t); + vertex->tex = lerp(edge->from()->tex, edge->to()->tex, t); + disconnect(edge); + disconnect(pair); + // Add edges. + Edge *e0 = addEdge(edge->from()->id, vertex->id); + Edge *p0 = addEdge(vertex->id, pair->to()->id); + Edge *e1 = addEdge(vertex->id, edge->to()->id); + Edge *p1 = addEdge(pair->from()->id, vertex->id); + // Link edges. + e0->setNext(e1); + p1->setNext(p0); + e0->setPrev(edge->prev); + e1->setNext(edge->next); + p1->setPrev(pair->prev); + p0->setNext(pair->next); + xaDebugAssert(e0->next == e1); + xaDebugAssert(e1->prev == e0); + xaDebugAssert(p1->next == p0); + xaDebugAssert(p0->prev == p1); + xaDebugAssert(p0->pair == e0); + xaDebugAssert(e0->pair == p0); + xaDebugAssert(p1->pair == e1); + xaDebugAssert(e1->pair == p1); + // Link faces. + e0->face = edge->face; + e1->face = edge->face; + // Link vertices. + edge->from()->setEdge(e0); + vertex->setEdge(e1); + delete edge; + delete pair; + return vertex; + } + +private: + std::vector<Vertex *> m_vertexArray; + std::vector<Edge *> m_edgeArray; + std::vector<Face *> m_faceArray; + + struct Key { + Key() {} + Key(const Key &k) : + p0(k.p0), + p1(k.p1) {} + Key(uint32_t v0, uint32_t v1) : + p0(v0), + p1(v1) {} + void operator=(const Key &k) { + p0 = k.p0; + p1 = k.p1; + } + bool operator==(const Key &k) const { + return p0 == k.p0 && p1 == k.p1; + } + + uint32_t p0; + uint32_t p1; + }; + + friend struct Hash<Mesh::Key>; + std::unordered_map<Key, Edge *, Hash<Key>, Equal<Key> > m_edgeMap; + uint32_t m_colocalVertexCount; +}; + +class MeshTopology { +public: + MeshTopology(const Mesh *mesh) { + buildTopologyInfo(mesh); + } + + /// Determine if the mesh is connected. + bool isConnected() const { + return m_connectedCount == 1; + } + + /// Determine if the mesh is closed. (Each edge is shared by two faces) + bool isClosed() const { + return m_boundaryCount == 0; + } + + /// Return true if the mesh has the topology of a disk. + bool isDisk() const { + return isConnected() && m_boundaryCount == 1 /* && m_eulerNumber == 1*/; + } + +private: + void buildTopologyInfo(const Mesh *mesh) { + const uint32_t vertexCount = mesh->colocalVertexCount(); + const uint32_t faceCount = mesh->faceCount(); + const uint32_t edgeCount = mesh->edgeCount(); + xaPrint("--- Building mesh topology:\n"); + std::vector<uint32_t> stack(faceCount); + BitArray bitFlags(faceCount); + bitFlags.clearAll(); + // Compute connectivity. + xaPrint("--- Computing connectivity.\n"); + m_connectedCount = 0; + for (uint32_t f = 0; f < faceCount; f++) { + if (bitFlags.bitAt(f) == false) { + m_connectedCount++; + stack.push_back(f); + while (!stack.empty()) { + const uint32_t top = stack.back(); + xaAssert(top != uint32_t(~0)); + stack.pop_back(); + if (bitFlags.bitAt(top) == false) { + bitFlags.setBitAt(top); + const Face *face = mesh->faceAt(top); + const Edge *firstEdge = face->edge; + const Edge *edge = firstEdge; + do { + const Face *neighborFace = edge->pair->face; + if (neighborFace != NULL) { + stack.push_back(neighborFace->id); + } + edge = edge->next; + } while (edge != firstEdge); + } + } + } + } + xaAssert(stack.empty()); + xaPrint("--- %d connected components.\n", m_connectedCount); + // Count boundary loops. + xaPrint("--- Counting boundary loops.\n"); + m_boundaryCount = 0; + bitFlags.resize(edgeCount); + bitFlags.clearAll(); + // Don't forget to link the boundary otherwise this won't work. + for (uint32_t e = 0; e < edgeCount; e++) { + const Edge *startEdge = mesh->edgeAt(e); + if (startEdge != NULL && startEdge->isBoundary() && bitFlags.bitAt(e) == false) { + xaDebugAssert(startEdge->face != NULL); + xaDebugAssert(startEdge->pair->face == NULL); + startEdge = startEdge->pair; + m_boundaryCount++; + const Edge *edge = startEdge; + do { + bitFlags.setBitAt(edge->id / 2); + edge = edge->next; + } while (startEdge != edge); + } + } + xaPrint("--- %d boundary loops found.\n", m_boundaryCount); + // Compute euler number. + m_eulerNumber = vertexCount - edgeCount + faceCount; + xaPrint("--- Euler number: %d.\n", m_eulerNumber); + // Compute genus. (only valid on closed connected surfaces) + m_genus = -1; + if (isClosed() && isConnected()) { + m_genus = (2 - m_eulerNumber) / 2; + xaPrint("--- Genus: %d.\n", m_genus); + } + } + +private: + ///< Number of boundary loops. + int m_boundaryCount; + + ///< Number of connected components. + int m_connectedCount; + + ///< Euler number. + int m_eulerNumber; + + /// Mesh genus. + int m_genus; +}; + +float computeSurfaceArea(const halfedge::Mesh *mesh) { + float area = 0; + for (halfedge::Mesh::ConstFaceIterator it(mesh->faces()); !it.isDone(); it.advance()) { + const halfedge::Face *face = it.current(); + area += face->area(); + } + xaDebugAssert(area >= 0); + return area; +} + +float computeParametricArea(const halfedge::Mesh *mesh) { + float area = 0; + for (halfedge::Mesh::ConstFaceIterator it(mesh->faces()); !it.isDone(); it.advance()) { + const halfedge::Face *face = it.current(); + area += face->parametricArea(); + } + return area; +} + +uint32_t countMeshTriangles(const Mesh *mesh) { + const uint32_t faceCount = mesh->faceCount(); + uint32_t triangleCount = 0; + for (uint32_t f = 0; f < faceCount; f++) { + const Face *face = mesh->faceAt(f); + uint32_t edgeCount = face->edgeCount(); + xaDebugAssert(edgeCount > 2); + triangleCount += edgeCount - 2; + } + return triangleCount; +} + +Mesh *unifyVertices(const Mesh *inputMesh) { + Mesh *mesh = new Mesh; + // Only add the first colocal. + const uint32_t vertexCount = inputMesh->vertexCount(); + for (uint32_t v = 0; v < vertexCount; v++) { + const Vertex *vertex = inputMesh->vertexAt(v); + if (vertex->isFirstColocal()) { + mesh->addVertex(vertex->pos); + } + } + std::vector<uint32_t> indexArray; + // Add new faces pointing to first colocals. + uint32_t faceCount = inputMesh->faceCount(); + for (uint32_t f = 0; f < faceCount; f++) { + const Face *face = inputMesh->faceAt(f); + indexArray.clear(); + for (Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const Edge *edge = it.current(); + const Vertex *vertex = edge->vertex->firstColocal(); + indexArray.push_back(vertex->id); + } + mesh->addFace(indexArray); + } + mesh->linkBoundary(); + return mesh; +} + +static bool pointInTriangle(const Vector2 &p, const Vector2 &a, const Vector2 &b, const Vector2 &c) { + return triangleArea(a, b, p) >= 0.00001f && + triangleArea(b, c, p) >= 0.00001f && + triangleArea(c, a, p) >= 0.00001f; +} + +// This is doing a simple ear-clipping algorithm that skips invalid triangles. Ideally, we should +// also sort the ears by angle, start with the ones that have the smallest angle and proceed in order. +Mesh *triangulate(const Mesh *inputMesh) { + Mesh *mesh = new Mesh; + // Add all vertices. + const uint32_t vertexCount = inputMesh->vertexCount(); + for (uint32_t v = 0; v < vertexCount; v++) { + const Vertex *vertex = inputMesh->vertexAt(v); + mesh->addVertex(vertex->pos); + } + std::vector<int> polygonVertices; + std::vector<float> polygonAngles; + std::vector<Vector2> polygonPoints; + const uint32_t faceCount = inputMesh->faceCount(); + for (uint32_t f = 0; f < faceCount; f++) { + const Face *face = inputMesh->faceAt(f); + xaDebugAssert(face != NULL); + const uint32_t edgeCount = face->edgeCount(); + xaDebugAssert(edgeCount >= 3); + polygonVertices.clear(); + polygonVertices.reserve(edgeCount); + if (edgeCount == 3) { + // Simple case for triangles. + for (Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const Edge *edge = it.current(); + const Vertex *vertex = edge->vertex; + polygonVertices.push_back(vertex->id); + } + int v0 = polygonVertices[0]; + int v1 = polygonVertices[1]; + int v2 = polygonVertices[2]; + mesh->addFace(v0, v1, v2); + } else { + // Build 2D polygon projecting vertices onto normal plane. + // Faces are not necesarily planar, this is for example the case, when the face comes from filling a hole. In such cases + // it's much better to use the best fit plane. + const Vector3 fn = face->normal(); + Basis basis; + basis.buildFrameForDirection(fn); + polygonPoints.clear(); + polygonPoints.reserve(edgeCount); + polygonAngles.clear(); + polygonAngles.reserve(edgeCount); + for (Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const Edge *edge = it.current(); + const Vertex *vertex = edge->vertex; + polygonVertices.push_back(vertex->id); + Vector2 p; + p.x = dot(basis.tangent, vertex->pos); + p.y = dot(basis.bitangent, vertex->pos); + polygonPoints.push_back(p); + } + polygonAngles.resize(edgeCount); + while (polygonVertices.size() > 2) { + uint32_t size = polygonVertices.size(); + // Update polygon angles. @@ Update only those that have changed. + float minAngle = 2 * PI; + uint32_t bestEar = 0; // Use first one if none of them is valid. + bool bestIsValid = false; + for (uint32_t i = 0; i < size; i++) { + uint32_t i0 = i; + uint32_t i1 = (i + 1) % size; // Use Sean's polygon interation trick. + uint32_t i2 = (i + 2) % size; + Vector2 p0 = polygonPoints[i0]; + Vector2 p1 = polygonPoints[i1]; + Vector2 p2 = polygonPoints[i2]; + + bool degenerate = distance(p0, p1) < NV_EPSILON || distance(p0, p2) < NV_EPSILON || distance(p1, p2) < NV_EPSILON; + if (degenerate) { + continue; + } + + float d = clamp(dot(p0 - p1, p2 - p1) / (length(p0 - p1) * length(p2 - p1)), -1.0f, 1.0f); + float angle = acosf(d); + float area = triangleArea(p0, p1, p2); + if (area < 0.0f) angle = 2.0f * PI - angle; + polygonAngles[i1] = angle; + if (angle < minAngle || !bestIsValid) { + // Make sure this is a valid ear, if not, skip this point. + bool valid = true; + for (uint32_t j = 0; j < size; j++) { + if (j == i0 || j == i1 || j == i2) continue; + Vector2 p = polygonPoints[j]; + if (pointInTriangle(p, p0, p1, p2)) { + valid = false; + break; + } + } + if (valid || !bestIsValid) { + minAngle = angle; + bestEar = i1; + bestIsValid = valid; + } + } + } + if (!bestIsValid) + break; + + xaDebugAssert(minAngle <= 2 * PI); + // Clip best ear: + uint32_t i0 = (bestEar + size - 1) % size; + uint32_t i1 = (bestEar + 0) % size; + uint32_t i2 = (bestEar + 1) % size; + int v0 = polygonVertices[i0]; + int v1 = polygonVertices[i1]; + int v2 = polygonVertices[i2]; + mesh->addFace(v0, v1, v2); + polygonVertices.erase(polygonVertices.begin() + i1); + polygonPoints.erase(polygonPoints.begin() + i1); + polygonAngles.erase(polygonAngles.begin() + i1); + } + } + } + mesh->linkBoundary(); + return mesh; +} + +} // namespace halfedge + +/// Mersenne twister random number generator. +class MTRand { +public: + enum time_e { Time }; + enum { N = 624 }; // length of state vector + enum { M = 397 }; + + /// Constructor that uses the current time as the seed. + MTRand(time_e) { + seed((uint32_t)time(NULL)); + } + + /// Constructor that uses the given seed. + MTRand(uint32_t s = 0) { + seed(s); + } + + /// Provide a new seed. + void seed(uint32_t s) { + initialize(s); + reload(); + } + + /// Get a random number between 0 - 65536. + uint32_t get() { + // Pull a 32-bit integer from the generator state + // Every other access function simply transforms the numbers extracted here + if (left == 0) { + reload(); + } + left--; + uint32_t s1; + s1 = *next++; + s1 ^= (s1 >> 11); + s1 ^= (s1 << 7) & 0x9d2c5680U; + s1 ^= (s1 << 15) & 0xefc60000U; + return (s1 ^ (s1 >> 18)); + }; + + /// Get a random number on [0, max] interval. + uint32_t getRange(uint32_t max) { + if (max == 0) return 0; + if (max == NV_UINT32_MAX) return get(); + const uint32_t np2 = nextPowerOfTwo(max + 1); // @@ This fails if max == NV_UINT32_MAX + const uint32_t mask = np2 - 1; + uint32_t n; + do { + n = get() & mask; + } while (n > max); + return n; + } + +private: + void initialize(uint32_t seed) { + // Initialize generator state with seed + // See Knuth TAOCP Vol 2, 3rd Ed, p.106 for multiplier. + // In previous versions, most significant bits (MSBs) of the seed affect + // only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto. + uint32_t *s = state; + uint32_t *r = state; + int i = 1; + *s++ = seed & 0xffffffffUL; + for (; i < N; ++i) { + *s++ = (1812433253UL * (*r ^ (*r >> 30)) + i) & 0xffffffffUL; + r++; + } + } + + void reload() { + // Generate N new values in state + // Made clearer and faster by Matthew Bellew (matthew.bellew@home.com) + uint32_t *p = state; + int i; + for (i = N - M; i--; ++p) + *p = twist(p[M], p[0], p[1]); + for (i = M; --i; ++p) + *p = twist(p[M - N], p[0], p[1]); + *p = twist(p[M - N], p[0], state[0]); + left = N, next = state; + } + + uint32_t hiBit(uint32_t u) const { + return u & 0x80000000U; + } + uint32_t loBit(uint32_t u) const { + return u & 0x00000001U; + } + uint32_t loBits(uint32_t u) const { + return u & 0x7fffffffU; + } + uint32_t mixBits(uint32_t u, uint32_t v) const { + return hiBit(u) | loBits(v); + } + uint32_t twist(uint32_t m, uint32_t s0, uint32_t s1) const { + return m ^ (mixBits(s0, s1) >> 1) ^ ((~loBit(s1) + 1) & 0x9908b0dfU); + } + + uint32_t state[N]; // internal state + uint32_t *next; // next value to get from state + int left; // number of values left before reload needed +}; + +namespace morton { +// Code from ryg: +// http://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/ + +// Inverse of part1By1 - "delete" all odd-indexed bits +uint32_t compact1By1(uint32_t x) { + x &= 0x55555555; // x = -f-e -d-c -b-a -9-8 -7-6 -5-4 -3-2 -1-0 + x = (x ^ (x >> 1)) & 0x33333333; // x = --fe --dc --ba --98 --76 --54 --32 --10 + x = (x ^ (x >> 2)) & 0x0f0f0f0f; // x = ---- fedc ---- ba98 ---- 7654 ---- 3210 + x = (x ^ (x >> 4)) & 0x00ff00ff; // x = ---- ---- fedc ba98 ---- ---- 7654 3210 + x = (x ^ (x >> 8)) & 0x0000ffff; // x = ---- ---- ---- ---- fedc ba98 7654 3210 + return x; +} + +// Inverse of part1By2 - "delete" all bits not at positions divisible by 3 +uint32_t compact1By2(uint32_t x) { + x &= 0x09249249; // x = ---- 9--8 --7- -6-- 5--4 --3- -2-- 1--0 + x = (x ^ (x >> 2)) & 0x030c30c3; // x = ---- --98 ---- 76-- --54 ---- 32-- --10 + x = (x ^ (x >> 4)) & 0x0300f00f; // x = ---- --98 ---- ---- 7654 ---- ---- 3210 + x = (x ^ (x >> 8)) & 0xff0000ff; // x = ---- --98 ---- ---- ---- ---- 7654 3210 + x = (x ^ (x >> 16)) & 0x000003ff; // x = ---- ---- ---- ---- ---- --98 7654 3210 + return x; +} + +uint32_t decodeMorton2X(uint32_t code) { + return compact1By1(code >> 0); +} + +uint32_t decodeMorton2Y(uint32_t code) { + return compact1By1(code >> 1); +} + +uint32_t decodeMorton3X(uint32_t code) { + return compact1By2(code >> 0); +} + +uint32_t decodeMorton3Y(uint32_t code) { + return compact1By2(code >> 1); +} + +uint32_t decodeMorton3Z(uint32_t code) { + return compact1By2(code >> 2); +} +} // namespace morton + +// A simple, dynamic proximity grid based on Jon's code. +// Instead of storing pointers here I store indices. +struct ProximityGrid { + void init(const Box &box, uint32_t count) { + cellArray.clear(); + // Determine grid size. + float cellWidth; + Vector3 diagonal = box.extents() * 2.f; + float volume = box.volume(); + if (equal(volume, 0)) { + // Degenerate box, treat like a quad. + Vector2 quad; + if (diagonal.x < diagonal.y && diagonal.x < diagonal.z) { + quad.x = diagonal.y; + quad.y = diagonal.z; + } else if (diagonal.y < diagonal.x && diagonal.y < diagonal.z) { + quad.x = diagonal.x; + quad.y = diagonal.z; + } else { + quad.x = diagonal.x; + quad.y = diagonal.y; + } + float cellArea = quad.x * quad.y / count; + cellWidth = sqrtf(cellArea); // pow(cellArea, 1.0f / 2.0f); + } else { + // Ideally we want one cell per point. + float cellVolume = volume / count; + cellWidth = powf(cellVolume, 1.0f / 3.0f); + } + xaDebugAssert(cellWidth != 0); + sx = std::max(1, ftoi_ceil(diagonal.x / cellWidth)); + sy = std::max(1, ftoi_ceil(diagonal.y / cellWidth)); + sz = std::max(1, ftoi_ceil(diagonal.z / cellWidth)); + invCellSize.x = float(sx) / diagonal.x; + invCellSize.y = float(sy) / diagonal.y; + invCellSize.z = float(sz) / diagonal.z; + cellArray.resize(sx * sy * sz); + corner = box.minCorner; // @@ Align grid better? + } + + int index_x(float x) const { + return clamp(ftoi_floor((x - corner.x) * invCellSize.x), 0, sx - 1); + } + + int index_y(float y) const { + return clamp(ftoi_floor((y - corner.y) * invCellSize.y), 0, sy - 1); + } + + int index_z(float z) const { + return clamp(ftoi_floor((z - corner.z) * invCellSize.z), 0, sz - 1); + } + + int index(int x, int y, int z) const { + xaDebugAssert(x >= 0 && x < sx); + xaDebugAssert(y >= 0 && y < sy); + xaDebugAssert(z >= 0 && z < sz); + int idx = (z * sy + y) * sx + x; + xaDebugAssert(idx >= 0 && uint32_t(idx) < cellArray.size()); + return idx; + } + + uint32_t mortonCount() const { + uint64_t s = uint64_t(max3(sx, sy, sz)); + s = nextPowerOfTwo(s); + if (s > 1024) { + return uint32_t(s * s * min3(sx, sy, sz)); + } + return uint32_t(s * s * s); + } + + int mortonIndex(uint32_t code) const { + uint32_t x, y, z; + uint32_t s = uint32_t(max3(sx, sy, sz)); + if (s > 1024) { + // Use layered two-dimensional morton order. + s = nextPowerOfTwo(s); + uint32_t layer = code / (s * s); + code = code % (s * s); + uint32_t layer_count = uint32_t(min3(sx, sy, sz)); + if (sx == (int)layer_count) { + x = layer; + y = morton::decodeMorton2X(code); + z = morton::decodeMorton2Y(code); + } else if (sy == (int)layer_count) { + x = morton::decodeMorton2Y(code); + y = layer; + z = morton::decodeMorton2X(code); + } else { /*if (sz == layer_count)*/ + x = morton::decodeMorton2X(code); + y = morton::decodeMorton2Y(code); + z = layer; + } + } else { + x = morton::decodeMorton3X(code); + y = morton::decodeMorton3Y(code); + z = morton::decodeMorton3Z(code); + } + if (x >= uint32_t(sx) || y >= uint32_t(sy) || z >= uint32_t(sz)) { + return -1; + } + return index(x, y, z); + } + + void add(const Vector3 &pos, uint32_t key) { + int x = index_x(pos.x); + int y = index_y(pos.y); + int z = index_z(pos.z); + uint32_t idx = index(x, y, z); + cellArray[idx].indexArray.push_back(key); + } + + // Gather all points inside the given sphere. + // Radius is assumed to be small, so we don't bother culling the cells. + void gather(const Vector3 &position, float radius, std::vector<uint32_t> &indexArray) { + int x0 = index_x(position.x - radius); + int x1 = index_x(position.x + radius); + int y0 = index_y(position.y - radius); + int y1 = index_y(position.y + radius); + int z0 = index_z(position.z - radius); + int z1 = index_z(position.z + radius); + for (int z = z0; z <= z1; z++) { + for (int y = y0; y <= y1; y++) { + for (int x = x0; x <= x1; x++) { + int idx = index(x, y, z); + indexArray.insert(indexArray.begin(), cellArray[idx].indexArray.begin(), cellArray[idx].indexArray.end()); + } + } + } + } + + struct Cell { + std::vector<uint32_t> indexArray; + }; + + std::vector<Cell> cellArray; + + Vector3 corner; + Vector3 invCellSize; + int sx, sy, sz; +}; + +// Based on Pierre Terdiman's and Michael Herf's source code. +// http://www.codercorner.com/RadixSortRevisited.htm +// http://www.stereopsis.com/radix.html +class RadixSort { +public: + RadixSort() : + m_size(0), + m_ranks(NULL), + m_ranks2(NULL), + m_validRanks(false) {} + ~RadixSort() { + // Release everything + free(m_ranks2); + free(m_ranks); + } + + RadixSort &sort(const float *input, uint32_t count) { + if (input == NULL || count == 0) return *this; + // Resize lists if needed + if (count != m_size) { + if (count > m_size) { + m_ranks2 = (uint32_t *)realloc(m_ranks2, sizeof(uint32_t) * count); + m_ranks = (uint32_t *)realloc(m_ranks, sizeof(uint32_t) * count); + } + m_size = count; + m_validRanks = false; + } + if (count < 32) { + insertionSort(input, count); + } else { + // @@ Avoid touching the input multiple times. + for (uint32_t i = 0; i < count; i++) { + FloatFlip((uint32_t &)input[i]); + } + radixSort<uint32_t>((const uint32_t *)input, count); + for (uint32_t i = 0; i < count; i++) { + IFloatFlip((uint32_t &)input[i]); + } + } + return *this; + } + + RadixSort &sort(const std::vector<float> &input) { + return sort(input.data(), input.size()); + } + + // Access to results. m_ranks is a list of indices in sorted order, i.e. in the order you may further process your data + const uint32_t *ranks() const { + xaDebugAssert(m_validRanks); + return m_ranks; + } + uint32_t *ranks() { + xaDebugAssert(m_validRanks); + return m_ranks; + } + +private: + uint32_t m_size; + uint32_t *m_ranks; + uint32_t *m_ranks2; + bool m_validRanks; + + void FloatFlip(uint32_t &f) { + int32_t mask = (int32_t(f) >> 31) | 0x80000000; // Warren Hunt, Manchor Ko. + f ^= mask; + } + + void IFloatFlip(uint32_t &f) { + uint32_t mask = ((f >> 31) - 1) | 0x80000000; // Michael Herf. + f ^= mask; + } + + template <typename T> + void createHistograms(const T *buffer, uint32_t count, uint32_t *histogram) { + const uint32_t bucketCount = sizeof(T); // (8 * sizeof(T)) / log2(radix) + // Init bucket pointers. + uint32_t *h[bucketCount]; + for (uint32_t i = 0; i < bucketCount; i++) { + h[i] = histogram + 256 * i; + } + // Clear histograms. + memset(histogram, 0, 256 * bucketCount * sizeof(uint32_t)); + // @@ Add support for signed integers. + // Build histograms. + const uint8_t *p = (const uint8_t *)buffer; // @@ Does this break aliasing rules? + const uint8_t *pe = p + count * sizeof(T); + while (p != pe) { + h[0][*p++]++, h[1][*p++]++, h[2][*p++]++, h[3][*p++]++; +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4127) +#endif + if (bucketCount == 8) h[4][*p++]++, h[5][*p++]++, h[6][*p++]++, h[7][*p++]++; +#ifdef _MSC_VER +#pragma warning(pop) +#endif + } + } + + template <typename T> + void insertionSort(const T *input, uint32_t count) { + if (!m_validRanks) { + m_ranks[0] = 0; + for (uint32_t i = 1; i != count; ++i) { + int rank = m_ranks[i] = i; + uint32_t j = i; + while (j != 0 && input[rank] < input[m_ranks[j - 1]]) { + m_ranks[j] = m_ranks[j - 1]; + --j; + } + if (i != j) { + m_ranks[j] = rank; + } + } + m_validRanks = true; + } else { + for (uint32_t i = 1; i != count; ++i) { + int rank = m_ranks[i]; + uint32_t j = i; + while (j != 0 && input[rank] < input[m_ranks[j - 1]]) { + m_ranks[j] = m_ranks[j - 1]; + --j; + } + if (i != j) { + m_ranks[j] = rank; + } + } + } + } + + template <typename T> + void radixSort(const T *input, uint32_t count) { + const uint32_t P = sizeof(T); // pass count + // Allocate histograms & offsets on the stack + uint32_t histogram[256 * P]; + uint32_t *link[256]; + createHistograms(input, count, histogram); + // Radix sort, j is the pass number (0=LSB, P=MSB) + for (uint32_t j = 0; j < P; j++) { + // Pointer to this bucket. + const uint32_t *h = &histogram[j * 256]; + const uint8_t *inputBytes = (const uint8_t *)input; // @@ Is this aliasing legal? + inputBytes += j; + if (h[inputBytes[0]] == count) { + // Skip this pass, all values are the same. + continue; + } + // Create offsets + link[0] = m_ranks2; + for (uint32_t i = 1; i < 256; i++) + link[i] = link[i - 1] + h[i - 1]; + // Perform Radix Sort + if (!m_validRanks) { + for (uint32_t i = 0; i < count; i++) { + *link[inputBytes[i * P]]++ = i; + } + m_validRanks = true; + } else { + for (uint32_t i = 0; i < count; i++) { + const uint32_t idx = m_ranks[i]; + *link[inputBytes[idx * P]]++ = idx; + } + } + // Swap pointers for next pass. Valid indices - the most recent ones - are in m_ranks after the swap. + std::swap(m_ranks, m_ranks2); + } + // All values were equal, generate linear ranks. + if (!m_validRanks) { + for (uint32_t i = 0; i < count; i++) { + m_ranks[i] = i; + } + m_validRanks = true; + } + } +}; + +namespace raster { +class ClippedTriangle { +public: + ClippedTriangle(Vector2::Arg a, Vector2::Arg b, Vector2::Arg c) { + m_numVertices = 3; + m_activeVertexBuffer = 0; + m_verticesA[0] = a; + m_verticesA[1] = b; + m_verticesA[2] = c; + m_vertexBuffers[0] = m_verticesA; + m_vertexBuffers[1] = m_verticesB; + } + + uint32_t vertexCount() { + return m_numVertices; + } + + const Vector2 *vertices() { + return m_vertexBuffers[m_activeVertexBuffer]; + } + + void clipHorizontalPlane(float offset, float clipdirection) { + Vector2 *v = m_vertexBuffers[m_activeVertexBuffer]; + m_activeVertexBuffer ^= 1; + Vector2 *v2 = m_vertexBuffers[m_activeVertexBuffer]; + v[m_numVertices] = v[0]; + float dy2, dy1 = offset - v[0].y; + int dy2in, dy1in = clipdirection * dy1 >= 0; + uint32_t p = 0; + for (uint32_t k = 0; k < m_numVertices; k++) { + dy2 = offset - v[k + 1].y; + dy2in = clipdirection * dy2 >= 0; + if (dy1in) v2[p++] = v[k]; + if (dy1in + dy2in == 1) { // not both in/out + float dx = v[k + 1].x - v[k].x; + float dy = v[k + 1].y - v[k].y; + v2[p++] = Vector2(v[k].x + dy1 * (dx / dy), offset); + } + dy1 = dy2; + dy1in = dy2in; + } + m_numVertices = p; + //for (uint32_t k=0; k<m_numVertices; k++) printf("(%f, %f)\n", v2[k].x, v2[k].y); printf("\n"); + } + + void clipVerticalPlane(float offset, float clipdirection) { + Vector2 *v = m_vertexBuffers[m_activeVertexBuffer]; + m_activeVertexBuffer ^= 1; + Vector2 *v2 = m_vertexBuffers[m_activeVertexBuffer]; + v[m_numVertices] = v[0]; + float dx2, dx1 = offset - v[0].x; + int dx2in, dx1in = clipdirection * dx1 >= 0; + uint32_t p = 0; + for (uint32_t k = 0; k < m_numVertices; k++) { + dx2 = offset - v[k + 1].x; + dx2in = clipdirection * dx2 >= 0; + if (dx1in) v2[p++] = v[k]; + if (dx1in + dx2in == 1) { // not both in/out + float dx = v[k + 1].x - v[k].x; + float dy = v[k + 1].y - v[k].y; + v2[p++] = Vector2(offset, v[k].y + dx1 * (dy / dx)); + } + dx1 = dx2; + dx1in = dx2in; + } + m_numVertices = p; + } + + void computeAreaCentroid() { + Vector2 *v = m_vertexBuffers[m_activeVertexBuffer]; + v[m_numVertices] = v[0]; + m_area = 0; + float centroidx = 0, centroidy = 0; + for (uint32_t k = 0; k < m_numVertices; k++) { + // http://local.wasp.uwa.edu.au/~pbourke/geometry/polyarea/ + float f = v[k].x * v[k + 1].y - v[k + 1].x * v[k].y; + m_area += f; + centroidx += f * (v[k].x + v[k + 1].x); + centroidy += f * (v[k].y + v[k + 1].y); + } + m_area = 0.5f * fabsf(m_area); + if (m_area == 0) { + m_centroid = Vector2(0.0f); + } else { + m_centroid = Vector2(centroidx / (6 * m_area), centroidy / (6 * m_area)); + } + } + + void clipAABox(float x0, float y0, float x1, float y1) { + clipVerticalPlane(x0, -1); + clipHorizontalPlane(y0, -1); + clipVerticalPlane(x1, 1); + clipHorizontalPlane(y1, 1); + computeAreaCentroid(); + } + + Vector2 centroid() { + return m_centroid; + } + + float area() { + return m_area; + } + +private: + Vector2 m_verticesA[7 + 1]; + Vector2 m_verticesB[7 + 1]; + Vector2 *m_vertexBuffers[2]; + uint32_t m_numVertices; + uint32_t m_activeVertexBuffer; + float m_area; + Vector2 m_centroid; +}; + +/// A callback to sample the environment. Return false to terminate rasterization. +typedef bool (*SamplingCallback)(void *param, int x, int y, Vector3::Arg bar, Vector3::Arg dx, Vector3::Arg dy, float coverage); + +/// A triangle for rasterization. +struct Triangle { + Triangle(Vector2::Arg v0, Vector2::Arg v1, Vector2::Arg v2, Vector3::Arg t0, Vector3::Arg t1, Vector3::Arg t2) { + // Init vertices. + this->v1 = v0; + this->v2 = v2; + this->v3 = v1; + // Set barycentric coordinates. + this->t1 = t0; + this->t2 = t2; + this->t3 = t1; + // make sure every triangle is front facing. + flipBackface(); + // Compute deltas. + valid = computeDeltas(); + computeUnitInwardNormals(); + } + + /// Compute texture space deltas. + /// This method takes two edge vectors that form a basis, determines the + /// coordinates of the canonic vectors in that basis, and computes the + /// texture gradient that corresponds to those vectors. + bool computeDeltas() { + Vector2 e0 = v3 - v1; + Vector2 e1 = v2 - v1; + Vector3 de0 = t3 - t1; + Vector3 de1 = t2 - t1; + float denom = 1.0f / (e0.y * e1.x - e1.y * e0.x); + if (!std::isfinite(denom)) { + return false; + } + float lambda1 = -e1.y * denom; + float lambda2 = e0.y * denom; + float lambda3 = e1.x * denom; + float lambda4 = -e0.x * denom; + dx = de0 * lambda1 + de1 * lambda2; + dy = de0 * lambda3 + de1 * lambda4; + return true; + } + + bool draw(const Vector2 &extents, bool enableScissors, SamplingCallback cb, void *param) { + // 28.4 fixed-point coordinates + const int Y1 = ftoi_round(16.0f * v1.y); + const int Y2 = ftoi_round(16.0f * v2.y); + const int Y3 = ftoi_round(16.0f * v3.y); + const int X1 = ftoi_round(16.0f * v1.x); + const int X2 = ftoi_round(16.0f * v2.x); + const int X3 = ftoi_round(16.0f * v3.x); + // Deltas + const int DX12 = X1 - X2; + const int DX23 = X2 - X3; + const int DX31 = X3 - X1; + const int DY12 = Y1 - Y2; + const int DY23 = Y2 - Y3; + const int DY31 = Y3 - Y1; + // Fixed-point deltas + const int FDX12 = DX12 << 4; + const int FDX23 = DX23 << 4; + const int FDX31 = DX31 << 4; + const int FDY12 = DY12 << 4; + const int FDY23 = DY23 << 4; + const int FDY31 = DY31 << 4; + int minx, miny, maxx, maxy; + if (enableScissors) { + int frustumX0 = 0 << 4; + int frustumY0 = 0 << 4; + int frustumX1 = (int)extents.x << 4; + int frustumY1 = (int)extents.y << 4; + // Bounding rectangle + minx = (std::max(min3(X1, X2, X3), frustumX0) + 0xF) >> 4; + miny = (std::max(min3(Y1, Y2, Y3), frustumY0) + 0xF) >> 4; + maxx = (std::min(max3(X1, X2, X3), frustumX1) + 0xF) >> 4; + maxy = (std::min(max3(Y1, Y2, Y3), frustumY1) + 0xF) >> 4; + } else { + // Bounding rectangle + minx = (min3(X1, X2, X3) + 0xF) >> 4; + miny = (min3(Y1, Y2, Y3) + 0xF) >> 4; + maxx = (max3(X1, X2, X3) + 0xF) >> 4; + maxy = (max3(Y1, Y2, Y3) + 0xF) >> 4; + } + // Block size, standard 8x8 (must be power of two) + const int q = 8; + // @@ This won't work when minx,miny are negative. This code path is not used. Leaving as is for now. + xaAssert(minx >= 0); + xaAssert(miny >= 0); + // Start in corner of 8x8 block + minx &= ~(q - 1); + miny &= ~(q - 1); + // Half-edge constants + int C1 = DY12 * X1 - DX12 * Y1; + int C2 = DY23 * X2 - DX23 * Y2; + int C3 = DY31 * X3 - DX31 * Y3; + // Correct for fill convention + if (DY12 < 0 || (DY12 == 0 && DX12 > 0)) C1++; + if (DY23 < 0 || (DY23 == 0 && DX23 > 0)) C2++; + if (DY31 < 0 || (DY31 == 0 && DX31 > 0)) C3++; + // Loop through blocks + for (int y = miny; y < maxy; y += q) { + for (int x = minx; x < maxx; x += q) { + // Corners of block + int x0 = x << 4; + int x1 = (x + q - 1) << 4; + int y0 = y << 4; + int y1 = (y + q - 1) << 4; + // Evaluate half-space functions + bool a00 = C1 + DX12 * y0 - DY12 * x0 > 0; + bool a10 = C1 + DX12 * y0 - DY12 * x1 > 0; + bool a01 = C1 + DX12 * y1 - DY12 * x0 > 0; + bool a11 = C1 + DX12 * y1 - DY12 * x1 > 0; + int a = (a00 << 0) | (a10 << 1) | (a01 << 2) | (a11 << 3); + bool b00 = C2 + DX23 * y0 - DY23 * x0 > 0; + bool b10 = C2 + DX23 * y0 - DY23 * x1 > 0; + bool b01 = C2 + DX23 * y1 - DY23 * x0 > 0; + bool b11 = C2 + DX23 * y1 - DY23 * x1 > 0; + int b = (b00 << 0) | (b10 << 1) | (b01 << 2) | (b11 << 3); + bool c00 = C3 + DX31 * y0 - DY31 * x0 > 0; + bool c10 = C3 + DX31 * y0 - DY31 * x1 > 0; + bool c01 = C3 + DX31 * y1 - DY31 * x0 > 0; + bool c11 = C3 + DX31 * y1 - DY31 * x1 > 0; + int c = (c00 << 0) | (c10 << 1) | (c01 << 2) | (c11 << 3); + // Skip block when outside an edge + if (a == 0x0 || b == 0x0 || c == 0x0) continue; + // Accept whole block when totally covered + if (a == 0xF && b == 0xF && c == 0xF) { + Vector3 texRow = t1 + dy * (y0 - v1.y) + dx * (x0 - v1.x); + for (int iy = y; iy < y + q; iy++) { + Vector3 tex = texRow; + for (int ix = x; ix < x + q; ix++) { + //Vector3 tex = t1 + dx * (ix - v1.x) + dy * (iy - v1.y); + if (!cb(param, ix, iy, tex, dx, dy, 1.0)) { + // early out. + return false; + } + tex += dx; + } + texRow += dy; + } + } else { // Partially covered block + int CY1 = C1 + DX12 * y0 - DY12 * x0; + int CY2 = C2 + DX23 * y0 - DY23 * x0; + int CY3 = C3 + DX31 * y0 - DY31 * x0; + Vector3 texRow = t1 + dy * (y0 - v1.y) + dx * (x0 - v1.x); + for (int iy = y; iy < y + q; iy++) { + int CX1 = CY1; + int CX2 = CY2; + int CX3 = CY3; + Vector3 tex = texRow; + for (int ix = x; ix < x + q; ix++) { + if (CX1 > 0 && CX2 > 0 && CX3 > 0) { + if (!cb(param, ix, iy, tex, dx, dy, 1.0)) { + // early out. + return false; + } + } + CX1 -= FDY12; + CX2 -= FDY23; + CX3 -= FDY31; + tex += dx; + } + CY1 += FDX12; + CY2 += FDX23; + CY3 += FDX31; + texRow += dy; + } + } + } + } + return true; + } + + // extents has to be multiple of BK_SIZE!! + bool drawAA(const Vector2 &extents, bool enableScissors, SamplingCallback cb, void *param) { + const float PX_INSIDE = 1.0f / sqrt(2.0f); + const float PX_OUTSIDE = -1.0f / sqrt(2.0f); + const float BK_SIZE = 8; + const float BK_INSIDE = sqrt(BK_SIZE * BK_SIZE / 2.0f); + const float BK_OUTSIDE = -sqrt(BK_SIZE * BK_SIZE / 2.0f); + + float minx, miny, maxx, maxy; + if (enableScissors) { + // Bounding rectangle + minx = floorf(std::max(min3(v1.x, v2.x, v3.x), 0.0f)); + miny = floorf(std::max(min3(v1.y, v2.y, v3.y), 0.0f)); + maxx = ceilf(std::min(max3(v1.x, v2.x, v3.x), extents.x - 1.0f)); + maxy = ceilf(std::min(max3(v1.y, v2.y, v3.y), extents.y - 1.0f)); + } else { + // Bounding rectangle + minx = floorf(min3(v1.x, v2.x, v3.x)); + miny = floorf(min3(v1.y, v2.y, v3.y)); + maxx = ceilf(max3(v1.x, v2.x, v3.x)); + maxy = ceilf(max3(v1.y, v2.y, v3.y)); + } + // There's no reason to align the blocks to the viewport, instead we align them to the origin of the triangle bounds. + minx = floorf(minx); + miny = floorf(miny); + //minx = (float)(((int)minx) & (~((int)BK_SIZE - 1))); // align to blocksize (we don't need to worry about blocks partially out of viewport) + //miny = (float)(((int)miny) & (~((int)BK_SIZE - 1))); + minx += 0.5; + miny += 0.5; // sampling at texel centers! + maxx += 0.5; + maxy += 0.5; + // Half-edge constants + float C1 = n1.x * (-v1.x) + n1.y * (-v1.y); + float C2 = n2.x * (-v2.x) + n2.y * (-v2.y); + float C3 = n3.x * (-v3.x) + n3.y * (-v3.y); + // Loop through blocks + for (float y0 = miny; y0 <= maxy; y0 += BK_SIZE) { + for (float x0 = minx; x0 <= maxx; x0 += BK_SIZE) { + // Corners of block + float xc = (x0 + (BK_SIZE - 1) / 2.0f); + float yc = (y0 + (BK_SIZE - 1) / 2.0f); + // Evaluate half-space functions + float aC = C1 + n1.x * xc + n1.y * yc; + float bC = C2 + n2.x * xc + n2.y * yc; + float cC = C3 + n3.x * xc + n3.y * yc; + // Skip block when outside an edge + if ((aC <= BK_OUTSIDE) || (bC <= BK_OUTSIDE) || (cC <= BK_OUTSIDE)) continue; + // Accept whole block when totally covered + if ((aC >= BK_INSIDE) && (bC >= BK_INSIDE) && (cC >= BK_INSIDE)) { + Vector3 texRow = t1 + dy * (y0 - v1.y) + dx * (x0 - v1.x); + for (float y = y0; y < y0 + BK_SIZE; y++) { + Vector3 tex = texRow; + for (float x = x0; x < x0 + BK_SIZE; x++) { + if (!cb(param, (int)x, (int)y, tex, dx, dy, 1.0f)) { + return false; + } + tex += dx; + } + texRow += dy; + } + } else { // Partially covered block + float CY1 = C1 + n1.x * x0 + n1.y * y0; + float CY2 = C2 + n2.x * x0 + n2.y * y0; + float CY3 = C3 + n3.x * x0 + n3.y * y0; + Vector3 texRow = t1 + dy * (y0 - v1.y) + dx * (x0 - v1.x); + for (float y = y0; y < y0 + BK_SIZE; y++) { // @@ This is not clipping to scissor rectangle correctly. + float CX1 = CY1; + float CX2 = CY2; + float CX3 = CY3; + Vector3 tex = texRow; + for (float x = x0; x < x0 + BK_SIZE; x++) { // @@ This is not clipping to scissor rectangle correctly. + if (CX1 >= PX_INSIDE && CX2 >= PX_INSIDE && CX3 >= PX_INSIDE) { + // pixel completely covered + Vector3 tex2 = t1 + dx * (x - v1.x) + dy * (y - v1.y); + if (!cb(param, (int)x, (int)y, tex2, dx, dy, 1.0f)) { + return false; + } + } else if ((CX1 >= PX_OUTSIDE) && (CX2 >= PX_OUTSIDE) && (CX3 >= PX_OUTSIDE)) { + // triangle partially covers pixel. do clipping. + ClippedTriangle ct(v1 - Vector2(x, y), v2 - Vector2(x, y), v3 - Vector2(x, y)); + ct.clipAABox(-0.5, -0.5, 0.5, 0.5); + Vector2 centroid = ct.centroid(); + float area = ct.area(); + if (area > 0.0f) { + Vector3 texCent = tex - dx * centroid.x - dy * centroid.y; + //xaAssert(texCent.x >= -0.1f && texCent.x <= 1.1f); // @@ Centroid is not very exact... + //xaAssert(texCent.y >= -0.1f && texCent.y <= 1.1f); + //xaAssert(texCent.z >= -0.1f && texCent.z <= 1.1f); + //Vector3 texCent2 = t1 + dx * (x - v1.x) + dy * (y - v1.y); + if (!cb(param, (int)x, (int)y, texCent, dx, dy, area)) { + return false; + } + } + } + CX1 += n1.x; + CX2 += n2.x; + CX3 += n3.x; + tex += dx; + } + CY1 += n1.y; + CY2 += n2.y; + CY3 += n3.y; + texRow += dy; + } + } + } + } + return true; + } + + void flipBackface() { + // check if triangle is backfacing, if so, swap two vertices + if (((v3.x - v1.x) * (v2.y - v1.y) - (v3.y - v1.y) * (v2.x - v1.x)) < 0) { + Vector2 hv = v1; + v1 = v2; + v2 = hv; // swap pos + Vector3 ht = t1; + t1 = t2; + t2 = ht; // swap tex + } + } + + // compute unit inward normals for each edge. + void computeUnitInwardNormals() { + n1 = v1 - v2; + n1 = Vector2(-n1.y, n1.x); + n1 = n1 * (1.0f / sqrtf(n1.x * n1.x + n1.y * n1.y)); + n2 = v2 - v3; + n2 = Vector2(-n2.y, n2.x); + n2 = n2 * (1.0f / sqrtf(n2.x * n2.x + n2.y * n2.y)); + n3 = v3 - v1; + n3 = Vector2(-n3.y, n3.x); + n3 = n3 * (1.0f / sqrtf(n3.x * n3.x + n3.y * n3.y)); + } + + // Vertices. + Vector2 v1, v2, v3; + Vector2 n1, n2, n3; // unit inward normals + Vector3 t1, t2, t3; + + // Deltas. + Vector3 dx, dy; + + float sign; + bool valid; +}; + +enum Mode { + Mode_Nearest, + Mode_Antialiased +}; + +// Process the given triangle. Returns false if rasterization was interrupted by the callback. +static bool drawTriangle(Mode mode, Vector2::Arg extents, bool enableScissors, const Vector2 v[3], SamplingCallback cb, void *param) { + Triangle tri(v[0], v[1], v[2], Vector3(1, 0, 0), Vector3(0, 1, 0), Vector3(0, 0, 1)); + // @@ It would be nice to have a conservative drawing mode that enlarges the triangle extents by one texel and is able to handle degenerate triangles. + // @@ Maybe the simplest thing to do would be raster triangle edges. + if (tri.valid) { + if (mode == Mode_Antialiased) { + return tri.drawAA(extents, enableScissors, cb, param); + } + if (mode == Mode_Nearest) { + return tri.draw(extents, enableScissors, cb, param); + } + } + return true; +} + +// Process the given quad. Returns false if rasterization was interrupted by the callback. +static bool drawQuad(Mode mode, Vector2::Arg extents, bool enableScissors, const Vector2 v[4], SamplingCallback cb, void *param) { + bool sign0 = triangleArea2(v[0], v[1], v[2]) > 0.0f; + bool sign1 = triangleArea2(v[0], v[2], v[3]) > 0.0f; + // Divide the quad into two non overlapping triangles. + if (sign0 == sign1) { + Triangle tri0(v[0], v[1], v[2], Vector3(0, 0, 0), Vector3(1, 0, 0), Vector3(1, 1, 0)); + Triangle tri1(v[0], v[2], v[3], Vector3(0, 0, 0), Vector3(1, 1, 0), Vector3(0, 1, 0)); + if (tri0.valid && tri1.valid) { + if (mode == Mode_Antialiased) { + return tri0.drawAA(extents, enableScissors, cb, param) && tri1.drawAA(extents, enableScissors, cb, param); + } else { + return tri0.draw(extents, enableScissors, cb, param) && tri1.draw(extents, enableScissors, cb, param); + } + } + } else { + Triangle tri0(v[0], v[1], v[3], Vector3(0, 0, 0), Vector3(1, 0, 0), Vector3(0, 1, 0)); + Triangle tri1(v[1], v[2], v[3], Vector3(1, 0, 0), Vector3(1, 1, 0), Vector3(0, 1, 0)); + if (tri0.valid && tri1.valid) { + if (mode == Mode_Antialiased) { + return tri0.drawAA(extents, enableScissors, cb, param) && tri1.drawAA(extents, enableScissors, cb, param); + } else { + return tri0.draw(extents, enableScissors, cb, param) && tri1.draw(extents, enableScissors, cb, param); + } + } + } + return true; +} +} // namespace raster + +// Full and sparse vector and matrix classes. BLAS subset. +// Pseudo-BLAS interface. +namespace sparse { +enum Transpose { + NoTransposed = 0, + Transposed = 1 +}; + +/** +* Sparse matrix class. The matrix is assumed to be sparse and to have +* very few non-zero elements, for this reason it's stored in indexed +* format. To multiply column vectors efficiently, the matrix stores +* the elements in indexed-column order, there is a list of indexed +* elements for each row of the matrix. As with the FullVector the +* dimension of the matrix is constant. +**/ +class Matrix { +public: + // An element of the sparse array. + struct Coefficient { + uint32_t x; // column + float v; // value + }; + + Matrix(uint32_t d) : + m_width(d) { m_array.resize(d); } + Matrix(uint32_t w, uint32_t h) : + m_width(w) { m_array.resize(h); } + Matrix(const Matrix &m) : + m_width(m.m_width) { m_array = m.m_array; } + + const Matrix &operator=(const Matrix &m) { + xaAssert(width() == m.width()); + xaAssert(height() == m.height()); + m_array = m.m_array; + return *this; + } + + uint32_t width() const { return m_width; } + uint32_t height() const { return m_array.size(); } + bool isSquare() const { return width() == height(); } + + // x is column, y is row + float getCoefficient(uint32_t x, uint32_t y) const { + xaDebugAssert(x < width()); + xaDebugAssert(y < height()); + const uint32_t count = m_array[y].size(); + for (uint32_t i = 0; i < count; i++) { + if (m_array[y][i].x == x) return m_array[y][i].v; + } + return 0.0f; + } + + void setCoefficient(uint32_t x, uint32_t y, float f) { + xaDebugAssert(x < width()); + xaDebugAssert(y < height()); + const uint32_t count = m_array[y].size(); + for (uint32_t i = 0; i < count; i++) { + if (m_array[y][i].x == x) { + m_array[y][i].v = f; + return; + } + } + if (f != 0.0f) { + Coefficient c = { x, f }; + m_array[y].push_back(c); + } + } + + float dotRow(uint32_t y, const FullVector &v) const { + xaDebugAssert(y < height()); + const uint32_t count = m_array[y].size(); + float sum = 0; + for (uint32_t i = 0; i < count; i++) { + sum += m_array[y][i].v * v[m_array[y][i].x]; + } + return sum; + } + + void madRow(uint32_t y, float alpha, FullVector &v) const { + xaDebugAssert(y < height()); + const uint32_t count = m_array[y].size(); + for (uint32_t i = 0; i < count; i++) { + v[m_array[y][i].x] += alpha * m_array[y][i].v; + } + } + + void clearRow(uint32_t y) { + xaDebugAssert(y < height()); + m_array[y].clear(); + } + + void scaleRow(uint32_t y, float f) { + xaDebugAssert(y < height()); + const uint32_t count = m_array[y].size(); + for (uint32_t i = 0; i < count; i++) { + m_array[y][i].v *= f; + } + } + + const std::vector<Coefficient> &getRow(uint32_t y) const { return m_array[y]; } + +private: + /// Number of columns. + const uint32_t m_width; + + /// Array of matrix elements. + std::vector<std::vector<Coefficient> > m_array; +}; + +// y = a * x + y +static void saxpy(float a, const FullVector &x, FullVector &y) { + xaDebugAssert(x.dimension() == y.dimension()); + const uint32_t dim = x.dimension(); + for (uint32_t i = 0; i < dim; i++) { + y[i] += a * x[i]; + } +} + +static void copy(const FullVector &x, FullVector &y) { + xaDebugAssert(x.dimension() == y.dimension()); + const uint32_t dim = x.dimension(); + for (uint32_t i = 0; i < dim; i++) { + y[i] = x[i]; + } +} + +static void scal(float a, FullVector &x) { + const uint32_t dim = x.dimension(); + for (uint32_t i = 0; i < dim; i++) { + x[i] *= a; + } +} + +static float dot(const FullVector &x, const FullVector &y) { + xaDebugAssert(x.dimension() == y.dimension()); + const uint32_t dim = x.dimension(); + float sum = 0; + for (uint32_t i = 0; i < dim; i++) { + sum += x[i] * y[i]; + } + return sum; +} + +static void mult(Transpose TM, const Matrix &M, const FullVector &x, FullVector &y) { + const uint32_t w = M.width(); + const uint32_t h = M.height(); + if (TM == Transposed) { + xaDebugAssert(h == x.dimension()); + xaDebugAssert(w == y.dimension()); + y.fill(0.0f); + for (uint32_t i = 0; i < h; i++) { + M.madRow(i, x[i], y); + } + } else { + xaDebugAssert(w == x.dimension()); + xaDebugAssert(h == y.dimension()); + for (uint32_t i = 0; i < h; i++) { + y[i] = M.dotRow(i, x); + } + } +} + +// y = M * x +static void mult(const Matrix &M, const FullVector &x, FullVector &y) { + mult(NoTransposed, M, x, y); +} + +static void sgemv(float alpha, Transpose TA, const Matrix &A, const FullVector &x, float beta, FullVector &y) { + const uint32_t w = A.width(); + const uint32_t h = A.height(); + if (TA == Transposed) { + xaDebugAssert(h == x.dimension()); + xaDebugAssert(w == y.dimension()); + for (uint32_t i = 0; i < h; i++) { + A.madRow(i, alpha * x[i], y); + } + } else { + xaDebugAssert(w == x.dimension()); + xaDebugAssert(h == y.dimension()); + for (uint32_t i = 0; i < h; i++) { + y[i] = alpha * A.dotRow(i, x) + beta * y[i]; + } + } +} + +// y = alpha*A*x + beta*y +static void sgemv(float alpha, const Matrix &A, const FullVector &x, float beta, FullVector &y) { + sgemv(alpha, NoTransposed, A, x, beta, y); +} + +// dot y-row of A by x-column of B +static float dotRowColumn(int y, const Matrix &A, int x, const Matrix &B) { + const std::vector<Matrix::Coefficient> &row = A.getRow(y); + const uint32_t count = row.size(); + float sum = 0.0f; + for (uint32_t i = 0; i < count; i++) { + const Matrix::Coefficient &c = row[i]; + sum += c.v * B.getCoefficient(x, c.x); + } + return sum; +} + +// dot y-row of A by x-row of B +static float dotRowRow(int y, const Matrix &A, int x, const Matrix &B) { + const std::vector<Matrix::Coefficient> &row = A.getRow(y); + const uint32_t count = row.size(); + float sum = 0.0f; + for (uint32_t i = 0; i < count; i++) { + const Matrix::Coefficient &c = row[i]; + sum += c.v * B.getCoefficient(c.x, x); + } + return sum; +} + +// dot y-column of A by x-column of B +static float dotColumnColumn(int y, const Matrix &A, int x, const Matrix &B) { + xaDebugAssert(A.height() == B.height()); + const uint32_t h = A.height(); + float sum = 0.0f; + for (uint32_t i = 0; i < h; i++) { + sum += A.getCoefficient(y, i) * B.getCoefficient(x, i); + } + return sum; +} + +static void transpose(const Matrix &A, Matrix &B) { + xaDebugAssert(A.width() == B.height()); + xaDebugAssert(B.width() == A.height()); + const uint32_t w = A.width(); + for (uint32_t x = 0; x < w; x++) { + B.clearRow(x); + } + const uint32_t h = A.height(); + for (uint32_t y = 0; y < h; y++) { + const std::vector<Matrix::Coefficient> &row = A.getRow(y); + const uint32_t count = row.size(); + for (uint32_t i = 0; i < count; i++) { + const Matrix::Coefficient &c = row[i]; + xaDebugAssert(c.x < w); + B.setCoefficient(y, c.x, c.v); + } + } +} + +static void sgemm(float alpha, Transpose TA, const Matrix &A, Transpose TB, const Matrix &B, float beta, Matrix &C) { + const uint32_t w = C.width(); + const uint32_t h = C.height(); + uint32_t aw = (TA == NoTransposed) ? A.width() : A.height(); + uint32_t ah = (TA == NoTransposed) ? A.height() : A.width(); + uint32_t bw = (TB == NoTransposed) ? B.width() : B.height(); + uint32_t bh = (TB == NoTransposed) ? B.height() : B.width(); + xaDebugAssert(aw == bh); + xaDebugAssert(bw == ah); + xaDebugAssert(w == bw); + xaDebugAssert(h == ah); +#ifdef NDEBUG + aw = ah = bw = bh = 0; // silence unused parameter warning +#endif + for (uint32_t y = 0; y < h; y++) { + for (uint32_t x = 0; x < w; x++) { + float c = beta * C.getCoefficient(x, y); + if (TA == NoTransposed && TB == NoTransposed) { + // dot y-row of A by x-column of B. + c += alpha * dotRowColumn(y, A, x, B); + } else if (TA == Transposed && TB == Transposed) { + // dot y-column of A by x-row of B. + c += alpha * dotRowColumn(x, B, y, A); + } else if (TA == Transposed && TB == NoTransposed) { + // dot y-column of A by x-column of B. + c += alpha * dotColumnColumn(y, A, x, B); + } else if (TA == NoTransposed && TB == Transposed) { + // dot y-row of A by x-row of B. + c += alpha * dotRowRow(y, A, x, B); + } + C.setCoefficient(x, y, c); + } + } +} + +static void mult(Transpose TA, const Matrix &A, Transpose TB, const Matrix &B, Matrix &C) { + sgemm(1.0f, TA, A, TB, B, 0.0f, C); +} + +// C = A * B +static void mult(const Matrix &A, const Matrix &B, Matrix &C) { + mult(NoTransposed, A, NoTransposed, B, C); +} + +} // namespace sparse + +class JacobiPreconditioner { +public: + JacobiPreconditioner(const sparse::Matrix &M, bool symmetric) : + m_inverseDiagonal(M.width()) { + xaAssert(M.isSquare()); + for (uint32_t x = 0; x < M.width(); x++) { + float elem = M.getCoefficient(x, x); + //xaDebugAssert( elem != 0.0f ); // This can be zero in the presence of zero area triangles. + if (symmetric) { + m_inverseDiagonal[x] = (elem != 0) ? 1.0f / sqrtf(fabsf(elem)) : 1.0f; + } else { + m_inverseDiagonal[x] = (elem != 0) ? 1.0f / elem : 1.0f; + } + } + } + + void apply(const FullVector &x, FullVector &y) const { + xaDebugAssert(x.dimension() == m_inverseDiagonal.dimension()); + xaDebugAssert(y.dimension() == m_inverseDiagonal.dimension()); + // @@ Wrap vector component-wise product into a separate function. + const uint32_t D = x.dimension(); + for (uint32_t i = 0; i < D; i++) { + y[i] = m_inverseDiagonal[i] * x[i]; + } + } + +private: + FullVector m_inverseDiagonal; +}; + +// Linear solvers. +class Solver { +public: + // Solve the symmetric system: At·A·x = At·b + static bool LeastSquaresSolver(const sparse::Matrix &A, const FullVector &b, FullVector &x, float epsilon = 1e-5f) { + xaDebugAssert(A.width() == x.dimension()); + xaDebugAssert(A.height() == b.dimension()); + xaDebugAssert(A.height() >= A.width()); // @@ If height == width we could solve it directly... + const uint32_t D = A.width(); + sparse::Matrix At(A.height(), A.width()); + sparse::transpose(A, At); + FullVector Atb(D); + sparse::mult(At, b, Atb); + sparse::Matrix AtA(D); + sparse::mult(At, A, AtA); + return SymmetricSolver(AtA, Atb, x, epsilon); + } + + // See section 10.4.3 in: Mesh Parameterization: Theory and Practice, Siggraph Course Notes, August 2007 + static bool LeastSquaresSolver(const sparse::Matrix &A, const FullVector &b, FullVector &x, const uint32_t *lockedParameters, uint32_t lockedCount, float epsilon = 1e-5f) { + xaDebugAssert(A.width() == x.dimension()); + xaDebugAssert(A.height() == b.dimension()); + xaDebugAssert(A.height() >= A.width() - lockedCount); + // @@ This is not the most efficient way of building a system with reduced degrees of freedom. It would be faster to do it on the fly. + const uint32_t D = A.width() - lockedCount; + xaDebugAssert(D > 0); + // Compute: b - Al * xl + FullVector b_Alxl(b); + for (uint32_t y = 0; y < A.height(); y++) { + const uint32_t count = A.getRow(y).size(); + for (uint32_t e = 0; e < count; e++) { + uint32_t column = A.getRow(y)[e].x; + bool isFree = true; + for (uint32_t i = 0; i < lockedCount; i++) { + isFree &= (lockedParameters[i] != column); + } + if (!isFree) { + b_Alxl[y] -= x[column] * A.getRow(y)[e].v; + } + } + } + // Remove locked columns from A. + sparse::Matrix Af(D, A.height()); + for (uint32_t y = 0; y < A.height(); y++) { + const uint32_t count = A.getRow(y).size(); + for (uint32_t e = 0; e < count; e++) { + uint32_t column = A.getRow(y)[e].x; + uint32_t ix = column; + bool isFree = true; + for (uint32_t i = 0; i < lockedCount; i++) { + isFree &= (lockedParameters[i] != column); + if (column > lockedParameters[i]) ix--; // shift columns + } + if (isFree) { + Af.setCoefficient(ix, y, A.getRow(y)[e].v); + } + } + } + // Remove elements from x + FullVector xf(D); + for (uint32_t i = 0, j = 0; i < A.width(); i++) { + bool isFree = true; + for (uint32_t l = 0; l < lockedCount; l++) { + isFree &= (lockedParameters[l] != i); + } + if (isFree) { + xf[j++] = x[i]; + } + } + // Solve reduced system. + bool result = LeastSquaresSolver(Af, b_Alxl, xf, epsilon); + // Copy results back to x. + for (uint32_t i = 0, j = 0; i < A.width(); i++) { + bool isFree = true; + for (uint32_t l = 0; l < lockedCount; l++) { + isFree &= (lockedParameters[l] != i); + } + if (isFree) { + x[i] = xf[j++]; + } + } + return result; + } + +private: + /** + * Compute the solution of the sparse linear system Ab=x using the Conjugate + * Gradient method. + * + * Solving sparse linear systems: + * (1) A·x = b + * + * The conjugate gradient algorithm solves (1) only in the case that A is + * symmetric and positive definite. It is based on the idea of minimizing the + * function + * + * (2) f(x) = 1/2·x·A·x - b·x + * + * This function is minimized when its gradient + * + * (3) df = A·x - b + * + * is zero, which is equivalent to (1). The minimization is carried out by + * generating a succession of search directions p.k and improved minimizers x.k. + * At each stage a quantity alfa.k is found that minimizes f(x.k + alfa.k·p.k), + * and x.k+1 is set equal to the new point x.k + alfa.k·p.k. The p.k and x.k are + * built up in such a way that x.k+1 is also the minimizer of f over the whole + * vector space of directions already taken, {p.1, p.2, . . . , p.k}. After N + * iterations you arrive at the minimizer over the entire vector space, i.e., the + * solution to (1). + * + * For a really good explanation of the method see: + * + * "An Introduction to the Conjugate Gradient Method Without the Agonizing Pain", + * Jonhathan Richard Shewchuk. + * + **/ + static bool ConjugateGradientSolver(const sparse::Matrix &A, const FullVector &b, FullVector &x, float epsilon) { + xaDebugAssert(A.isSquare()); + xaDebugAssert(A.width() == b.dimension()); + xaDebugAssert(A.width() == x.dimension()); + int i = 0; + const int D = A.width(); + const int i_max = 4 * D; // Convergence should be linear, but in some cases, it's not. + FullVector r(D); // residual + FullVector p(D); // search direction + FullVector q(D); // + float delta_0; + float delta_old; + float delta_new; + float alpha; + float beta; + // r = b - A·x; + sparse::copy(b, r); + sparse::sgemv(-1, A, x, 1, r); + // p = r; + sparse::copy(r, p); + delta_new = sparse::dot(r, r); + delta_0 = delta_new; + while (i < i_max && delta_new > epsilon * epsilon * delta_0) { + i++; + // q = A·p + mult(A, p, q); + // alpha = delta_new / p·q + alpha = delta_new / sparse::dot(p, q); + // x = alfa·p + x + sparse::saxpy(alpha, p, x); + if ((i & 31) == 0) { // recompute r after 32 steps + // r = b - A·x + sparse::copy(b, r); + sparse::sgemv(-1, A, x, 1, r); + } else { + // r = r - alpha·q + sparse::saxpy(-alpha, q, r); + } + delta_old = delta_new; + delta_new = sparse::dot(r, r); + beta = delta_new / delta_old; + // p = beta·p + r + sparse::scal(beta, p); + sparse::saxpy(1, r, p); + } + return delta_new <= epsilon * epsilon * delta_0; + } + + // Conjugate gradient with preconditioner. + static bool ConjugateGradientSolver(const JacobiPreconditioner &preconditioner, const sparse::Matrix &A, const FullVector &b, FullVector &x, float epsilon) { + xaDebugAssert(A.isSquare()); + xaDebugAssert(A.width() == b.dimension()); + xaDebugAssert(A.width() == x.dimension()); + int i = 0; + const int D = A.width(); + const int i_max = 4 * D; // Convergence should be linear, but in some cases, it's not. + FullVector r(D); // residual + FullVector p(D); // search direction + FullVector q(D); // + FullVector s(D); // preconditioned + float delta_0; + float delta_old; + float delta_new; + float alpha; + float beta; + // r = b - A·x + sparse::copy(b, r); + sparse::sgemv(-1, A, x, 1, r); + // p = M^-1 · r + preconditioner.apply(r, p); + delta_new = sparse::dot(r, p); + delta_0 = delta_new; + while (i < i_max && delta_new > epsilon * epsilon * delta_0) { + i++; + // q = A·p + mult(A, p, q); + // alpha = delta_new / p·q + alpha = delta_new / sparse::dot(p, q); + // x = alfa·p + x + sparse::saxpy(alpha, p, x); + if ((i & 31) == 0) { // recompute r after 32 steps + // r = b - A·x + sparse::copy(b, r); + sparse::sgemv(-1, A, x, 1, r); + } else { + // r = r - alfa·q + sparse::saxpy(-alpha, q, r); + } + // s = M^-1 · r + preconditioner.apply(r, s); + delta_old = delta_new; + delta_new = sparse::dot(r, s); + beta = delta_new / delta_old; + // p = s + beta·p + sparse::scal(beta, p); + sparse::saxpy(1, s, p); + } + return delta_new <= epsilon * epsilon * delta_0; + } + + static bool SymmetricSolver(const sparse::Matrix &A, const FullVector &b, FullVector &x, float epsilon = 1e-5f) { + xaDebugAssert(A.height() == A.width()); + xaDebugAssert(A.height() == b.dimension()); + xaDebugAssert(b.dimension() == x.dimension()); + JacobiPreconditioner jacobi(A, true); + return ConjugateGradientSolver(jacobi, A, b, x, epsilon); + } +}; + +namespace param { +class Atlas; +class Chart; + +// Fast sweep in 3 directions +static bool findApproximateDiameterVertices(halfedge::Mesh *mesh, halfedge::Vertex **a, halfedge::Vertex **b) { + xaDebugAssert(mesh != NULL); + xaDebugAssert(a != NULL); + xaDebugAssert(b != NULL); + const uint32_t vertexCount = mesh->vertexCount(); + halfedge::Vertex *minVertex[3]; + halfedge::Vertex *maxVertex[3]; + minVertex[0] = minVertex[1] = minVertex[2] = NULL; + maxVertex[0] = maxVertex[1] = maxVertex[2] = NULL; + for (uint32_t v = 1; v < vertexCount; v++) { + halfedge::Vertex *vertex = mesh->vertexAt(v); + xaDebugAssert(vertex != NULL); + if (vertex->isBoundary()) { + minVertex[0] = minVertex[1] = minVertex[2] = vertex; + maxVertex[0] = maxVertex[1] = maxVertex[2] = vertex; + break; + } + } + if (minVertex[0] == NULL) { + // Input mesh has not boundaries. + return false; + } + for (uint32_t v = 1; v < vertexCount; v++) { + halfedge::Vertex *vertex = mesh->vertexAt(v); + xaDebugAssert(vertex != NULL); + if (!vertex->isBoundary()) { + // Skip interior vertices. + continue; + } + if (vertex->pos.x < minVertex[0]->pos.x) + minVertex[0] = vertex; + else if (vertex->pos.x > maxVertex[0]->pos.x) + maxVertex[0] = vertex; + if (vertex->pos.y < minVertex[1]->pos.y) + minVertex[1] = vertex; + else if (vertex->pos.y > maxVertex[1]->pos.y) + maxVertex[1] = vertex; + if (vertex->pos.z < minVertex[2]->pos.z) + minVertex[2] = vertex; + else if (vertex->pos.z > maxVertex[2]->pos.z) + maxVertex[2] = vertex; + } + float lengths[3]; + for (int i = 0; i < 3; i++) { + lengths[i] = length(minVertex[i]->pos - maxVertex[i]->pos); + } + if (lengths[0] > lengths[1] && lengths[0] > lengths[2]) { + *a = minVertex[0]; + *b = maxVertex[0]; + } else if (lengths[1] > lengths[2]) { + *a = minVertex[1]; + *b = maxVertex[1]; + } else { + *a = minVertex[2]; + *b = maxVertex[2]; + } + return true; +} + +// Conformal relations from Brecht Van Lommel (based on ABF): + +static float vec_angle_cos(Vector3::Arg v1, Vector3::Arg v2, Vector3::Arg v3) { + Vector3 d1 = v1 - v2; + Vector3 d2 = v3 - v2; + return clamp(dot(d1, d2) / (length(d1) * length(d2)), -1.0f, 1.0f); +} + +static float vec_angle(Vector3::Arg v1, Vector3::Arg v2, Vector3::Arg v3) { + float dot = vec_angle_cos(v1, v2, v3); + return acosf(dot); +} + +static void triangle_angles(Vector3::Arg v1, Vector3::Arg v2, Vector3::Arg v3, float *a1, float *a2, float *a3) { + *a1 = vec_angle(v3, v1, v2); + *a2 = vec_angle(v1, v2, v3); + *a3 = PI - *a2 - *a1; +} + +static void setup_abf_relations(sparse::Matrix &A, int row, const halfedge::Vertex *v0, const halfedge::Vertex *v1, const halfedge::Vertex *v2) { + int id0 = v0->id; + int id1 = v1->id; + int id2 = v2->id; + Vector3 p0 = v0->pos; + Vector3 p1 = v1->pos; + Vector3 p2 = v2->pos; + // @@ IC: Wouldn't it be more accurate to return cos and compute 1-cos^2? + // It does indeed seem to be a little bit more robust. + // @@ Need to revisit this more carefully! + float a0, a1, a2; + triangle_angles(p0, p1, p2, &a0, &a1, &a2); + float s0 = sinf(a0); + float s1 = sinf(a1); + float s2 = sinf(a2); + if (s1 > s0 && s1 > s2) { + std::swap(s1, s2); + std::swap(s0, s1); + std::swap(a1, a2); + std::swap(a0, a1); + std::swap(id1, id2); + std::swap(id0, id1); + } else if (s0 > s1 && s0 > s2) { + std::swap(s0, s2); + std::swap(s0, s1); + std::swap(a0, a2); + std::swap(a0, a1); + std::swap(id0, id2); + std::swap(id0, id1); + } + float c0 = cosf(a0); + float ratio = (s2 == 0.0f) ? 1.0f : s1 / s2; + float cosine = c0 * ratio; + float sine = s0 * ratio; + // Note : 2*id + 0 --> u + // 2*id + 1 --> v + int u0_id = 2 * id0 + 0; + int v0_id = 2 * id0 + 1; + int u1_id = 2 * id1 + 0; + int v1_id = 2 * id1 + 1; + int u2_id = 2 * id2 + 0; + int v2_id = 2 * id2 + 1; + // Real part + A.setCoefficient(u0_id, 2 * row + 0, cosine - 1.0f); + A.setCoefficient(v0_id, 2 * row + 0, -sine); + A.setCoefficient(u1_id, 2 * row + 0, -cosine); + A.setCoefficient(v1_id, 2 * row + 0, sine); + A.setCoefficient(u2_id, 2 * row + 0, 1); + // Imaginary part + A.setCoefficient(u0_id, 2 * row + 1, sine); + A.setCoefficient(v0_id, 2 * row + 1, cosine - 1.0f); + A.setCoefficient(u1_id, 2 * row + 1, -sine); + A.setCoefficient(v1_id, 2 * row + 1, -cosine); + A.setCoefficient(v2_id, 2 * row + 1, 1); +} + +bool computeLeastSquaresConformalMap(halfedge::Mesh *mesh) { + xaDebugAssert(mesh != NULL); + // For this to work properly, mesh should not have colocals that have the same + // attributes, unless you want the vertices to actually have different texcoords. + const uint32_t vertexCount = mesh->vertexCount(); + const uint32_t D = 2 * vertexCount; + const uint32_t N = 2 * halfedge::countMeshTriangles(mesh); + // N is the number of equations (one per triangle) + // D is the number of variables (one per vertex; there are 2 pinned vertices). + if (N < D - 4) { + return false; + } + sparse::Matrix A(D, N); + FullVector b(N); + FullVector x(D); + // Fill b: + b.fill(0.0f); + // Fill x: + halfedge::Vertex *v0; + halfedge::Vertex *v1; + if (!findApproximateDiameterVertices(mesh, &v0, &v1)) { + // Mesh has no boundaries. + return false; + } + if (v0->tex == v1->tex) { + // LSCM expects an existing parameterization. + return false; + } + for (uint32_t v = 0; v < vertexCount; v++) { + halfedge::Vertex *vertex = mesh->vertexAt(v); + xaDebugAssert(vertex != NULL); + // Initial solution. + x[2 * v + 0] = vertex->tex.x; + x[2 * v + 1] = vertex->tex.y; + } + // Fill A: + const uint32_t faceCount = mesh->faceCount(); + for (uint32_t f = 0, t = 0; f < faceCount; f++) { + const halfedge::Face *face = mesh->faceAt(f); + xaDebugAssert(face != NULL); + xaDebugAssert(face->edgeCount() == 3); + const halfedge::Vertex *vertex0 = NULL; + for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const halfedge::Edge *edge = it.current(); + xaAssert(edge != NULL); + if (vertex0 == NULL) { + vertex0 = edge->vertex; + } else if (edge->next->vertex != vertex0) { + const halfedge::Vertex *vertex1 = edge->from(); + const halfedge::Vertex *vertex2 = edge->to(); + setup_abf_relations(A, t, vertex0, vertex1, vertex2); + //setup_conformal_map_relations(A, t, vertex0, vertex1, vertex2); + t++; + } + } + } + const uint32_t lockedParameters[] = { + 2 * v0->id + 0, + 2 * v0->id + 1, + 2 * v1->id + 0, + 2 * v1->id + 1 + }; + // Solve + Solver::LeastSquaresSolver(A, b, x, lockedParameters, 4, 0.000001f); + // Map x back to texcoords: + for (uint32_t v = 0; v < vertexCount; v++) { + halfedge::Vertex *vertex = mesh->vertexAt(v); + xaDebugAssert(vertex != NULL); + vertex->tex = Vector2(x[2 * v + 0], x[2 * v + 1]); + } + return true; +} + +bool computeOrthogonalProjectionMap(halfedge::Mesh *mesh) { + Vector3 axis[2]; + uint32_t vertexCount = mesh->vertexCount(); + std::vector<Vector3> points(vertexCount); + points.resize(vertexCount); + for (uint32_t i = 0; i < vertexCount; i++) { + points[i] = mesh->vertexAt(i)->pos; + } + // Avoid redundant computations. + float matrix[6]; + Fit::computeCovariance(vertexCount, points.data(), matrix); + if (matrix[0] == 0 && matrix[3] == 0 && matrix[5] == 0) { + return false; + } + float eigenValues[3]; + Vector3 eigenVectors[3]; + if (!Fit::eigenSolveSymmetric3(matrix, eigenValues, eigenVectors)) { + return false; + } + axis[0] = normalize(eigenVectors[0]); + axis[1] = normalize(eigenVectors[1]); + // Project vertices to plane. + for (halfedge::Mesh::VertexIterator it(mesh->vertices()); !it.isDone(); it.advance()) { + halfedge::Vertex *vertex = it.current(); + vertex->tex.x = dot(axis[0], vertex->pos); + vertex->tex.y = dot(axis[1], vertex->pos); + } + return true; +} + +void computeSingleFaceMap(halfedge::Mesh *mesh) { + xaDebugAssert(mesh != NULL); + xaDebugAssert(mesh->faceCount() == 1); + halfedge::Face *face = mesh->faceAt(0); + xaAssert(face != NULL); + Vector3 p0 = face->edge->from()->pos; + Vector3 p1 = face->edge->to()->pos; + Vector3 X = normalizeSafe(p1 - p0, Vector3(0.0f), 0.0f); + Vector3 Z = face->normal(); + Vector3 Y = normalizeSafe(cross(Z, X), Vector3(0.0f), 0.0f); + uint32_t i = 0; + for (halfedge::Face::EdgeIterator it(face->edges()); !it.isDone(); it.advance(), i++) { + halfedge::Vertex *vertex = it.vertex(); + xaAssert(vertex != NULL); + if (i == 0) { + vertex->tex = Vector2(0); + } else { + Vector3 pn = vertex->pos; + float xn = dot((pn - p0), X); + float yn = dot((pn - p0), Y); + vertex->tex = Vector2(xn, yn); + } + } +} + +// Dummy implementation of a priority queue using sort at insertion. +// - Insertion is o(n) +// - Smallest element goes at the end, so that popping it is o(1). +// - Resorting is n*log(n) +// @@ Number of elements in the queue is usually small, and we'd have to rebalance often. I'm not sure it's worth implementing a heap. +// @@ Searcing at removal would remove the need for sorting when priorities change. +struct PriorityQueue { + PriorityQueue(uint32_t size = UINT_MAX) : + maxSize(size) {} + + void push(float priority, uint32_t face) { + uint32_t i = 0; + const uint32_t count = pairs.size(); + for (; i < count; i++) { + if (pairs[i].priority > priority) break; + } + Pair p = { priority, face }; + pairs.insert(pairs.begin() + i, p); + if (pairs.size() > maxSize) { + pairs.erase(pairs.begin()); + } + } + + // push face out of order, to be sorted later. + void push(uint32_t face) { + Pair p = { 0.0f, face }; + pairs.push_back(p); + } + + uint32_t pop() { + uint32_t f = pairs.back().face; + pairs.pop_back(); + return f; + } + + void sort() { + //sort(pairs); // @@ My intro sort appears to be much slower than it should! + std::sort(pairs.begin(), pairs.end()); + } + + void clear() { + pairs.clear(); + } + + uint32_t count() const { + return pairs.size(); + } + + float firstPriority() const { + return pairs.back().priority; + } + + const uint32_t maxSize; + + struct Pair { + bool operator<(const Pair &p) const { + return priority > p.priority; // !! Sort in inverse priority order! + } + + float priority; + uint32_t face; + }; + + std::vector<Pair> pairs; +}; + +struct ChartBuildData { + ChartBuildData(int p_id) : + id(p_id) { + planeNormal = Vector3(0); + centroid = Vector3(0); + coneAxis = Vector3(0); + coneAngle = 0; + area = 0; + boundaryLength = 0; + normalSum = Vector3(0); + centroidSum = Vector3(0); + } + + int id; + + // Proxy info: + Vector3 planeNormal; + Vector3 centroid; + Vector3 coneAxis; + float coneAngle; + + float area; + float boundaryLength; + Vector3 normalSum; + Vector3 centroidSum; + + std::vector<uint32_t> seeds; // @@ These could be a pointers to the halfedge faces directly. + std::vector<uint32_t> faces; + PriorityQueue candidates; +}; + +struct AtlasBuilder { + AtlasBuilder(const halfedge::Mesh *m) : + mesh(m), + facesLeft(m->faceCount()) { + const uint32_t faceCount = m->faceCount(); + faceChartArray.resize(faceCount, -1); + faceCandidateArray.resize(faceCount, (uint32_t)-1); + // @@ Floyd for the whole mesh is too slow. We could compute floyd progressively per patch as the patch grows. We need a better solution to compute most central faces. + //computeShortestPaths(); + // Precompute edge lengths and face areas. + uint32_t edgeCount = m->edgeCount(); + edgeLengths.resize(edgeCount); + for (uint32_t i = 0; i < edgeCount; i++) { + uint32_t id = m->edgeAt(i)->id; + xaDebugAssert(id / 2 == i); +#ifdef NDEBUG + id = 0; // silence unused parameter warning +#endif + edgeLengths[i] = m->edgeAt(i)->length(); + } + faceAreas.resize(faceCount); + for (uint32_t i = 0; i < faceCount; i++) { + faceAreas[i] = m->faceAt(i)->area(); + } + } + + ~AtlasBuilder() { + const uint32_t chartCount = chartArray.size(); + for (uint32_t i = 0; i < chartCount; i++) { + delete chartArray[i]; + } + } + + void markUnchartedFaces(const std::vector<uint32_t> &unchartedFaces) { + const uint32_t unchartedFaceCount = unchartedFaces.size(); + for (uint32_t i = 0; i < unchartedFaceCount; i++) { + uint32_t f = unchartedFaces[i]; + faceChartArray[f] = -2; + //faceCandidateArray[f] = -2; // @@ ? + removeCandidate(f); + } + xaDebugAssert(facesLeft >= unchartedFaceCount); + facesLeft -= unchartedFaceCount; + } + + void computeShortestPaths() { + const uint32_t faceCount = mesh->faceCount(); + shortestPaths.resize(faceCount * faceCount, FLT_MAX); + // Fill edges: + for (uint32_t i = 0; i < faceCount; i++) { + shortestPaths[i * faceCount + i] = 0.0f; + const halfedge::Face *face_i = mesh->faceAt(i); + Vector3 centroid_i = face_i->centroid(); + for (halfedge::Face::ConstEdgeIterator it(face_i->edges()); !it.isDone(); it.advance()) { + const halfedge::Edge *edge = it.current(); + if (!edge->isBoundary()) { + const halfedge::Face *face_j = edge->pair->face; + uint32_t j = face_j->id; + Vector3 centroid_j = face_j->centroid(); + shortestPaths[i * faceCount + j] = shortestPaths[j * faceCount + i] = length(centroid_i - centroid_j); + } + } + } + // Use Floyd-Warshall algorithm to compute all paths: + for (uint32_t k = 0; k < faceCount; k++) { + for (uint32_t i = 0; i < faceCount; i++) { + for (uint32_t j = 0; j < faceCount; j++) { + shortestPaths[i * faceCount + j] = std::min(shortestPaths[i * faceCount + j], shortestPaths[i * faceCount + k] + shortestPaths[k * faceCount + j]); + } + } + } + } + + void placeSeeds(float threshold, uint32_t maxSeedCount) { + // Instead of using a predefiened number of seeds: + // - Add seeds one by one, growing chart until a certain treshold. + // - Undo charts and restart growing process. + // @@ How can we give preference to faces far from sharp features as in the LSCM paper? + // - those points can be found using a simple flood filling algorithm. + // - how do we weight the probabilities? + for (uint32_t i = 0; i < maxSeedCount; i++) { + if (facesLeft == 0) { + // No faces left, stop creating seeds. + break; + } + createRandomChart(threshold); + } + } + + void createRandomChart(float threshold) { + ChartBuildData *chart = new ChartBuildData(chartArray.size()); + chartArray.push_back(chart); + // Pick random face that is not used by any chart yet. + uint32_t randomFaceIdx = rand.getRange(facesLeft - 1); + uint32_t i = 0; + for (uint32_t f = 0; f != randomFaceIdx; f++, i++) { + while (faceChartArray[i] != -1) + i++; + } + while (faceChartArray[i] != -1) + i++; + chart->seeds.push_back(i); + addFaceToChart(chart, i, true); + // Grow the chart as much as possible within the given threshold. + growChart(chart, threshold * 0.5f, facesLeft); + //growCharts(threshold - threshold * 0.75f / chartCount(), facesLeft); + } + + void addFaceToChart(ChartBuildData *chart, uint32_t f, bool recomputeProxy = false) { + // Add face to chart. + chart->faces.push_back(f); + xaDebugAssert(faceChartArray[f] == -1); + faceChartArray[f] = chart->id; + facesLeft--; + // Update area and boundary length. + chart->area = evaluateChartArea(chart, f); + chart->boundaryLength = evaluateBoundaryLength(chart, f); + chart->normalSum = evaluateChartNormalSum(chart, f); + chart->centroidSum = evaluateChartCentroidSum(chart, f); + if (recomputeProxy) { + // Update proxy and candidate's priorities. + updateProxy(chart); + } + // Update candidates. + removeCandidate(f); + updateCandidates(chart, f); + updatePriorities(chart); + } + + // Returns true if any of the charts can grow more. + bool growCharts(float threshold, uint32_t faceCount) { + // Using one global list. + faceCount = std::min(faceCount, facesLeft); + for (uint32_t i = 0; i < faceCount; i++) { + const Candidate &candidate = getBestCandidate(); + if (candidate.metric > threshold) { + return false; // Can't grow more. + } + addFaceToChart(candidate.chart, candidate.face); + } + return facesLeft != 0; // Can continue growing. + } + + bool growChart(ChartBuildData *chart, float threshold, uint32_t faceCount) { + // Try to add faceCount faces within threshold to chart. + for (uint32_t i = 0; i < faceCount;) { + if (chart->candidates.count() == 0 || chart->candidates.firstPriority() > threshold) { + return false; + } + uint32_t f = chart->candidates.pop(); + if (faceChartArray[f] == -1) { + addFaceToChart(chart, f); + i++; + } + } + if (chart->candidates.count() == 0 || chart->candidates.firstPriority() > threshold) { + return false; + } + return true; + } + + void resetCharts() { + const uint32_t faceCount = mesh->faceCount(); + for (uint32_t i = 0; i < faceCount; i++) { + faceChartArray[i] = -1; + faceCandidateArray[i] = (uint32_t)-1; + } + facesLeft = faceCount; + candidateArray.clear(); + const uint32_t chartCount = chartArray.size(); + for (uint32_t i = 0; i < chartCount; i++) { + ChartBuildData *chart = chartArray[i]; + const uint32_t seed = chart->seeds.back(); + chart->area = 0.0f; + chart->boundaryLength = 0.0f; + chart->normalSum = Vector3(0); + chart->centroidSum = Vector3(0); + chart->faces.clear(); + chart->candidates.clear(); + addFaceToChart(chart, seed); + } + } + + void updateCandidates(ChartBuildData *chart, uint32_t f) { + const halfedge::Face *face = mesh->faceAt(f); + // Traverse neighboring faces, add the ones that do not belong to any chart yet. + for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const halfedge::Edge *edge = it.current()->pair; + if (!edge->isBoundary()) { + uint32_t faceId = edge->face->id; + if (faceChartArray[faceId] == -1) { + chart->candidates.push(faceId); + } + } + } + } + + void updateProxies() { + const uint32_t chartCount = chartArray.size(); + for (uint32_t i = 0; i < chartCount; i++) { + updateProxy(chartArray[i]); + } + } + + void updateProxy(ChartBuildData *chart) { + //#pragma message(NV_FILE_LINE "TODO: Use best fit plane instead of average normal.") + chart->planeNormal = normalizeSafe(chart->normalSum, Vector3(0), 0.0f); + chart->centroid = chart->centroidSum / float(chart->faces.size()); + } + + bool relocateSeeds() { + bool anySeedChanged = false; + const uint32_t chartCount = chartArray.size(); + for (uint32_t i = 0; i < chartCount; i++) { + if (relocateSeed(chartArray[i])) { + anySeedChanged = true; + } + } + return anySeedChanged; + } + + bool relocateSeed(ChartBuildData *chart) { + Vector3 centroid = computeChartCentroid(chart); + const uint32_t N = 10; // @@ Hardcoded to 10? + PriorityQueue bestTriangles(N); + // Find the first N triangles that fit the proxy best. + const uint32_t faceCount = chart->faces.size(); + for (uint32_t i = 0; i < faceCount; i++) { + float priority = evaluateProxyFitMetric(chart, chart->faces[i]); + bestTriangles.push(priority, chart->faces[i]); + } + // Of those, choose the most central triangle. + uint32_t mostCentral; + float maxDistance = -1; + const uint32_t bestCount = bestTriangles.count(); + for (uint32_t i = 0; i < bestCount; i++) { + const halfedge::Face *face = mesh->faceAt(bestTriangles.pairs[i].face); + Vector3 faceCentroid = face->triangleCenter(); + float distance = length(centroid - faceCentroid); + if (distance > maxDistance) { + maxDistance = distance; + mostCentral = bestTriangles.pairs[i].face; + } + } + xaDebugAssert(maxDistance >= 0); + // In order to prevent k-means cyles we record all the previously chosen seeds. + uint32_t index = std::find(chart->seeds.begin(), chart->seeds.end(), mostCentral) - chart->seeds.begin(); + if (index < chart->seeds.size()) { + // Move new seed to the end of the seed array. + uint32_t last = chart->seeds.size() - 1; + std::swap(chart->seeds[index], chart->seeds[last]); + return false; + } else { + // Append new seed. + chart->seeds.push_back(mostCentral); + return true; + } + } + + void updatePriorities(ChartBuildData *chart) { + // Re-evaluate candidate priorities. + uint32_t candidateCount = chart->candidates.count(); + for (uint32_t i = 0; i < candidateCount; i++) { + chart->candidates.pairs[i].priority = evaluatePriority(chart, chart->candidates.pairs[i].face); + if (faceChartArray[chart->candidates.pairs[i].face] == -1) { + updateCandidate(chart, chart->candidates.pairs[i].face, chart->candidates.pairs[i].priority); + } + } + // Sort candidates. + chart->candidates.sort(); + } + + // Evaluate combined metric. + float evaluatePriority(ChartBuildData *chart, uint32_t face) { + // Estimate boundary length and area: + float newBoundaryLength = evaluateBoundaryLength(chart, face); + float newChartArea = evaluateChartArea(chart, face); + float F = evaluateProxyFitMetric(chart, face); + float C = evaluateRoundnessMetric(chart, face, newBoundaryLength, newChartArea); + float P = evaluateStraightnessMetric(chart, face); + // Penalize faces that cross seams, reward faces that close seams or reach boundaries. + float N = evaluateNormalSeamMetric(chart, face); + float T = evaluateTextureSeamMetric(chart, face); + //float R = evaluateCompletenessMetric(chart, face); + //float D = evaluateDihedralAngleMetric(chart, face); + // @@ Add a metric based on local dihedral angle. + // @@ Tweaking the normal and texture seam metrics. + // - Cause more impedance. Never cross 90 degree edges. + // - + float cost = float( + options.proxyFitMetricWeight * F + + options.roundnessMetricWeight * C + + options.straightnessMetricWeight * P + + options.normalSeamMetricWeight * N + + options.textureSeamMetricWeight * T); + // Enforce limits strictly: + if (newChartArea > options.maxChartArea) cost = FLT_MAX; + if (newBoundaryLength > options.maxBoundaryLength) cost = FLT_MAX; + // Make sure normal seams are fully respected: + if (options.normalSeamMetricWeight >= 1000 && N != 0) cost = FLT_MAX; + xaAssert(std::isfinite(cost)); + return cost; + } + + // Returns a value in [0-1]. + float evaluateProxyFitMetric(ChartBuildData *chart, uint32_t f) { + const halfedge::Face *face = mesh->faceAt(f); + Vector3 faceNormal = face->triangleNormal(); + // Use plane fitting metric for now: + return 1 - dot(faceNormal, chart->planeNormal); // @@ normal deviations should be weighted by face area + } + + float evaluateRoundnessMetric(ChartBuildData *chart, uint32_t /*face*/, float newBoundaryLength, float newChartArea) { + float roundness = square(chart->boundaryLength) / chart->area; + float newRoundness = square(newBoundaryLength) / newChartArea; + if (newRoundness > roundness) { + return square(newBoundaryLength) / (newChartArea * 4 * PI); + } else { + // Offer no impedance to faces that improve roundness. + return 0; + } + } + + float evaluateStraightnessMetric(ChartBuildData *chart, uint32_t f) { + float l_out = 0.0f; + float l_in = 0.0f; + const halfedge::Face *face = mesh->faceAt(f); + for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const halfedge::Edge *edge = it.current(); + float l = edgeLengths[edge->id / 2]; + if (edge->isBoundary()) { + l_out += l; + } else { + uint32_t neighborFaceId = edge->pair->face->id; + if (faceChartArray[neighborFaceId] != chart->id) { + l_out += l; + } else { + l_in += l; + } + } + } + xaDebugAssert(l_in != 0.0f); // Candidate face must be adjacent to chart. @@ This is not true if the input mesh has zero-length edges. + float ratio = (l_out - l_in) / (l_out + l_in); + return std::min(ratio, 0.0f); // Only use the straightness metric to close gaps. + } + + float evaluateNormalSeamMetric(ChartBuildData *chart, uint32_t f) { + float seamFactor = 0.0f; + float totalLength = 0.0f; + const halfedge::Face *face = mesh->faceAt(f); + for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const halfedge::Edge *edge = it.current(); + if (edge->isBoundary()) { + continue; + } + const uint32_t neighborFaceId = edge->pair->face->id; + if (faceChartArray[neighborFaceId] != chart->id) { + continue; + } + //float l = edge->length(); + float l = edgeLengths[edge->id / 2]; + totalLength += l; + if (!edge->isSeam()) { + continue; + } + // Make sure it's a normal seam. + if (edge->isNormalSeam()) { + float d0 = clamp(dot(edge->vertex->nor, edge->pair->next->vertex->nor), 0.0f, 1.0f); + float d1 = clamp(dot(edge->next->vertex->nor, edge->pair->vertex->nor), 0.0f, 1.0f); + l *= 1 - (d0 + d1) * 0.5f; + seamFactor += l; + } + } + if (seamFactor == 0) return 0.0f; + return seamFactor / totalLength; + } + + float evaluateTextureSeamMetric(ChartBuildData *chart, uint32_t f) { + float seamLength = 0.0f; + float totalLength = 0.0f; + const halfedge::Face *face = mesh->faceAt(f); + for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const halfedge::Edge *edge = it.current(); + if (edge->isBoundary()) { + continue; + } + const uint32_t neighborFaceId = edge->pair->face->id; + if (faceChartArray[neighborFaceId] != chart->id) { + continue; + } + //float l = edge->length(); + float l = edgeLengths[edge->id / 2]; + totalLength += l; + if (!edge->isSeam()) { + continue; + } + // Make sure it's a texture seam. + if (edge->isTextureSeam()) { + seamLength += l; + } + } + if (seamLength == 0.0f) { + return 0.0f; // Avoid division by zero. + } + return seamLength / totalLength; + } + + float evaluateChartArea(ChartBuildData *chart, uint32_t f) { + const halfedge::Face *face = mesh->faceAt(f); + return chart->area + faceAreas[face->id]; + } + + float evaluateBoundaryLength(ChartBuildData *chart, uint32_t f) { + float boundaryLength = chart->boundaryLength; + // Add new edges, subtract edges shared with the chart. + const halfedge::Face *face = mesh->faceAt(f); + for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const halfedge::Edge *edge = it.current(); + //float edgeLength = edge->length(); + float edgeLength = edgeLengths[edge->id / 2]; + if (edge->isBoundary()) { + boundaryLength += edgeLength; + } else { + uint32_t neighborFaceId = edge->pair->face->id; + if (faceChartArray[neighborFaceId] != chart->id) { + boundaryLength += edgeLength; + } else { + boundaryLength -= edgeLength; + } + } + } + return std::max(0.0f, boundaryLength); // @@ Hack! + } + + Vector3 evaluateChartNormalSum(ChartBuildData *chart, uint32_t f) { + const halfedge::Face *face = mesh->faceAt(f); + return chart->normalSum + face->triangleNormalAreaScaled(); + } + + Vector3 evaluateChartCentroidSum(ChartBuildData *chart, uint32_t f) { + const halfedge::Face *face = mesh->faceAt(f); + return chart->centroidSum + face->centroid(); + } + + Vector3 computeChartCentroid(const ChartBuildData *chart) { + Vector3 centroid(0); + const uint32_t faceCount = chart->faces.size(); + for (uint32_t i = 0; i < faceCount; i++) { + const halfedge::Face *face = mesh->faceAt(chart->faces[i]); + centroid += face->triangleCenter(); + } + return centroid / float(faceCount); + } + + void fillHoles(float threshold) { + while (facesLeft > 0) + createRandomChart(threshold); + } + + void mergeCharts() { + std::vector<float> sharedBoundaryLengths; + const uint32_t chartCount = chartArray.size(); + for (int c = chartCount - 1; c >= 0; c--) { + sharedBoundaryLengths.clear(); + sharedBoundaryLengths.resize(chartCount, 0.0f); + ChartBuildData *chart = chartArray[c]; + float externalBoundary = 0.0f; + const uint32_t faceCount = chart->faces.size(); + for (uint32_t i = 0; i < faceCount; i++) { + uint32_t f = chart->faces[i]; + const halfedge::Face *face = mesh->faceAt(f); + for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const halfedge::Edge *edge = it.current(); + //float l = edge->length(); + float l = edgeLengths[edge->id / 2]; + if (edge->isBoundary()) { + externalBoundary += l; + } else { + uint32_t neighborFace = edge->pair->face->id; + uint32_t neighborChart = faceChartArray[neighborFace]; + if (neighborChart != (uint32_t)c) { + if ((edge->isSeam() && (edge->isNormalSeam() || edge->isTextureSeam())) || neighborChart == -2) { + externalBoundary += l; + } else { + sharedBoundaryLengths[neighborChart] += l; + } + } + } + } + } + for (int cc = chartCount - 1; cc >= 0; cc--) { + if (cc == c) + continue; + ChartBuildData *chart2 = chartArray[cc]; + if (chart2 == NULL) + continue; + if (sharedBoundaryLengths[cc] > 0.8 * std::max(0.0f, chart->boundaryLength - externalBoundary)) { + // Try to avoid degenerate configurations. + if (chart2->boundaryLength > sharedBoundaryLengths[cc]) { + if (dot(chart2->planeNormal, chart->planeNormal) > -0.25) { + mergeChart(chart2, chart, sharedBoundaryLengths[cc]); + delete chart; + chartArray[c] = NULL; + break; + } + } + } + if (sharedBoundaryLengths[cc] > 0.20 * std::max(0.0f, chart->boundaryLength - externalBoundary)) { + // Compare proxies. + if (dot(chart2->planeNormal, chart->planeNormal) > 0) { + mergeChart(chart2, chart, sharedBoundaryLengths[cc]); + delete chart; + chartArray[c] = NULL; + break; + } + } + } + } + // Remove deleted charts. + for (int c = 0; c < int32_t(chartArray.size()); /*do not increment if removed*/) { + if (chartArray[c] == NULL) { + chartArray.erase(chartArray.begin() + c); + // Update faceChartArray. + const uint32_t faceCount = faceChartArray.size(); + for (uint32_t i = 0; i < faceCount; i++) { + xaDebugAssert(faceChartArray[i] != -1); + xaDebugAssert(faceChartArray[i] != c); + xaDebugAssert(faceChartArray[i] <= int32_t(chartArray.size())); + if (faceChartArray[i] > c) { + faceChartArray[i]--; + } + } + } else { + chartArray[c]->id = c; + c++; + } + } + } + + // @@ Cleanup. + struct Candidate { + uint32_t face; + ChartBuildData *chart; + float metric; + }; + + // @@ Get N best candidates in one pass. + const Candidate &getBestCandidate() const { + uint32_t best = 0; + float bestCandidateMetric = FLT_MAX; + const uint32_t candidateCount = candidateArray.size(); + xaAssert(candidateCount > 0); + for (uint32_t i = 0; i < candidateCount; i++) { + const Candidate &candidate = candidateArray[i]; + if (candidate.metric < bestCandidateMetric) { + bestCandidateMetric = candidate.metric; + best = i; + } + } + return candidateArray[best]; + } + + void removeCandidate(uint32_t f) { + int c = faceCandidateArray[f]; + if (c != -1) { + faceCandidateArray[f] = (uint32_t)-1; + if (c == int(candidateArray.size() - 1)) { + candidateArray.pop_back(); + } else { + // Replace with last. + candidateArray[c] = candidateArray[candidateArray.size() - 1]; + candidateArray.pop_back(); + faceCandidateArray[candidateArray[c].face] = c; + } + } + } + + void updateCandidate(ChartBuildData *chart, uint32_t f, float metric) { + if (faceCandidateArray[f] == -1) { + const uint32_t index = candidateArray.size(); + faceCandidateArray[f] = index; + candidateArray.resize(index + 1); + candidateArray[index].face = f; + candidateArray[index].chart = chart; + candidateArray[index].metric = metric; + } else { + int c = faceCandidateArray[f]; + xaDebugAssert(c != -1); + Candidate &candidate = candidateArray[c]; + xaDebugAssert(candidate.face == f); + if (metric < candidate.metric || chart == candidate.chart) { + candidate.metric = metric; + candidate.chart = chart; + } + } + } + + void mergeChart(ChartBuildData *owner, ChartBuildData *chart, float sharedBoundaryLength) { + const uint32_t faceCount = chart->faces.size(); + for (uint32_t i = 0; i < faceCount; i++) { + uint32_t f = chart->faces[i]; + xaDebugAssert(faceChartArray[f] == chart->id); + faceChartArray[f] = owner->id; + owner->faces.push_back(f); + } + // Update adjacencies? + owner->area += chart->area; + owner->boundaryLength += chart->boundaryLength - sharedBoundaryLength; + owner->normalSum += chart->normalSum; + owner->centroidSum += chart->centroidSum; + updateProxy(owner); + } + + uint32_t chartCount() const { return chartArray.size(); } + const std::vector<uint32_t> &chartFaces(uint32_t i) const { return chartArray[i]->faces; } + + const halfedge::Mesh *mesh; + uint32_t facesLeft; + std::vector<int> faceChartArray; + std::vector<ChartBuildData *> chartArray; + std::vector<float> shortestPaths; + std::vector<float> edgeLengths; + std::vector<float> faceAreas; + std::vector<Candidate> candidateArray; // + std::vector<uint32_t> faceCandidateArray; // Map face index to candidate index. + MTRand rand; + CharterOptions options; +}; + +/// A chart is a connected set of faces with a certain topology (usually a disk). +class Chart { +public: + Chart() : + m_isDisk(false), + m_isVertexMapped(false) {} + + void build(const halfedge::Mesh *originalMesh, const std::vector<uint32_t> &faceArray) { + // Copy face indices. + m_faceArray = faceArray; + const uint32_t meshVertexCount = originalMesh->vertexCount(); + m_chartMesh.reset(new halfedge::Mesh()); + m_unifiedMesh.reset(new halfedge::Mesh()); + std::vector<uint32_t> chartMeshIndices(meshVertexCount, (uint32_t)~0); + std::vector<uint32_t> unifiedMeshIndices(meshVertexCount, (uint32_t)~0); + // Add vertices. + const uint32_t faceCount = faceArray.size(); + for (uint32_t f = 0; f < faceCount; f++) { + const halfedge::Face *face = originalMesh->faceAt(faceArray[f]); + xaDebugAssert(face != NULL); + for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const halfedge::Vertex *vertex = it.current()->vertex; + const halfedge::Vertex *unifiedVertex = vertex->firstColocal(); + if (unifiedMeshIndices[unifiedVertex->id] == ~0) { + unifiedMeshIndices[unifiedVertex->id] = m_unifiedMesh->vertexCount(); + xaDebugAssert(vertex->pos == unifiedVertex->pos); + m_unifiedMesh->addVertex(vertex->pos); + } + if (chartMeshIndices[vertex->id] == ~0) { + chartMeshIndices[vertex->id] = m_chartMesh->vertexCount(); + m_chartToOriginalMap.push_back(vertex->original_id); + m_chartToUnifiedMap.push_back(unifiedMeshIndices[unifiedVertex->id]); + halfedge::Vertex *v = m_chartMesh->addVertex(vertex->pos); + v->nor = vertex->nor; + v->tex = vertex->tex; + } + } + } + // This is ignoring the canonical map: + // - Is it really necessary to link colocals? + m_chartMesh->linkColocals(); + //m_unifiedMesh->linkColocals(); // Not strictly necessary, no colocals in the unified mesh. # Wrong. + // This check is not valid anymore, if the original mesh vertices were linked with a canonical map, then it might have + // some colocal vertices that were unlinked. So, the unified mesh might have some duplicate vertices, because firstColocal() + // is not guaranteed to return the same vertex for two colocal vertices. + //xaAssert(m_chartMesh->colocalVertexCount() == m_unifiedMesh->vertexCount()); + // Is that OK? What happens in meshes were that happens? Does anything break? Apparently not... + std::vector<uint32_t> faceIndices; + faceIndices.reserve(7); + // Add faces. + for (uint32_t f = 0; f < faceCount; f++) { + const halfedge::Face *face = originalMesh->faceAt(faceArray[f]); + xaDebugAssert(face != NULL); + faceIndices.clear(); + for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const halfedge::Vertex *vertex = it.current()->vertex; + xaDebugAssert(vertex != NULL); + faceIndices.push_back(chartMeshIndices[vertex->id]); + } + m_chartMesh->addFace(faceIndices); + faceIndices.clear(); + for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const halfedge::Vertex *vertex = it.current()->vertex; + xaDebugAssert(vertex != NULL); + vertex = vertex->firstColocal(); + faceIndices.push_back(unifiedMeshIndices[vertex->id]); + } + m_unifiedMesh->addFace(faceIndices); + } + m_chartMesh->linkBoundary(); + m_unifiedMesh->linkBoundary(); + //exportMesh(m_unifiedMesh.ptr(), "debug_input.obj"); + if (m_unifiedMesh->splitBoundaryEdges()) { + m_unifiedMesh.reset(halfedge::unifyVertices(m_unifiedMesh.get())); + } + //exportMesh(m_unifiedMesh.ptr(), "debug_split.obj"); + // Closing the holes is not always the best solution and does not fix all the problems. + // We need to do some analysis of the holes and the genus to: + // - Find cuts that reduce genus. + // - Find cuts to connect holes. + // - Use minimal spanning trees or seamster. + if (!closeHoles()) { + /*static int pieceCount = 0; + StringBuilder fileName; + fileName.format("debug_hole_%d.obj", pieceCount++); + exportMesh(m_unifiedMesh.ptr(), fileName.str());*/ + } + m_unifiedMesh.reset(halfedge::triangulate(m_unifiedMesh.get())); + //exportMesh(m_unifiedMesh.ptr(), "debug_triangulated.obj"); + // Analyze chart topology. + halfedge::MeshTopology topology(m_unifiedMesh.get()); + m_isDisk = topology.isDisk(); + } + + void buildVertexMap(const halfedge::Mesh *originalMesh, const std::vector<uint32_t> &unchartedMaterialArray) { + xaAssert(m_chartMesh.get() == NULL && m_unifiedMesh.get() == NULL); + m_isVertexMapped = true; + // Build face indices. + m_faceArray.clear(); + const uint32_t meshFaceCount = originalMesh->faceCount(); + for (uint32_t f = 0; f < meshFaceCount; f++) { + const halfedge::Face *face = originalMesh->faceAt(f); + if (std::find(unchartedMaterialArray.begin(), unchartedMaterialArray.end(), face->material) != unchartedMaterialArray.end()) { + m_faceArray.push_back(f); + } + } + const uint32_t faceCount = m_faceArray.size(); + if (faceCount == 0) { + return; + } + // @@ The chartMesh construction is basically the same as with regular charts, don't duplicate! + const uint32_t meshVertexCount = originalMesh->vertexCount(); + m_chartMesh.reset(new halfedge::Mesh()); + std::vector<uint32_t> chartMeshIndices(meshVertexCount, (uint32_t)~0); + // Vertex map mesh only has disconnected vertices. + for (uint32_t f = 0; f < faceCount; f++) { + const halfedge::Face *face = originalMesh->faceAt(m_faceArray[f]); + xaDebugAssert(face != NULL); + for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const halfedge::Vertex *vertex = it.current()->vertex; + if (chartMeshIndices[vertex->id] == ~0) { + chartMeshIndices[vertex->id] = m_chartMesh->vertexCount(); + m_chartToOriginalMap.push_back(vertex->original_id); + halfedge::Vertex *v = m_chartMesh->addVertex(vertex->pos); + v->nor = vertex->nor; + v->tex = vertex->tex; // @@ Not necessary. + } + } + } + // @@ Link colocals using the original mesh canonical map? Build canonical map on the fly? Do we need to link colocals at all for this? + //m_chartMesh->linkColocals(); + std::vector<uint32_t> faceIndices; + faceIndices.reserve(7); + // Add faces. + for (uint32_t f = 0; f < faceCount; f++) { + const halfedge::Face *face = originalMesh->faceAt(m_faceArray[f]); + xaDebugAssert(face != NULL); + faceIndices.clear(); + for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const halfedge::Vertex *vertex = it.current()->vertex; + xaDebugAssert(vertex != NULL); + xaDebugAssert(chartMeshIndices[vertex->id] != ~0); + faceIndices.push_back(chartMeshIndices[vertex->id]); + } + halfedge::Face *new_face = m_chartMesh->addFace(faceIndices); + xaDebugAssert(new_face != NULL); +#ifdef NDEBUG + new_face = NULL; // silence unused parameter warning +#endif + } + m_chartMesh->linkBoundary(); + const uint32_t chartVertexCount = m_chartMesh->vertexCount(); + Box bounds; + bounds.clearBounds(); + for (uint32_t i = 0; i < chartVertexCount; i++) { + halfedge::Vertex *vertex = m_chartMesh->vertexAt(i); + bounds.addPointToBounds(vertex->pos); + } + ProximityGrid grid; + grid.init(bounds, chartVertexCount); + for (uint32_t i = 0; i < chartVertexCount; i++) { + halfedge::Vertex *vertex = m_chartMesh->vertexAt(i); + grid.add(vertex->pos, i); + } + uint32_t texelCount = 0; + const float positionThreshold = 0.01f; + const float normalThreshold = 0.01f; + uint32_t verticesVisited = 0; + uint32_t cellsVisited = 0; + std::vector<int> vertexIndexArray(chartVertexCount, -1); // Init all indices to -1. + // Traverse vertices in morton order. @@ It may be more interesting to sort them based on orientation. + const uint32_t cellCodeCount = grid.mortonCount(); + for (uint32_t cellCode = 0; cellCode < cellCodeCount; cellCode++) { + int cell = grid.mortonIndex(cellCode); + if (cell < 0) continue; + cellsVisited++; + const std::vector<uint32_t> &indexArray = grid.cellArray[cell].indexArray; + for (uint32_t i = 0; i < indexArray.size(); i++) { + uint32_t idx = indexArray[i]; + halfedge::Vertex *vertex = m_chartMesh->vertexAt(idx); + xaDebugAssert(vertexIndexArray[idx] == -1); + std::vector<uint32_t> neighbors; + grid.gather(vertex->pos, positionThreshold, /*ref*/ neighbors); + // Compare against all nearby vertices, cluster greedily. + for (uint32_t j = 0; j < neighbors.size(); j++) { + uint32_t otherIdx = neighbors[j]; + if (vertexIndexArray[otherIdx] != -1) { + halfedge::Vertex *otherVertex = m_chartMesh->vertexAt(otherIdx); + if (distance(vertex->pos, otherVertex->pos) < positionThreshold && + distance(vertex->nor, otherVertex->nor) < normalThreshold) { + vertexIndexArray[idx] = vertexIndexArray[otherIdx]; + break; + } + } + } + // If index not assigned, assign new one. + if (vertexIndexArray[idx] == -1) { + vertexIndexArray[idx] = texelCount++; + } + verticesVisited++; + } + } + xaDebugAssert(cellsVisited == grid.cellArray.size()); + xaDebugAssert(verticesVisited == chartVertexCount); + vertexMapWidth = ftoi_ceil(sqrtf(float(texelCount))); + vertexMapWidth = (vertexMapWidth + 3) & ~3; // Width aligned to 4. + vertexMapHeight = vertexMapWidth == 0 ? 0 : (texelCount + vertexMapWidth - 1) / vertexMapWidth; + //vertexMapHeight = (vertexMapHeight + 3) & ~3; // Height aligned to 4. + xaDebugAssert(vertexMapWidth >= vertexMapHeight); + xaPrint("Reduced vertex count from %d to %d.\n", chartVertexCount, texelCount); + // Lay down the clustered vertices in morton order. + std::vector<uint32_t> texelCodes(texelCount); + // For each texel, assign one morton code. + uint32_t texelCode = 0; + for (uint32_t i = 0; i < texelCount; i++) { + uint32_t x, y; + do { + x = morton::decodeMorton2X(texelCode); + y = morton::decodeMorton2Y(texelCode); + texelCode++; + } while (x >= uint32_t(vertexMapWidth) || y >= uint32_t(vertexMapHeight)); + texelCodes[i] = texelCode - 1; + } + for (uint32_t i = 0; i < chartVertexCount; i++) { + halfedge::Vertex *vertex = m_chartMesh->vertexAt(i); + int idx = vertexIndexArray[i]; + if (idx != -1) { + uint32_t tc = texelCodes[idx]; + uint32_t x = morton::decodeMorton2X(tc); + uint32_t y = morton::decodeMorton2Y(tc); + vertex->tex.x = float(x); + vertex->tex.y = float(y); + } + } + } + + bool closeHoles() { + xaDebugAssert(!m_isVertexMapped); + std::vector<halfedge::Edge *> boundaryEdges; + getBoundaryEdges(m_unifiedMesh.get(), boundaryEdges); + uint32_t boundaryCount = boundaryEdges.size(); + if (boundaryCount <= 1) { + // Nothing to close. + return true; + } + // Compute lengths and areas. + std::vector<float> boundaryLengths; + for (uint32_t i = 0; i < boundaryCount; i++) { + const halfedge::Edge *startEdge = boundaryEdges[i]; + xaAssert(startEdge->face == NULL); + //float boundaryEdgeCount = 0; + float boundaryLength = 0.0f; + //Vector3 boundaryCentroid(zero); + const halfedge::Edge *edge = startEdge; + do { + Vector3 t0 = edge->from()->pos; + Vector3 t1 = edge->to()->pos; + //boundaryEdgeCount++; + boundaryLength += length(t1 - t0); + //boundaryCentroid += edge->vertex()->pos; + edge = edge->next; + } while (edge != startEdge); + boundaryLengths.push_back(boundaryLength); + //boundaryCentroids.append(boundaryCentroid / boundaryEdgeCount); + } + // Find disk boundary. + uint32_t diskBoundary = 0; + float maxLength = boundaryLengths[0]; + for (uint32_t i = 1; i < boundaryCount; i++) { + if (boundaryLengths[i] > maxLength) { + maxLength = boundaryLengths[i]; + diskBoundary = i; + } + } + // Close holes. + for (uint32_t i = 0; i < boundaryCount; i++) { + if (diskBoundary == i) { + // Skip disk boundary. + continue; + } + halfedge::Edge *startEdge = boundaryEdges[i]; + xaDebugAssert(startEdge != NULL); + xaDebugAssert(startEdge->face == NULL); + std::vector<halfedge::Vertex *> vertexLoop; + std::vector<halfedge::Edge *> edgeLoop; + halfedge::Edge *edge = startEdge; + do { + halfedge::Vertex *vertex = edge->next->vertex; // edge->to() + uint32_t j; + for (j = 0; j < vertexLoop.size(); j++) { + if (vertex->isColocal(vertexLoop[j])) { + break; + } + } + bool isCrossing = (j != vertexLoop.size()); + if (isCrossing) { + halfedge::Edge *prev = edgeLoop[j]; // Previous edge before the loop. + halfedge::Edge *next = edge->next; // Next edge after the loop. + xaDebugAssert(prev->to()->isColocal(next->from())); + // Close loop. + edgeLoop.push_back(edge); + closeLoop(j + 1, edgeLoop); + // Link boundary loop. + prev->setNext(next); + vertex->setEdge(next); + // Start over again. + vertexLoop.clear(); + edgeLoop.clear(); + edge = startEdge; + vertex = edge->to(); + } + vertexLoop.push_back(vertex); + edgeLoop.push_back(edge); + edge = edge->next; + } while (edge != startEdge); + closeLoop(0, edgeLoop); + } + getBoundaryEdges(m_unifiedMesh.get(), boundaryEdges); + boundaryCount = boundaryEdges.size(); + xaDebugAssert(boundaryCount == 1); + return boundaryCount == 1; + } + + bool isDisk() const { + return m_isDisk; + } + bool isVertexMapped() const { + return m_isVertexMapped; + } + + uint32_t vertexCount() const { + return m_chartMesh->vertexCount(); + } + uint32_t colocalVertexCount() const { + return m_unifiedMesh->vertexCount(); + } + + uint32_t faceCount() const { + return m_faceArray.size(); + } + uint32_t faceAt(uint32_t i) const { + return m_faceArray[i]; + } + + const halfedge::Mesh *chartMesh() const { + return m_chartMesh.get(); + } + halfedge::Mesh *chartMesh() { + return m_chartMesh.get(); + } + const halfedge::Mesh *unifiedMesh() const { + return m_unifiedMesh.get(); + } + halfedge::Mesh *unifiedMesh() { + return m_unifiedMesh.get(); + } + + //uint32_t vertexIndex(uint32_t i) const { return m_vertexIndexArray[i]; } + + uint32_t mapChartVertexToOriginalVertex(uint32_t i) const { + return m_chartToOriginalMap[i]; + } + uint32_t mapChartVertexToUnifiedVertex(uint32_t i) const { + return m_chartToUnifiedMap[i]; + } + + const std::vector<uint32_t> &faceArray() const { + return m_faceArray; + } + + // Transfer parameterization from unified mesh to chart mesh. + void transferParameterization() { + xaDebugAssert(!m_isVertexMapped); + uint32_t vertexCount = m_chartMesh->vertexCount(); + for (uint32_t v = 0; v < vertexCount; v++) { + halfedge::Vertex *vertex = m_chartMesh->vertexAt(v); + halfedge::Vertex *unifiedVertex = m_unifiedMesh->vertexAt(mapChartVertexToUnifiedVertex(v)); + vertex->tex = unifiedVertex->tex; + } + } + + float computeSurfaceArea() const { + return halfedge::computeSurfaceArea(m_chartMesh.get()) * scale; + } + + float computeParametricArea() const { + // This only makes sense in parameterized meshes. + xaDebugAssert(m_isDisk); + xaDebugAssert(!m_isVertexMapped); + return halfedge::computeParametricArea(m_chartMesh.get()); + } + + Vector2 computeParametricBounds() const { + // This only makes sense in parameterized meshes. + xaDebugAssert(m_isDisk); + xaDebugAssert(!m_isVertexMapped); + Box bounds; + bounds.clearBounds(); + uint32_t vertexCount = m_chartMesh->vertexCount(); + for (uint32_t v = 0; v < vertexCount; v++) { + halfedge::Vertex *vertex = m_chartMesh->vertexAt(v); + bounds.addPointToBounds(Vector3(vertex->tex, 0)); + } + return bounds.extents().xy(); + } + + float scale = 1.0f; + uint32_t vertexMapWidth; + uint32_t vertexMapHeight; + bool blockAligned = true; + +private: + bool closeLoop(uint32_t start, const std::vector<halfedge::Edge *> &loop) { + const uint32_t vertexCount = loop.size() - start; + xaDebugAssert(vertexCount >= 3); + if (vertexCount < 3) return false; + xaDebugAssert(loop[start]->vertex->isColocal(loop[start + vertexCount - 1]->to())); + // If the hole is planar, then we add a single face that will be properly triangulated later. + // If the hole is not planar, we add a triangle fan with a vertex at the hole centroid. + // This is still a bit of a hack. There surely are better hole filling algorithms out there. + std::vector<Vector3> points(vertexCount); + for (uint32_t i = 0; i < vertexCount; i++) { + points[i] = loop[start + i]->vertex->pos; + } + bool isPlanar = Fit::isPlanar(vertexCount, points.data()); + if (isPlanar) { + // Add face and connect edges. + halfedge::Face *face = m_unifiedMesh->addFace(); + for (uint32_t i = 0; i < vertexCount; i++) { + halfedge::Edge *edge = loop[start + i]; + edge->face = face; + edge->setNext(loop[start + (i + 1) % vertexCount]); + } + face->edge = loop[start]; + xaDebugAssert(face->isValid()); + } else { + // If the polygon is not planar, we just cross our fingers, and hope this will work: + // Compute boundary centroid: + Vector3 centroidPos(0); + for (uint32_t i = 0; i < vertexCount; i++) { + centroidPos += points[i]; + } + centroidPos *= (1.0f / vertexCount); + halfedge::Vertex *centroid = m_unifiedMesh->addVertex(centroidPos); + // Add one pair of edges for each boundary vertex. + for (uint32_t j = vertexCount - 1, i = 0; i < vertexCount; j = i++) { + halfedge::Face *face = m_unifiedMesh->addFace(centroid->id, loop[start + j]->vertex->id, loop[start + i]->vertex->id); + xaDebugAssert(face != NULL); +#ifdef NDEBUG + face = NULL; // silence unused parameter warning +#endif + } + } + return true; + } + + static void getBoundaryEdges(halfedge::Mesh *mesh, std::vector<halfedge::Edge *> &boundaryEdges) { + xaDebugAssert(mesh != NULL); + const uint32_t edgeCount = mesh->edgeCount(); + BitArray bitFlags(edgeCount); + bitFlags.clearAll(); + boundaryEdges.clear(); + // Search for boundary edges. Mark all the edges that belong to the same boundary. + for (uint32_t e = 0; e < edgeCount; e++) { + halfedge::Edge *startEdge = mesh->edgeAt(e); + if (startEdge != NULL && startEdge->isBoundary() && bitFlags.bitAt(e) == false) { + xaDebugAssert(startEdge->face != NULL); + xaDebugAssert(startEdge->pair->face == NULL); + startEdge = startEdge->pair; + const halfedge::Edge *edge = startEdge; + do { + xaDebugAssert(edge->face == NULL); + xaDebugAssert(bitFlags.bitAt(edge->id / 2) == false); + bitFlags.setBitAt(edge->id / 2); + edge = edge->next; + } while (startEdge != edge); + boundaryEdges.push_back(startEdge); + } + } + } + + // Chart mesh. + std::auto_ptr<halfedge::Mesh> m_chartMesh; + + std::auto_ptr<halfedge::Mesh> m_unifiedMesh; + bool m_isDisk; + bool m_isVertexMapped; + + // List of faces of the original mesh that belong to this chart. + std::vector<uint32_t> m_faceArray; + + // Map vertices of the chart mesh to vertices of the original mesh. + std::vector<uint32_t> m_chartToOriginalMap; + + std::vector<uint32_t> m_chartToUnifiedMap; +}; + +// Estimate quality of existing parameterization. +class ParameterizationQuality { +public: + ParameterizationQuality() { + m_totalTriangleCount = 0; + m_flippedTriangleCount = 0; + m_zeroAreaTriangleCount = 0; + m_parametricArea = 0.0f; + m_geometricArea = 0.0f; + m_stretchMetric = 0.0f; + m_maxStretchMetric = 0.0f; + m_conformalMetric = 0.0f; + m_authalicMetric = 0.0f; + } + + ParameterizationQuality(const halfedge::Mesh *mesh) { + xaDebugAssert(mesh != NULL); + m_totalTriangleCount = 0; + m_flippedTriangleCount = 0; + m_zeroAreaTriangleCount = 0; + m_parametricArea = 0.0f; + m_geometricArea = 0.0f; + m_stretchMetric = 0.0f; + m_maxStretchMetric = 0.0f; + m_conformalMetric = 0.0f; + m_authalicMetric = 0.0f; + const uint32_t faceCount = mesh->faceCount(); + for (uint32_t f = 0; f < faceCount; f++) { + const halfedge::Face *face = mesh->faceAt(f); + const halfedge::Vertex *vertex0 = NULL; + Vector3 p[3]; + Vector2 t[3]; + for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const halfedge::Edge *edge = it.current(); + if (vertex0 == NULL) { + vertex0 = edge->vertex; + p[0] = vertex0->pos; + t[0] = vertex0->tex; + } else if (edge->to() != vertex0) { + p[1] = edge->from()->pos; + p[2] = edge->to()->pos; + t[1] = edge->from()->tex; + t[2] = edge->to()->tex; + processTriangle(p, t); + } + } + } + if (m_flippedTriangleCount + m_zeroAreaTriangleCount == faceCount) { + // If all triangles are flipped, then none is. + m_flippedTriangleCount = 0; + } + xaDebugAssert(std::isfinite(m_parametricArea) && m_parametricArea >= 0); + xaDebugAssert(std::isfinite(m_geometricArea) && m_geometricArea >= 0); + xaDebugAssert(std::isfinite(m_stretchMetric)); + xaDebugAssert(std::isfinite(m_maxStretchMetric)); + xaDebugAssert(std::isfinite(m_conformalMetric)); + xaDebugAssert(std::isfinite(m_authalicMetric)); + } + + bool isValid() const { + return m_flippedTriangleCount == 0; // @@ Does not test for self-overlaps. + } + + float rmsStretchMetric() const { + if (m_geometricArea == 0) return 0.0f; + float normFactor = sqrtf(m_parametricArea / m_geometricArea); + return sqrtf(m_stretchMetric / m_geometricArea) * normFactor; + } + + float maxStretchMetric() const { + if (m_geometricArea == 0) return 0.0f; + float normFactor = sqrtf(m_parametricArea / m_geometricArea); + return m_maxStretchMetric * normFactor; + } + + float rmsConformalMetric() const { + if (m_geometricArea == 0) return 0.0f; + return sqrtf(m_conformalMetric / m_geometricArea); + } + + float maxAuthalicMetric() const { + if (m_geometricArea == 0) return 0.0f; + return sqrtf(m_authalicMetric / m_geometricArea); + } + + void operator+=(const ParameterizationQuality &pq) { + m_totalTriangleCount += pq.m_totalTriangleCount; + m_flippedTriangleCount += pq.m_flippedTriangleCount; + m_zeroAreaTriangleCount += pq.m_zeroAreaTriangleCount; + m_parametricArea += pq.m_parametricArea; + m_geometricArea += pq.m_geometricArea; + m_stretchMetric += pq.m_stretchMetric; + m_maxStretchMetric = std::max(m_maxStretchMetric, pq.m_maxStretchMetric); + m_conformalMetric += pq.m_conformalMetric; + m_authalicMetric += pq.m_authalicMetric; + } + +private: + void processTriangle(Vector3 q[3], Vector2 p[3]) { + m_totalTriangleCount++; + // Evaluate texture stretch metric. See: + // - "Texture Mapping Progressive Meshes", Sander, Snyder, Gortler & Hoppe + // - "Mesh Parameterization: Theory and Practice", Siggraph'07 Course Notes, Hormann, Levy & Sheffer. + float t1 = p[0].x; + float s1 = p[0].y; + float t2 = p[1].x; + float s2 = p[1].y; + float t3 = p[2].x; + float s3 = p[2].y; + float geometricArea = length(cross(q[1] - q[0], q[2] - q[0])) / 2; + float parametricArea = ((s2 - s1) * (t3 - t1) - (s3 - s1) * (t2 - t1)) / 2; + if (isZero(parametricArea)) { + m_zeroAreaTriangleCount++; + return; + } + Vector3 Ss = (q[0] * (t2 - t3) + q[1] * (t3 - t1) + q[2] * (t1 - t2)) / (2 * parametricArea); + Vector3 St = (q[0] * (s3 - s2) + q[1] * (s1 - s3) + q[2] * (s2 - s1)) / (2 * parametricArea); + float a = dot(Ss, Ss); // E + float b = dot(Ss, St); // F + float c = dot(St, St); // G + // Compute eigen-values of the first fundamental form: + float sigma1 = sqrtf(0.5f * std::max(0.0f, a + c - sqrtf(square(a - c) + 4 * square(b)))); // gamma uppercase, min eigenvalue. + float sigma2 = sqrtf(0.5f * std::max(0.0f, a + c + sqrtf(square(a - c) + 4 * square(b)))); // gamma lowercase, max eigenvalue. + xaAssert(sigma2 >= sigma1); + // isometric: sigma1 = sigma2 = 1 + // conformal: sigma1 / sigma2 = 1 + // authalic: sigma1 * sigma2 = 1 + float rmsStretch = sqrtf((a + c) * 0.5f); + float rmsStretch2 = sqrtf((square(sigma1) + square(sigma2)) * 0.5f); + xaDebugAssert(equal(rmsStretch, rmsStretch2, 0.01f)); +#ifdef NDEBUG + rmsStretch2 = 0; // silence unused parameter warning +#endif + if (parametricArea < 0.0f) { + // Count flipped triangles. + m_flippedTriangleCount++; + parametricArea = fabsf(parametricArea); + } + m_stretchMetric += square(rmsStretch) * geometricArea; + m_maxStretchMetric = std::max(m_maxStretchMetric, sigma2); + if (!isZero(sigma1, 0.000001f)) { + // sigma1 is zero when geometricArea is zero. + m_conformalMetric += (sigma2 / sigma1) * geometricArea; + } + m_authalicMetric += (sigma1 * sigma2) * geometricArea; + // Accumulate total areas. + m_geometricArea += geometricArea; + m_parametricArea += parametricArea; + //triangleConformalEnergy(q, p); + } + + uint32_t m_totalTriangleCount; + uint32_t m_flippedTriangleCount; + uint32_t m_zeroAreaTriangleCount; + float m_parametricArea; + float m_geometricArea; + float m_stretchMetric; + float m_maxStretchMetric; + float m_conformalMetric; + float m_authalicMetric; +}; + +// Set of charts corresponding to a single mesh. +class MeshCharts { +public: + MeshCharts(const halfedge::Mesh *mesh) : + m_mesh(mesh) {} + + ~MeshCharts() { + for (size_t i = 0; i < m_chartArray.size(); i++) + delete m_chartArray[i]; + } + + uint32_t chartCount() const { + return m_chartArray.size(); + } + uint32_t vertexCount() const { + return m_totalVertexCount; + } + + const Chart *chartAt(uint32_t i) const { + return m_chartArray[i]; + } + Chart *chartAt(uint32_t i) { + return m_chartArray[i]; + } + + // Extract the charts of the input mesh. + void extractCharts() { + const uint32_t faceCount = m_mesh->faceCount(); + int first = 0; + std::vector<uint32_t> queue; + queue.reserve(faceCount); + BitArray bitFlags(faceCount); + bitFlags.clearAll(); + for (uint32_t f = 0; f < faceCount; f++) { + if (bitFlags.bitAt(f) == false) { + // Start new patch. Reset queue. + first = 0; + queue.clear(); + queue.push_back(f); + bitFlags.setBitAt(f); + while (first != (int)queue.size()) { + const halfedge::Face *face = m_mesh->faceAt(queue[first]); + // Visit face neighbors of queue[first] + for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const halfedge::Edge *edge = it.current(); + xaDebugAssert(edge->pair != NULL); + if (!edge->isBoundary() && /*!edge->isSeam()*/ + //!(edge->from()->tex() != edge->pair()->to()->tex() || edge->to()->tex() != edge->pair()->from()->tex())) + !(edge->from() != edge->pair->to() || edge->to() != edge->pair->from())) { // Preserve existing seams (not just texture seams). + const halfedge::Face *neighborFace = edge->pair->face; + xaDebugAssert(neighborFace != NULL); + if (bitFlags.bitAt(neighborFace->id) == false) { + queue.push_back(neighborFace->id); + bitFlags.setBitAt(neighborFace->id); + } + } + } + first++; + } + Chart *chart = new Chart(); + chart->build(m_mesh, queue); + m_chartArray.push_back(chart); + } + } + } + + /* + Compute charts using a simple segmentation algorithm. + + LSCM: + - identify sharp features using local dihedral angles. + - identify seed faces farthest from sharp features. + - grow charts from these seeds. + + MCGIM: + - phase 1: chart growth + - grow all charts simultaneously using dijkstra search on the dual graph of the mesh. + - graph edges are weighted based on planarity metric. + - metric uses distance to global chart normal. + - terminate when all faces have been assigned. + - phase 2: seed computation: + - place new seed of the chart at the most interior face. + - most interior is evaluated using distance metric only. + + - method repeates the two phases, until the location of the seeds does not change. + - cycles are detected by recording all the previous seeds and chartification terminates. + + D-Charts: + + - Uniaxial conic metric: + - N_c = axis of the generalized cone that best fits the chart. (cone can a be cylinder or a plane). + - omega_c = angle between the face normals and the axis. + - Fitting error between chart C and tringle t: F(c,t) = (N_c*n_t - cos(omega_c))^2 + + - Compactness metrics: + - Roundness: + - C(c,t) = pi * D(S_c,t)^2 / A_c + - S_c = chart seed. + - D(S_c,t) = length of the shortest path inside the chart betwen S_c and t. + - A_c = chart area. + - Straightness: + - P(c,t) = l_out(c,t) / l_in(c,t) + - l_out(c,t) = lenght of the edges not shared between C and t. + - l_in(c,t) = lenght of the edges shared between C and t. + + - Combined metric: + - Cost(c,t) = F(c,t)^alpha + C(c,t)^beta + P(c,t)^gamma + - alpha = 1, beta = 0.7, gamma = 0.5 + + Our basic approach: + - Just one iteration of k-means? + - Avoid dijkstra by greedily growing charts until a threshold is met. Increase threshold and repeat until no faces left. + - If distortion metric is too high, split chart, add two seeds. + - If chart size is low, try removing chart. + + Postprocess: + - If topology is not disk: + - Fill holes, if new faces fit proxy. + - Find best cut, otherwise. + - After parameterization: + - If boundary self-intersects: + - cut chart along the closest two diametral boundary vertices, repeat parametrization. + - what if the overlap is on an appendix? How do we find that out and cut appropiately? + - emphasize roundness metrics to prevent those cases. + - If interior self-overlaps: preserve boundary parameterization and use mean-value map. + */ + void computeCharts(const CharterOptions &options, const std::vector<uint32_t> &unchartedMaterialArray) { + Chart *vertexMap = NULL; + if (unchartedMaterialArray.size() != 0) { + vertexMap = new Chart(); + vertexMap->buildVertexMap(m_mesh, unchartedMaterialArray); + if (vertexMap->faceCount() == 0) { + delete vertexMap; + vertexMap = NULL; + } + } + AtlasBuilder builder(m_mesh); + if (vertexMap != NULL) { + // Mark faces that do not need to be charted. + builder.markUnchartedFaces(vertexMap->faceArray()); + m_chartArray.push_back(vertexMap); + } + if (builder.facesLeft != 0) { + // Tweak these values: + const float maxThreshold = 2; + const uint32_t growFaceCount = 32; + const uint32_t maxIterations = 4; + builder.options = options; + //builder.options.proxyFitMetricWeight *= 0.75; // relax proxy fit weight during initial seed placement. + //builder.options.roundnessMetricWeight = 0; + //builder.options.straightnessMetricWeight = 0; + // This seems a reasonable estimate. + uint32_t maxSeedCount = std::max(6U, builder.facesLeft); + // Create initial charts greedely. + xaPrint("### Placing seeds\n"); + builder.placeSeeds(maxThreshold, maxSeedCount); + xaPrint("### Placed %d seeds (max = %d)\n", builder.chartCount(), maxSeedCount); + builder.updateProxies(); + builder.mergeCharts(); +#if 1 + xaPrint("### Relocating seeds\n"); + builder.relocateSeeds(); + xaPrint("### Reset charts\n"); + builder.resetCharts(); + if (vertexMap != NULL) { + builder.markUnchartedFaces(vertexMap->faceArray()); + } + builder.options = options; + xaPrint("### Growing charts\n"); + // Restart process growing charts in parallel. + uint32_t iteration = 0; + while (true) { + if (!builder.growCharts(maxThreshold, growFaceCount)) { + xaPrint("### Can't grow anymore\n"); + // If charts cannot grow more: fill holes, merge charts, relocate seeds and start new iteration. + xaPrint("### Filling holes\n"); + builder.fillHoles(maxThreshold); + xaPrint("### Using %d charts now\n", builder.chartCount()); + builder.updateProxies(); + xaPrint("### Merging charts\n"); + builder.mergeCharts(); + xaPrint("### Using %d charts now\n", builder.chartCount()); + xaPrint("### Reseeding\n"); + if (!builder.relocateSeeds()) { + xaPrint("### Cannot relocate seeds anymore\n"); + // Done! + break; + } + if (iteration == maxIterations) { + xaPrint("### Reached iteration limit\n"); + break; + } + iteration++; + xaPrint("### Reset charts\n"); + builder.resetCharts(); + if (vertexMap != NULL) { + builder.markUnchartedFaces(vertexMap->faceArray()); + } + xaPrint("### Growing charts\n"); + } + }; +#endif + // Make sure no holes are left! + xaDebugAssert(builder.facesLeft == 0); + const uint32_t chartCount = builder.chartArray.size(); + for (uint32_t i = 0; i < chartCount; i++) { + Chart *chart = new Chart(); + m_chartArray.push_back(chart); + chart->build(m_mesh, builder.chartFaces(i)); + } + } + const uint32_t chartCount = m_chartArray.size(); + // Build face indices. + m_faceChart.resize(m_mesh->faceCount()); + m_faceIndex.resize(m_mesh->faceCount()); + for (uint32_t i = 0; i < chartCount; i++) { + const Chart *chart = m_chartArray[i]; + const uint32_t faceCount = chart->faceCount(); + for (uint32_t f = 0; f < faceCount; f++) { + uint32_t idx = chart->faceAt(f); + m_faceChart[idx] = i; + m_faceIndex[idx] = f; + } + } + // Build an exclusive prefix sum of the chart vertex counts. + m_chartVertexCountPrefixSum.resize(chartCount); + if (chartCount > 0) { + m_chartVertexCountPrefixSum[0] = 0; + for (uint32_t i = 1; i < chartCount; i++) { + const Chart *chart = m_chartArray[i - 1]; + m_chartVertexCountPrefixSum[i] = m_chartVertexCountPrefixSum[i - 1] + chart->vertexCount(); + } + m_totalVertexCount = m_chartVertexCountPrefixSum[chartCount - 1] + m_chartArray[chartCount - 1]->vertexCount(); + } else { + m_totalVertexCount = 0; + } + } + + void parameterizeCharts() { + ParameterizationQuality globalParameterizationQuality; + // Parameterize the charts. + uint32_t diskCount = 0; + const uint32_t chartCount = m_chartArray.size(); + for (uint32_t i = 0; i < chartCount; i++) { + Chart *chart = m_chartArray[i]; + + bool isValid = false; + + if (chart->isVertexMapped()) { + continue; + } + + if (chart->isDisk()) { + diskCount++; + ParameterizationQuality chartParameterizationQuality; + if (chart->faceCount() == 1) { + computeSingleFaceMap(chart->unifiedMesh()); + chartParameterizationQuality = ParameterizationQuality(chart->unifiedMesh()); + } else { + computeOrthogonalProjectionMap(chart->unifiedMesh()); + ParameterizationQuality orthogonalQuality(chart->unifiedMesh()); + computeLeastSquaresConformalMap(chart->unifiedMesh()); + ParameterizationQuality lscmQuality(chart->unifiedMesh()); + chartParameterizationQuality = lscmQuality; + } + isValid = chartParameterizationQuality.isValid(); + if (!isValid) { + xaPrint("*** Invalid parameterization.\n"); + } + // @@ Check that parameterization quality is above a certain threshold. + // @@ Detect boundary self-intersections. + globalParameterizationQuality += chartParameterizationQuality; + } + + // Transfer parameterization from unified mesh to chart mesh. + chart->transferParameterization(); + } + xaPrint(" Parameterized %d/%d charts.\n", diskCount, chartCount); + xaPrint(" RMS stretch metric: %f\n", globalParameterizationQuality.rmsStretchMetric()); + xaPrint(" MAX stretch metric: %f\n", globalParameterizationQuality.maxStretchMetric()); + xaPrint(" RMS conformal metric: %f\n", globalParameterizationQuality.rmsConformalMetric()); + xaPrint(" RMS authalic metric: %f\n", globalParameterizationQuality.maxAuthalicMetric()); + } + + uint32_t faceChartAt(uint32_t i) const { + return m_faceChart[i]; + } + uint32_t faceIndexWithinChartAt(uint32_t i) const { + return m_faceIndex[i]; + } + + uint32_t vertexCountBeforeChartAt(uint32_t i) const { + return m_chartVertexCountPrefixSum[i]; + } + +private: + const halfedge::Mesh *m_mesh; + + std::vector<Chart *> m_chartArray; + + std::vector<uint32_t> m_chartVertexCountPrefixSum; + uint32_t m_totalVertexCount; + + std::vector<uint32_t> m_faceChart; // the chart of every face of the input mesh. + std::vector<uint32_t> m_faceIndex; // the index within the chart for every face of the input mesh. +}; + +/// An atlas is a set of charts. +class Atlas { +public: + ~Atlas() { + for (size_t i = 0; i < m_meshChartsArray.size(); i++) + delete m_meshChartsArray[i]; + } + + uint32_t meshCount() const { + return m_meshChartsArray.size(); + } + + const MeshCharts *meshAt(uint32_t i) const { + return m_meshChartsArray[i]; + } + + MeshCharts *meshAt(uint32_t i) { + return m_meshChartsArray[i]; + } + + uint32_t chartCount() const { + uint32_t count = 0; + for (uint32_t c = 0; c < m_meshChartsArray.size(); c++) { + count += m_meshChartsArray[c]->chartCount(); + } + return count; + } + + const Chart *chartAt(uint32_t i) const { + for (uint32_t c = 0; c < m_meshChartsArray.size(); c++) { + uint32_t count = m_meshChartsArray[c]->chartCount(); + if (i < count) { + return m_meshChartsArray[c]->chartAt(i); + } + i -= count; + } + return NULL; + } + + Chart *chartAt(uint32_t i) { + for (uint32_t c = 0; c < m_meshChartsArray.size(); c++) { + uint32_t count = m_meshChartsArray[c]->chartCount(); + if (i < count) { + return m_meshChartsArray[c]->chartAt(i); + } + i -= count; + } + return NULL; + } + + // Add mesh charts and takes ownership. + // Extract the charts and add to this atlas. + void addMeshCharts(MeshCharts *meshCharts) { + m_meshChartsArray.push_back(meshCharts); + } + + void extractCharts(const halfedge::Mesh *mesh) { + MeshCharts *meshCharts = new MeshCharts(mesh); + meshCharts->extractCharts(); + addMeshCharts(meshCharts); + } + + void computeCharts(const halfedge::Mesh *mesh, const CharterOptions &options, const std::vector<uint32_t> &unchartedMaterialArray) { + MeshCharts *meshCharts = new MeshCharts(mesh); + meshCharts->computeCharts(options, unchartedMaterialArray); + addMeshCharts(meshCharts); + } + + void parameterizeCharts() { + for (uint32_t i = 0; i < m_meshChartsArray.size(); i++) { + m_meshChartsArray[i]->parameterizeCharts(); + } + } + +private: + std::vector<MeshCharts *> m_meshChartsArray; +}; + +struct AtlasPacker { + AtlasPacker(Atlas *atlas) : + m_atlas(atlas), + m_width(0), + m_height(0) { + // Save the original uvs. + m_originalChartUvs.resize(m_atlas->chartCount()); + for (uint32_t i = 0; i < m_atlas->chartCount(); i++) { + const halfedge::Mesh *mesh = atlas->chartAt(i)->chartMesh(); + m_originalChartUvs[i].resize(mesh->vertexCount()); + for (uint32_t j = 0; j < mesh->vertexCount(); j++) + m_originalChartUvs[i][j] = mesh->vertexAt(j)->tex; + } + } + + uint32_t getWidth() const { return m_width; } + uint32_t getHeight() const { return m_height; } + + // Pack charts in the smallest possible rectangle. + void packCharts(const PackerOptions &options) { + const uint32_t chartCount = m_atlas->chartCount(); + if (chartCount == 0) return; + float texelsPerUnit = 1; + if (options.method == PackMethod::TexelArea) + texelsPerUnit = options.texelArea; + for (int iteration = 0;; iteration++) { + m_rand = MTRand(); + std::vector<float> chartOrderArray(chartCount); + std::vector<Vector2> chartExtents(chartCount); + float meshArea = 0; + for (uint32_t c = 0; c < chartCount; c++) { + Chart *chart = m_atlas->chartAt(c); + if (!chart->isVertexMapped() && !chart->isDisk()) { + chartOrderArray[c] = 0; + // Skip non-disks. + continue; + } + Vector2 extents(0.0f); + if (chart->isVertexMapped()) { + // Arrange vertices in a rectangle. + extents.x = float(chart->vertexMapWidth); + extents.y = float(chart->vertexMapHeight); + } else { + // Compute surface area to sort charts. + float chartArea = chart->computeSurfaceArea(); + meshArea += chartArea; + //chartOrderArray[c] = chartArea; + // Compute chart scale + float parametricArea = fabsf(chart->computeParametricArea()); // @@ There doesn't seem to be anything preventing parametric area to be negative. + if (parametricArea < NV_EPSILON) { + // When the parametric area is too small we use a rough approximation to prevent divisions by very small numbers. + Vector2 bounds = chart->computeParametricBounds(); + parametricArea = bounds.x * bounds.y; + } + float scale = (chartArea / parametricArea) * texelsPerUnit; + if (parametricArea == 0) { // < NV_EPSILON) + scale = 0; + } + xaAssert(std::isfinite(scale)); + // Compute bounding box of chart. + Vector2 majorAxis, minorAxis, origin, end; + computeBoundingBox(chart, &majorAxis, &minorAxis, &origin, &end); + xaAssert(isFinite(majorAxis) && isFinite(minorAxis) && isFinite(origin)); + // Sort charts by perimeter. @@ This is sometimes producing somewhat unexpected results. Is this right? + //chartOrderArray[c] = ((end.x - origin.x) + (end.y - origin.y)) * scale; + // Translate, rotate and scale vertices. Compute extents. + halfedge::Mesh *mesh = chart->chartMesh(); + const uint32_t vertexCount = mesh->vertexCount(); + for (uint32_t i = 0; i < vertexCount; i++) { + halfedge::Vertex *vertex = mesh->vertexAt(i); + //Vector2 t = vertex->tex - origin; + Vector2 tmp; + tmp.x = dot(vertex->tex, majorAxis); + tmp.y = dot(vertex->tex, minorAxis); + tmp -= origin; + tmp *= scale; + if (tmp.x < 0 || tmp.y < 0) { + xaPrint("tmp: %f %f\n", tmp.x, tmp.y); + xaPrint("scale: %f\n", scale); + xaPrint("origin: %f %f\n", origin.x, origin.y); + xaPrint("majorAxis: %f %f\n", majorAxis.x, majorAxis.y); + xaPrint("minorAxis: %f %f\n", minorAxis.x, minorAxis.y); + xaDebugAssert(false); + } + //xaAssert(tmp.x >= 0 && tmp.y >= 0); + vertex->tex = tmp; + xaAssert(std::isfinite(vertex->tex.x) && std::isfinite(vertex->tex.y)); + extents = max(extents, tmp); + } + xaDebugAssert(extents.x >= 0 && extents.y >= 0); + // Limit chart size. + if (extents.x > 1024 || extents.y > 1024) { + float limit = std::max(extents.x, extents.y); + scale = 1024 / (limit + 1); + for (uint32_t i = 0; i < vertexCount; i++) { + halfedge::Vertex *vertex = mesh->vertexAt(i); + vertex->tex *= scale; + } + extents *= scale; + xaDebugAssert(extents.x <= 1024 && extents.y <= 1024); + } + // Scale the charts to use the entire texel area available. So, if the width is 0.1 we could scale it to 1 without increasing the lightmap usage and making a better + // use of it. In many cases this also improves the look of the seams, since vertices on the chart boundaries have more chances of being aligned with the texel centers. + float scale_x = 1.0f; + float scale_y = 1.0f; + float divide_x = 1.0f; + float divide_y = 1.0f; + if (extents.x > 0) { + int cw = ftoi_ceil(extents.x); + if (options.blockAlign && chart->blockAligned) { + // Align all chart extents to 4x4 blocks, but taking padding into account. + if (options.conservative) { + cw = align(cw + 2, 4) - 2; + } else { + cw = align(cw + 1, 4) - 1; + } + } + scale_x = (float(cw) - NV_EPSILON); + divide_x = extents.x; + extents.x = float(cw); + } + if (extents.y > 0) { + int ch = ftoi_ceil(extents.y); + if (options.blockAlign && chart->blockAligned) { + // Align all chart extents to 4x4 blocks, but taking padding into account. + if (options.conservative) { + ch = align(ch + 2, 4) - 2; + } else { + ch = align(ch + 1, 4) - 1; + } + } + scale_y = (float(ch) - NV_EPSILON); + divide_y = extents.y; + extents.y = float(ch); + } + for (uint32_t v = 0; v < vertexCount; v++) { + halfedge::Vertex *vertex = mesh->vertexAt(v); + vertex->tex.x /= divide_x; + vertex->tex.y /= divide_y; + vertex->tex.x *= scale_x; + vertex->tex.y *= scale_y; + xaAssert(std::isfinite(vertex->tex.x) && std::isfinite(vertex->tex.y)); + } + } + chartExtents[c] = extents; + // Sort charts by perimeter. + chartOrderArray[c] = extents.x + extents.y; + } + // @@ We can try to improve compression of small charts by sorting them by proximity like we do with vertex samples. + // @@ How to do that? One idea: compute chart centroid, insert into grid, compute morton index of the cell, sort based on morton index. + // @@ We would sort by morton index, first, then quantize the chart sizes, so that all small charts have the same size, and sort by size preserving the morton order. + //xaPrint("Sorting charts.\n"); + // Sort charts by area. + m_radix = RadixSort(); + m_radix.sort(chartOrderArray); + const uint32_t *ranks = m_radix.ranks(); + // First iteration - guess texelsPerUnit. + if (options.method != PackMethod::TexelArea && iteration == 0) { + // Estimate size of the map based on the mesh surface area and given texel scale. + const float texelCount = std::max(1.0f, meshArea * square(texelsPerUnit) / 0.75f); // Assume 75% utilization. + texelsPerUnit = sqrt((options.resolution * options.resolution) / texelCount); + resetUvs(); + continue; + } + // Init bit map. + m_bitmap.clearAll(); + m_bitmap.resize(options.resolution, options.resolution, false); + int w = 0; + int h = 0; + // Add sorted charts to bitmap. + for (uint32_t i = 0; i < chartCount; i++) { + uint32_t c = ranks[chartCount - i - 1]; // largest chart first + Chart *chart = m_atlas->chartAt(c); + if (!chart->isVertexMapped() && !chart->isDisk()) continue; + //float scale_x = 1; + //float scale_y = 1; + BitMap chart_bitmap; + if (chart->isVertexMapped()) { + chart->blockAligned = false; + // Init all bits to 1. + chart_bitmap.resize(ftoi_ceil(chartExtents[c].x), ftoi_ceil(chartExtents[c].y), /*initValue=*/true); + // @@ Another alternative would be to try to map each vertex to a different texel trying to fill all the available unused texels. + } else { + // @@ Add special cases for dot and line charts. @@ Lightmap rasterizer also needs to handle these special cases. + // @@ We could also have a special case for chart quads. If the quad surface <= 4 texels, align vertices with texel centers and do not add padding. May be very useful for foliage. + // @@ In general we could reduce the padding of all charts by one texel by using a rasterizer that takes into account the 2-texel footprint of the tent bilinear filter. For example, + // if we have a chart that is less than 1 texel wide currently we add one texel to the left and one texel to the right creating a 3-texel-wide bitmap. However, if we know that the + // chart is only 1 texel wide we could align it so that it only touches the footprint of two texels: + // | | <- Touches texels 0, 1 and 2. + // | | <- Only touches texels 0 and 1. + // \ \ / \ / / + // \ X X / + // \ / \ / \ / + // V V V + // 0 1 2 + if (options.conservative) { + // Init all bits to 0. + chart_bitmap.resize(ftoi_ceil(chartExtents[c].x) + 1 + options.padding, ftoi_ceil(chartExtents[c].y) + 1 + options.padding, /*initValue=*/false); // + 2 to add padding on both sides. + // Rasterize chart and dilate. + drawChartBitmapDilate(chart, &chart_bitmap, options.padding); + } else { + // Init all bits to 0. + chart_bitmap.resize(ftoi_ceil(chartExtents[c].x) + 1, ftoi_ceil(chartExtents[c].y) + 1, /*initValue=*/false); // Add half a texels on each side. + // Rasterize chart and dilate. + drawChartBitmap(chart, &chart_bitmap, Vector2(1), Vector2(0.5)); + } + } + int best_x, best_y; + int best_cw, best_ch; // Includes padding now. + int best_r; + findChartLocation(options.quality, &chart_bitmap, chartExtents[c], w, h, &best_x, &best_y, &best_cw, &best_ch, &best_r, chart->blockAligned); + /*if (w < best_x + best_cw || h < best_y + best_ch) + { + xaPrint("Resize extents to (%d, %d).\n", best_x + best_cw, best_y + best_ch); + }*/ + // Update parametric extents. + w = std::max(w, best_x + best_cw); + h = std::max(h, best_y + best_ch); + w = align(w, 4); + h = align(h, 4); + // Resize bitmap if necessary. + if (uint32_t(w) > m_bitmap.width() || uint32_t(h) > m_bitmap.height()) { + //xaPrint("Resize bitmap (%d, %d).\n", nextPowerOfTwo(w), nextPowerOfTwo(h)); + m_bitmap.resize(nextPowerOfTwo(uint32_t(w)), nextPowerOfTwo(uint32_t(h)), false); + } + //xaPrint("Add chart at (%d, %d).\n", best_x, best_y); + addChart(&chart_bitmap, w, h, best_x, best_y, best_r); + //float best_angle = 2 * PI * best_r; + // Translate and rotate chart texture coordinates. + halfedge::Mesh *mesh = chart->chartMesh(); + const uint32_t vertexCount = mesh->vertexCount(); + for (uint32_t v = 0; v < vertexCount; v++) { + halfedge::Vertex *vertex = mesh->vertexAt(v); + Vector2 t = vertex->tex; + if (best_r) std::swap(t.x, t.y); + //vertex->tex.x = best_x + t.x * cosf(best_angle) - t.y * sinf(best_angle); + //vertex->tex.y = best_y + t.x * sinf(best_angle) + t.y * cosf(best_angle); + vertex->tex.x = best_x + t.x + 0.5f; + vertex->tex.y = best_y + t.y + 0.5f; + xaAssert(vertex->tex.x >= 0 && vertex->tex.y >= 0); + xaAssert(std::isfinite(vertex->tex.x) && std::isfinite(vertex->tex.y)); + } + } + //w -= padding - 1; // Leave one pixel border! + //h -= padding - 1; + m_width = std::max(0, w); + m_height = std::max(0, h); + xaAssert(isAligned(m_width, 4)); + xaAssert(isAligned(m_height, 4)); + if (options.method == PackMethod::ExactResolution) { + texelsPerUnit *= sqrt((options.resolution * options.resolution) / (float)(m_width * m_height)); + if (iteration > 1 && m_width <= options.resolution && m_height <= options.resolution) { + m_width = m_height = options.resolution; + return; + } + resetUvs(); + } else { + return; + } + } + } + + float computeAtlasUtilization() const { + const uint32_t w = m_width; + const uint32_t h = m_height; + xaDebugAssert(w <= m_bitmap.width()); + xaDebugAssert(h <= m_bitmap.height()); + uint32_t count = 0; + for (uint32_t y = 0; y < h; y++) { + for (uint32_t x = 0; x < w; x++) { + count += m_bitmap.bitAt(x, y); + } + } + return float(count) / (w * h); + } + +private: + void resetUvs() { + for (uint32_t i = 0; i < m_atlas->chartCount(); i++) { + halfedge::Mesh *mesh = m_atlas->chartAt(i)->chartMesh(); + for (uint32_t j = 0; j < mesh->vertexCount(); j++) + mesh->vertexAt(j)->tex = m_originalChartUvs[i][j]; + } + } + + // IC: Brute force is slow, and random may take too much time to converge. We start inserting large charts in a small atlas. Using brute force is lame, because most of the space + // is occupied at this point. At the end we have many small charts and a large atlas with sparse holes. Finding those holes randomly is slow. A better approach would be to + // start stacking large charts as if they were tetris pieces. Once charts get small try to place them randomly. It may be interesting to try a intermediate strategy, first try + // along one axis and then try exhaustively along that axis. + void findChartLocation(int quality, const BitMap *bitmap, Vector2::Arg extents, int w, int h, int *best_x, int *best_y, int *best_w, int *best_h, int *best_r, bool blockAligned) { + int attempts = 256; + if (quality == 1) attempts = 4096; + if (quality == 2) attempts = 2048; + if (quality == 3) attempts = 1024; + if (quality == 4) attempts = 512; + if (quality == 0 || w * h < attempts) { + findChartLocation_bruteForce(bitmap, extents, w, h, best_x, best_y, best_w, best_h, best_r, blockAligned); + } else { + findChartLocation_random(bitmap, extents, w, h, best_x, best_y, best_w, best_h, best_r, attempts, blockAligned); + } + } + + void findChartLocation_bruteForce(const BitMap *bitmap, Vector2::Arg /*extents*/, int w, int h, int *best_x, int *best_y, int *best_w, int *best_h, int *best_r, bool blockAligned) { + const int BLOCK_SIZE = 4; + int best_metric = INT_MAX; + int step_size = blockAligned ? BLOCK_SIZE : 1; + // Try two different orientations. + for (int r = 0; r < 2; r++) { + int cw = bitmap->width(); + int ch = bitmap->height(); + if (r & 1) std::swap(cw, ch); + for (int y = 0; y <= h + 1; y += step_size) { // + 1 to extend atlas in case atlas full. + for (int x = 0; x <= w + 1; x += step_size) { // + 1 not really necessary here. + // Early out. + int area = std::max(w, x + cw) * std::max(h, y + ch); + //int perimeter = max(w, x+cw) + max(h, y+ch); + int extents = std::max(std::max(w, x + cw), std::max(h, y + ch)); + int metric = extents * extents + area; + if (metric > best_metric) { + continue; + } + if (metric == best_metric && std::max(x, y) >= std::max(*best_x, *best_y)) { + // If metric is the same, pick the one closest to the origin. + continue; + } + if (canAddChart(bitmap, w, h, x, y, r)) { + best_metric = metric; + *best_x = x; + *best_y = y; + *best_w = cw; + *best_h = ch; + *best_r = r; + if (area == w * h) { + // Chart is completely inside, do not look at any other location. + goto done; + } + } + } + } + } + done: + xaDebugAssert(best_metric != INT_MAX); + } + + void findChartLocation_random(const BitMap *bitmap, Vector2::Arg /*extents*/, int w, int h, int *best_x, int *best_y, int *best_w, int *best_h, int *best_r, int minTrialCount, bool blockAligned) { + const int BLOCK_SIZE = 4; + int best_metric = INT_MAX; + for (int i = 0; i < minTrialCount || best_metric == INT_MAX; i++) { + int r = m_rand.getRange(1); + int x = m_rand.getRange(w + 1); // + 1 to extend atlas in case atlas full. We may want to use a higher number to increase probability of extending atlas. + int y = m_rand.getRange(h + 1); // + 1 to extend atlas in case atlas full. + if (blockAligned) { + x = align(x, BLOCK_SIZE); + y = align(y, BLOCK_SIZE); + } + int cw = bitmap->width(); + int ch = bitmap->height(); + if (r & 1) std::swap(cw, ch); + // Early out. + int area = std::max(w, x + cw) * std::max(h, y + ch); + //int perimeter = max(w, x+cw) + max(h, y+ch); + int extents = std::max(std::max(w, x + cw), std::max(h, y + ch)); + int metric = extents * extents + area; + if (metric > best_metric) { + continue; + } + if (metric == best_metric && std::min(x, y) > std::min(*best_x, *best_y)) { + // If metric is the same, pick the one closest to the origin. + continue; + } + if (canAddChart(bitmap, w, h, x, y, r)) { + best_metric = metric; + *best_x = x; + *best_y = y; + *best_w = cw; + *best_h = ch; + *best_r = r; + if (area == w * h) { + // Chart is completely inside, do not look at any other location. + break; + } + } + } + } + + void drawChartBitmapDilate(const Chart *chart, BitMap *bitmap, int padding) { + const int w = bitmap->width(); + const int h = bitmap->height(); + const Vector2 extents = Vector2(float(w), float(h)); + // Rasterize chart faces, check that all bits are not set. + const uint32_t faceCount = chart->faceCount(); + for (uint32_t f = 0; f < faceCount; f++) { + const halfedge::Face *face = chart->chartMesh()->faceAt(f); + Vector2 vertices[4]; + uint32_t edgeCount = 0; + for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + if (edgeCount < 4) { + vertices[edgeCount] = it.vertex()->tex + Vector2(0.5) + Vector2(float(padding), float(padding)); + } + edgeCount++; + } + if (edgeCount == 3) { + raster::drawTriangle(raster::Mode_Antialiased, extents, true, vertices, AtlasPacker::setBitsCallback, bitmap); + } else { + raster::drawQuad(raster::Mode_Antialiased, extents, true, vertices, AtlasPacker::setBitsCallback, bitmap); + } + } + // Expand chart by padding pixels. (dilation) + BitMap tmp(w, h); + for (int i = 0; i < padding; i++) { + tmp.clearAll(); + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + bool b = bitmap->bitAt(x, y); + if (!b) { + if (x > 0) { + b |= bitmap->bitAt(x - 1, y); + if (y > 0) b |= bitmap->bitAt(x - 1, y - 1); + if (y < h - 1) b |= bitmap->bitAt(x - 1, y + 1); + } + if (y > 0) b |= bitmap->bitAt(x, y - 1); + if (y < h - 1) b |= bitmap->bitAt(x, y + 1); + if (x < w - 1) { + b |= bitmap->bitAt(x + 1, y); + if (y > 0) b |= bitmap->bitAt(x + 1, y - 1); + if (y < h - 1) b |= bitmap->bitAt(x + 1, y + 1); + } + } + if (b) tmp.setBitAt(x, y); + } + } + std::swap(tmp, *bitmap); + } + } + + void drawChartBitmap(const Chart *chart, BitMap *bitmap, const Vector2 &scale, const Vector2 &offset) { + const int w = bitmap->width(); + const int h = bitmap->height(); + const Vector2 extents = Vector2(float(w), float(h)); + static const Vector2 pad[4] = { + Vector2(-0.5, -0.5), + Vector2(0.5, -0.5), + Vector2(-0.5, 0.5), + Vector2(0.5, 0.5) + }; + // Rasterize 4 times to add proper padding. + for (int i = 0; i < 4; i++) { + // Rasterize chart faces, check that all bits are not set. + const uint32_t faceCount = chart->chartMesh()->faceCount(); + for (uint32_t f = 0; f < faceCount; f++) { + const halfedge::Face *face = chart->chartMesh()->faceAt(f); + Vector2 vertices[4]; + uint32_t edgeCount = 0; + for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + if (edgeCount < 4) { + vertices[edgeCount] = it.vertex()->tex * scale + offset + pad[i]; + xaAssert(ftoi_ceil(vertices[edgeCount].x) >= 0); + xaAssert(ftoi_ceil(vertices[edgeCount].y) >= 0); + xaAssert(ftoi_ceil(vertices[edgeCount].x) <= w); + xaAssert(ftoi_ceil(vertices[edgeCount].y) <= h); + } + edgeCount++; + } + if (edgeCount == 3) { + raster::drawTriangle(raster::Mode_Antialiased, extents, /*enableScissors=*/true, vertices, AtlasPacker::setBitsCallback, bitmap); + } else { + raster::drawQuad(raster::Mode_Antialiased, extents, /*enableScissors=*/true, vertices, AtlasPacker::setBitsCallback, bitmap); + } + } + } + // Expand chart by padding pixels. (dilation) + BitMap tmp(w, h); + tmp.clearAll(); + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + bool b = bitmap->bitAt(x, y); + if (!b) { + if (x > 0) { + b |= bitmap->bitAt(x - 1, y); + if (y > 0) b |= bitmap->bitAt(x - 1, y - 1); + if (y < h - 1) b |= bitmap->bitAt(x - 1, y + 1); + } + if (y > 0) b |= bitmap->bitAt(x, y - 1); + if (y < h - 1) b |= bitmap->bitAt(x, y + 1); + if (x < w - 1) { + b |= bitmap->bitAt(x + 1, y); + if (y > 0) b |= bitmap->bitAt(x + 1, y - 1); + if (y < h - 1) b |= bitmap->bitAt(x + 1, y + 1); + } + } + if (b) tmp.setBitAt(x, y); + } + } + std::swap(tmp, *bitmap); + } + + bool canAddChart(const BitMap *bitmap, int atlas_w, int atlas_h, int offset_x, int offset_y, int r) { + xaDebugAssert(r == 0 || r == 1); + // Check whether the two bitmaps overlap. + const int w = bitmap->width(); + const int h = bitmap->height(); + if (r == 0) { + for (int y = 0; y < h; y++) { + int yy = y + offset_y; + if (yy >= 0) { + for (int x = 0; x < w; x++) { + int xx = x + offset_x; + if (xx >= 0) { + if (bitmap->bitAt(x, y)) { + if (xx < atlas_w && yy < atlas_h) { + if (m_bitmap.bitAt(xx, yy)) return false; + } + } + } + } + } + } + } else if (r == 1) { + for (int y = 0; y < h; y++) { + int xx = y + offset_x; + if (xx >= 0) { + for (int x = 0; x < w; x++) { + int yy = x + offset_y; + if (yy >= 0) { + if (bitmap->bitAt(x, y)) { + if (xx < atlas_w && yy < atlas_h) { + if (m_bitmap.bitAt(xx, yy)) return false; + } + } + } + } + } + } + } + return true; + } + + void addChart(const BitMap *bitmap, int atlas_w, int atlas_h, int offset_x, int offset_y, int r) { + xaDebugAssert(r == 0 || r == 1); + // Check whether the two bitmaps overlap. + const int w = bitmap->width(); + const int h = bitmap->height(); + if (r == 0) { + for (int y = 0; y < h; y++) { + int yy = y + offset_y; + if (yy >= 0) { + for (int x = 0; x < w; x++) { + int xx = x + offset_x; + if (xx >= 0) { + if (bitmap->bitAt(x, y)) { + if (xx < atlas_w && yy < atlas_h) { + xaDebugAssert(m_bitmap.bitAt(xx, yy) == false); + m_bitmap.setBitAt(xx, yy); + } + } + } + } + } + } + } else if (r == 1) { + for (int y = 0; y < h; y++) { + int xx = y + offset_x; + if (xx >= 0) { + for (int x = 0; x < w; x++) { + int yy = x + offset_y; + if (yy >= 0) { + if (bitmap->bitAt(x, y)) { + if (xx < atlas_w && yy < atlas_h) { + xaDebugAssert(m_bitmap.bitAt(xx, yy) == false); + m_bitmap.setBitAt(xx, yy); + } + } + } + } + } + } + } + } + + static bool setBitsCallback(void *param, int x, int y, Vector3::Arg, Vector3::Arg, Vector3::Arg, float area) { + BitMap *bitmap = (BitMap *)param; + if (area > 0.0) { + bitmap->setBitAt(x, y); + } + return true; + } + + // Compute the convex hull using Graham Scan. + static void convexHull(const std::vector<Vector2> &input, std::vector<Vector2> &output, float epsilon) { + const uint32_t inputCount = input.size(); + std::vector<float> coords(inputCount); + for (uint32_t i = 0; i < inputCount; i++) { + coords[i] = input[i].x; + } + RadixSort radix; + radix.sort(coords); + const uint32_t *ranks = radix.ranks(); + std::vector<Vector2> top; + top.reserve(inputCount); + std::vector<Vector2> bottom; + bottom.reserve(inputCount); + Vector2 P = input[ranks[0]]; + Vector2 Q = input[ranks[inputCount - 1]]; + float topy = std::max(P.y, Q.y); + float boty = std::min(P.y, Q.y); + for (uint32_t i = 0; i < inputCount; i++) { + Vector2 p = input[ranks[i]]; + if (p.y >= boty) top.push_back(p); + } + for (uint32_t i = 0; i < inputCount; i++) { + Vector2 p = input[ranks[inputCount - 1 - i]]; + if (p.y <= topy) bottom.push_back(p); + } + // Filter top list. + output.clear(); + output.push_back(top[0]); + output.push_back(top[1]); + for (uint32_t i = 2; i < top.size();) { + Vector2 a = output[output.size() - 2]; + Vector2 b = output[output.size() - 1]; + Vector2 c = top[i]; + float area = triangleArea(a, b, c); + if (area >= -epsilon) { + output.pop_back(); + } + if (area < -epsilon || output.size() == 1) { + output.push_back(c); + i++; + } + } + uint32_t top_count = output.size(); + output.push_back(bottom[1]); + // Filter bottom list. + for (uint32_t i = 2; i < bottom.size();) { + Vector2 a = output[output.size() - 2]; + Vector2 b = output[output.size() - 1]; + Vector2 c = bottom[i]; + float area = triangleArea(a, b, c); + if (area >= -epsilon) { + output.pop_back(); + } + if (area < -epsilon || output.size() == top_count) { + output.push_back(c); + i++; + } + } + // Remove duplicate element. + xaDebugAssert(output.front() == output.back()); + output.pop_back(); + } + + // This should compute convex hull and use rotating calipers to find the best box. Currently it uses a brute force method. + static void computeBoundingBox(Chart *chart, Vector2 *majorAxis, Vector2 *minorAxis, Vector2 *minCorner, Vector2 *maxCorner) { + // Compute list of boundary points. + std::vector<Vector2> points; + points.reserve(16); + halfedge::Mesh *mesh = chart->chartMesh(); + const uint32_t vertexCount = mesh->vertexCount(); + for (uint32_t i = 0; i < vertexCount; i++) { + halfedge::Vertex *vertex = mesh->vertexAt(i); + if (vertex->isBoundary()) { + points.push_back(vertex->tex); + } + } + xaDebugAssert(points.size() > 0); + std::vector<Vector2> hull; + convexHull(points, hull, 0.00001f); + // @@ Ideally I should use rotating calipers to find the best box. Using brute force for now. + float best_area = FLT_MAX; + Vector2 best_min; + Vector2 best_max; + Vector2 best_axis; + const uint32_t hullCount = hull.size(); + for (uint32_t i = 0, j = hullCount - 1; i < hullCount; j = i, i++) { + if (equal(hull[i], hull[j])) { + continue; + } + Vector2 axis = normalize(hull[i] - hull[j], 0.0f); + xaDebugAssert(isFinite(axis)); + // Compute bounding box. + Vector2 box_min(FLT_MAX, FLT_MAX); + Vector2 box_max(-FLT_MAX, -FLT_MAX); + for (uint32_t v = 0; v < hullCount; v++) { + Vector2 point = hull[v]; + float x = dot(axis, point); + if (x < box_min.x) box_min.x = x; + if (x > box_max.x) box_max.x = x; + float y = dot(Vector2(-axis.y, axis.x), point); + if (y < box_min.y) box_min.y = y; + if (y > box_max.y) box_max.y = y; + } + // Compute box area. + float area = (box_max.x - box_min.x) * (box_max.y - box_min.y); + if (area < best_area) { + best_area = area; + best_min = box_min; + best_max = box_max; + best_axis = axis; + } + } + // Consider all points, not only boundary points, in case the input chart is malformed. + for (uint32_t i = 0; i < vertexCount; i++) { + halfedge::Vertex *vertex = mesh->vertexAt(i); + Vector2 point = vertex->tex; + float x = dot(best_axis, point); + if (x < best_min.x) best_min.x = x; + if (x > best_max.x) best_max.x = x; + float y = dot(Vector2(-best_axis.y, best_axis.x), point); + if (y < best_min.y) best_min.y = y; + if (y > best_max.y) best_max.y = y; + } + *majorAxis = best_axis; + *minorAxis = Vector2(-best_axis.y, best_axis.x); + *minCorner = best_min; + *maxCorner = best_max; + } + + Atlas *m_atlas; + BitMap m_bitmap; + RadixSort m_radix; + uint32_t m_width; + uint32_t m_height; + MTRand m_rand; + std::vector<std::vector<Vector2> > m_originalChartUvs; +}; + +} // namespace param +} // namespace internal + +struct Atlas { + internal::param::Atlas atlas; + std::vector<internal::halfedge::Mesh *> heMeshes; + uint32_t width = 0; + uint32_t height = 0; + OutputMesh **outputMeshes = NULL; +}; + +void SetPrint(PrintFunc print) { + internal::s_print = print; +} + +Atlas *Create() { + Atlas *atlas = new Atlas(); + return atlas; +} + +void Destroy(Atlas *atlas) { + xaAssert(atlas); + for (int i = 0; i < (int)atlas->heMeshes.size(); i++) { + delete atlas->heMeshes[i]; + if (atlas->outputMeshes) { + OutputMesh *outputMesh = atlas->outputMeshes[i]; + for (uint32_t j = 0; j < outputMesh->chartCount; j++) + delete[] outputMesh->chartArray[j].indexArray; + delete[] outputMesh->chartArray; + delete[] outputMesh->vertexArray; + delete[] outputMesh->indexArray; + delete outputMesh; + } + } + delete[] atlas->outputMeshes; + delete atlas; +} + +static internal::Vector3 DecodePosition(const InputMesh &mesh, uint32_t index) { + xaAssert(mesh.vertexPositionData); + return *((const internal::Vector3 *)&((const uint8_t *)mesh.vertexPositionData)[mesh.vertexPositionStride * index]); +} + +static internal::Vector3 DecodeNormal(const InputMesh &mesh, uint32_t index) { + xaAssert(mesh.vertexNormalData); + return *((const internal::Vector3 *)&((const uint8_t *)mesh.vertexNormalData)[mesh.vertexNormalStride * index]); +} + +static internal::Vector2 DecodeUv(const InputMesh &mesh, uint32_t index) { + xaAssert(mesh.vertexUvData); + return *((const internal::Vector2 *)&((const uint8_t *)mesh.vertexUvData)[mesh.vertexUvStride * index]); +} + +static uint32_t DecodeIndex(IndexFormat::Enum format, const void *indexData, uint32_t i) { + if (format == IndexFormat::HalfFloat) + return (uint32_t)((const uint16_t *)indexData)[i]; + return ((const uint32_t *)indexData)[i]; +} + +static float EdgeLength(internal::Vector3 pos1, internal::Vector3 pos2) { + return internal::length(pos2 - pos1); +} + +AddMeshError AddMesh(Atlas *atlas, const InputMesh &mesh, bool useColocalVertices) { + xaAssert(atlas); + AddMeshError error; + error.code = AddMeshErrorCode::Success; + error.face = error.index0 = error.index1 = UINT32_MAX; + // Expecting triangle faces. + if ((mesh.indexCount % 3) != 0) { + error.code = AddMeshErrorCode::InvalidIndexCount; + return error; + } + // Check if any index is out of range. + for (uint32_t j = 0; j < mesh.indexCount; j++) { + const uint32_t index = DecodeIndex(mesh.indexFormat, mesh.indexData, j); + if (index < 0 || index >= mesh.vertexCount) { + error.code = AddMeshErrorCode::IndexOutOfRange; + error.index0 = index; + return error; + } + } + // Build half edge mesh. + internal::halfedge::Mesh *heMesh = new internal::halfedge::Mesh; + std::vector<uint32_t> canonicalMap; + canonicalMap.reserve(mesh.vertexCount); + for (uint32_t i = 0; i < mesh.vertexCount; i++) { + internal::halfedge::Vertex *vertex = heMesh->addVertex(DecodePosition(mesh, i)); + if (mesh.vertexNormalData) + vertex->nor = DecodeNormal(mesh, i); + if (mesh.vertexUvData) + vertex->tex = DecodeUv(mesh, i); + // Link colocals. You probably want to do this more efficiently! Sort by one axis or use a hash or grid. + uint32_t firstColocal = i; + if (useColocalVertices) { + for (uint32_t j = 0; j < i; j++) { + if (vertex->pos != DecodePosition(mesh, j)) + continue; +#if 0 + if (mesh.vertexNormalData && vertex->nor != DecodeNormal(mesh, j)) + continue; +#endif + if (mesh.vertexUvData && vertex->tex != DecodeUv(mesh, j)) + continue; + firstColocal = j; + break; + } + } + canonicalMap.push_back(firstColocal); + } + heMesh->linkColocalsWithCanonicalMap(canonicalMap); + for (uint32_t i = 0; i < mesh.indexCount / 3; i++) { + uint32_t tri[3]; + for (int j = 0; j < 3; j++) + tri[j] = DecodeIndex(mesh.indexFormat, mesh.indexData, i * 3 + j); + // Check for zero length edges. + for (int j = 0; j < 3; j++) { + const uint32_t edges[6] = { 0, 1, 1, 2, 2, 0 }; + const uint32_t index1 = tri[edges[j * 2 + 0]]; + const uint32_t index2 = tri[edges[j * 2 + 1]]; + const internal::Vector3 pos1 = DecodePosition(mesh, index1); + const internal::Vector3 pos2 = DecodePosition(mesh, index2); + if (EdgeLength(pos1, pos2) <= 0.0f) { + delete heMesh; + error.code = AddMeshErrorCode::ZeroLengthEdge; + error.face = i; + error.index0 = index1; + error.index1 = index2; + return error; + } + } + // Check for zero area faces. + { + const internal::Vector3 a = DecodePosition(mesh, tri[0]); + const internal::Vector3 b = DecodePosition(mesh, tri[1]); + const internal::Vector3 c = DecodePosition(mesh, tri[2]); + const float area = internal::length(internal::cross(b - a, c - a)) * 0.5f; + if (area <= 0.0f) { + delete heMesh; + error.code = AddMeshErrorCode::ZeroAreaFace; + error.face = i; + return error; + } + } + internal::halfedge::Face *face = heMesh->addFace(tri[0], tri[1], tri[2]); + + if (!face && heMesh->errorCode == internal::halfedge::Mesh::ErrorCode::AlreadyAddedEdge) { + //there is still hope for this, no reason to not add, at least add as separate + face = heMesh->addUniqueFace(tri[0], tri[1], tri[2]); + } + + if (!face) { + //continue; + + if (heMesh->errorCode == internal::halfedge::Mesh::ErrorCode::AlreadyAddedEdge) { + error.code = AddMeshErrorCode::AlreadyAddedEdge; + } else if (heMesh->errorCode == internal::halfedge::Mesh::ErrorCode::DegenerateColocalEdge) { + error.code = AddMeshErrorCode::DegenerateColocalEdge; + } else if (heMesh->errorCode == internal::halfedge::Mesh::ErrorCode::DegenerateEdge) { + error.code = AddMeshErrorCode::DegenerateEdge; + } else if (heMesh->errorCode == internal::halfedge::Mesh::ErrorCode::DuplicateEdge) { + error.code = AddMeshErrorCode::DuplicateEdge; + } + error.face = i; + error.index0 = heMesh->errorIndex0; + error.index1 = heMesh->errorIndex1; + delete heMesh; + return error; + } + if (mesh.faceMaterialData) + face->material = mesh.faceMaterialData[i]; + } + heMesh->linkBoundary(); + atlas->heMeshes.push_back(heMesh); + return error; +} + +void Generate(Atlas *atlas, CharterOptions charterOptions, PackerOptions packerOptions) { + xaAssert(atlas); + xaAssert(packerOptions.texelArea > 0); + // Chart meshes. + for (int i = 0; i < (int)atlas->heMeshes.size(); i++) { + std::vector<uint32_t> uncharted_materials; + atlas->atlas.computeCharts(atlas->heMeshes[i], charterOptions, uncharted_materials); + } + atlas->atlas.parameterizeCharts(); + internal::param::AtlasPacker packer(&atlas->atlas); + packer.packCharts(packerOptions); + //float utilization = return packer.computeAtlasUtilization(); + atlas->width = packer.getWidth(); + atlas->height = packer.getHeight(); + // Build output meshes. + atlas->outputMeshes = new OutputMesh *[atlas->heMeshes.size()]; + for (int i = 0; i < (int)atlas->heMeshes.size(); i++) { + const internal::halfedge::Mesh *heMesh = atlas->heMeshes[i]; + OutputMesh *outputMesh = atlas->outputMeshes[i] = new OutputMesh; + const internal::param::MeshCharts *charts = atlas->atlas.meshAt(i); + // Vertices. + outputMesh->vertexCount = charts->vertexCount(); + outputMesh->vertexArray = new OutputVertex[outputMesh->vertexCount]; + for (uint32_t i = 0; i < charts->chartCount(); i++) { + const internal::param::Chart *chart = charts->chartAt(i); + const uint32_t vertexOffset = charts->vertexCountBeforeChartAt(i); + for (uint32_t v = 0; v < chart->vertexCount(); v++) { + OutputVertex &output_vertex = outputMesh->vertexArray[vertexOffset + v]; + output_vertex.xref = chart->mapChartVertexToOriginalVertex(v); + internal::Vector2 uv = chart->chartMesh()->vertexAt(v)->tex; + output_vertex.uv[0] = uv.x; + output_vertex.uv[1] = uv.y; + } + } + // Indices. + outputMesh->indexCount = heMesh->faceCount() * 3; + outputMesh->indexArray = new uint32_t[outputMesh->indexCount]; + for (uint32_t f = 0; f < heMesh->faceCount(); f++) { + const uint32_t c = charts->faceChartAt(f); + const uint32_t i = charts->faceIndexWithinChartAt(f); + const uint32_t vertexOffset = charts->vertexCountBeforeChartAt(c); + const internal::param::Chart *chart = charts->chartAt(c); + xaDebugAssert(i < chart->chartMesh()->faceCount()); + xaDebugAssert(chart->faceAt(i) == f); + const internal::halfedge::Face *face = chart->chartMesh()->faceAt(i); + const internal::halfedge::Edge *edge = face->edge; + outputMesh->indexArray[3 * f + 0] = vertexOffset + edge->vertex->id; + outputMesh->indexArray[3 * f + 1] = vertexOffset + edge->next->vertex->id; + outputMesh->indexArray[3 * f + 2] = vertexOffset + edge->next->next->vertex->id; + } + // Charts. + outputMesh->chartCount = charts->chartCount(); + outputMesh->chartArray = new OutputChart[outputMesh->chartCount]; + for (uint32_t i = 0; i < charts->chartCount(); i++) { + OutputChart *outputChart = &outputMesh->chartArray[i]; + const internal::param::Chart *chart = charts->chartAt(i); + const uint32_t vertexOffset = charts->vertexCountBeforeChartAt(i); + const internal::halfedge::Mesh *mesh = chart->chartMesh(); + outputChart->indexCount = mesh->faceCount() * 3; + outputChart->indexArray = new uint32_t[outputChart->indexCount]; + for (uint32_t j = 0; j < mesh->faceCount(); j++) { + const internal::halfedge::Face *face = mesh->faceAt(j); + const internal::halfedge::Edge *edge = face->edge; + outputChart->indexArray[3 * j + 0] = vertexOffset + edge->vertex->id; + outputChart->indexArray[3 * j + 1] = vertexOffset + edge->next->vertex->id; + outputChart->indexArray[3 * j + 2] = vertexOffset + edge->next->next->vertex->id; + } + } + } +} + +uint32_t GetWidth(const Atlas *atlas) { + xaAssert(atlas); + return atlas->width; +} + +uint32_t GetHeight(const Atlas *atlas) { + xaAssert(atlas); + return atlas->height; +} + +uint32_t GetNumCharts(const Atlas *atlas) { + xaAssert(atlas); + return atlas->atlas.chartCount(); +} + +const OutputMesh *const *GetOutputMeshes(const Atlas *atlas) { + xaAssert(atlas); + return atlas->outputMeshes; +} + +const char *StringForEnum(AddMeshErrorCode::Enum error) { + if (error == AddMeshErrorCode::AlreadyAddedEdge) + return "already added edge"; + if (error == AddMeshErrorCode::DegenerateColocalEdge) + return "degenerate colocal edge"; + if (error == AddMeshErrorCode::DegenerateEdge) + return "degenerate edge"; + if (error == AddMeshErrorCode::DuplicateEdge) + return "duplicate edge"; + if (error == AddMeshErrorCode::IndexOutOfRange) + return "index out of range"; + if (error == AddMeshErrorCode::InvalidIndexCount) + return "invalid index count"; + if (error == AddMeshErrorCode::ZeroAreaFace) + return "zero area face"; + if (error == AddMeshErrorCode::ZeroLengthEdge) + return "zero length edge"; + return "success"; +} + +} // namespace xatlas diff --git a/thirdparty/xatlas/xatlas.h b/thirdparty/xatlas/xatlas.h new file mode 100644 index 0000000000..4140429fee --- /dev/null +++ b/thirdparty/xatlas/xatlas.h @@ -0,0 +1,160 @@ +// This code is in the public domain -- castanyo@yahoo.es +#pragma once +#ifndef XATLAS_H +#define XATLAS_H +#include <float.h> // FLT_MAX +#include <limits.h> +#include <stdint.h> +namespace xatlas { + +typedef void (*PrintFunc)(const char *, ...); + +struct Atlas; + +struct CharterOptions { + float proxyFitMetricWeight; + float roundnessMetricWeight; + float straightnessMetricWeight; + float normalSeamMetricWeight; + float textureSeamMetricWeight; + float maxChartArea; + float maxBoundaryLength; + + CharterOptions() { + // These are the default values we use on The Witness. + proxyFitMetricWeight = 2.0f; + roundnessMetricWeight = 0.01f; + straightnessMetricWeight = 6.0f; + normalSeamMetricWeight = 4.0f; + textureSeamMetricWeight = 0.5f; + /* + proxyFitMetricWeight = 1.0f; + roundnessMetricWeight = 0.1f; + straightnessMetricWeight = 0.25f; + normalSeamMetricWeight = 1.0f; + textureSeamMetricWeight = 0.1f; + */ + maxChartArea = FLT_MAX; + maxBoundaryLength = FLT_MAX; + } +}; + +struct PackMethod { + enum Enum { + TexelArea, // texel_area determines resolution + ApproximateResolution, // guess texel_area to approximately match desired resolution + ExactResolution // run the packer multiple times to exactly match the desired resolution (slow) + }; +}; + +struct PackerOptions { + PackMethod::Enum method; + + // 0 - brute force + // 1 - 4096 attempts + // 2 - 2048 + // 3 - 1024 + // 4 - 512 + // other - 256 + // Avoid brute force packing, since it can be unusably slow in some situations. + int quality; + + float texelArea; // This is not really texel area, but 1 / texel width? + uint32_t resolution; + bool blockAlign; // Align charts to 4x4 blocks. + bool conservative; // Pack charts with extra padding. + int padding; + + PackerOptions() { + method = PackMethod::ApproximateResolution; + quality = 1; + texelArea = 8; + resolution = 512; + blockAlign = false; + conservative = false; + padding = 0; + } +}; + +struct AddMeshErrorCode { + enum Enum { + Success, + AlreadyAddedEdge, // index0 and index1 are the edge indices + DegenerateColocalEdge, // index0 and index1 are the edge indices + DegenerateEdge, // index0 and index1 are the edge indices + DuplicateEdge, // index0 and index1 are the edge indices + IndexOutOfRange, // index0 is the index + InvalidIndexCount, // not evenly divisible by 3 - expecting triangles + ZeroAreaFace, + ZeroLengthEdge // index0 and index1 are the edge indices + }; +}; + +struct AddMeshError { + AddMeshErrorCode::Enum code; + uint32_t face; + uint32_t index0, index1; +}; + +struct IndexFormat { + enum Enum { + HalfFloat, + Float + }; +}; + +struct InputMesh { + uint32_t vertexCount; + const void *vertexPositionData; + uint32_t vertexPositionStride; + const void *vertexNormalData; // optional + uint32_t vertexNormalStride; // optional + + // optional + // The input UVs are provided as a hint to the chart generator. + const void *vertexUvData; + uint32_t vertexUvStride; + + uint32_t indexCount; + const void *indexData; + IndexFormat::Enum indexFormat; + + // optional. indexCount / 3 in length. + // Charter also uses material boundaries as a hint to cut charts. + const uint16_t *faceMaterialData; +}; + +struct OutputChart { + uint32_t *indexArray; + uint32_t indexCount; +}; + +struct OutputVertex { + float uv[2]; + uint32_t xref; // Index of input vertex from which this output vertex originated. +}; + +struct OutputMesh { + OutputChart *chartArray; + uint32_t chartCount; + uint32_t *indexArray; + uint32_t indexCount; + OutputVertex *vertexArray; + uint32_t vertexCount; +}; + +void SetPrint(PrintFunc print); +Atlas *Create(); +void Destroy(Atlas *atlas); +// useColocalVertices - generates fewer charts (good), but is more sensitive to bad geometry. +AddMeshError AddMesh(Atlas *atlas, const InputMesh &mesh, bool useColocalVertices = true); +void Generate(Atlas *atlas, CharterOptions charterOptions = CharterOptions(), PackerOptions packerOptions = PackerOptions()); +uint32_t GetWidth(const Atlas *atlas); +uint32_t GetHeight(const Atlas *atlas); +uint32_t GetNumCharts(const Atlas *atlas); +const OutputMesh *const *GetOutputMeshes(const Atlas *atlas); +const char *StringForEnum(AddMeshErrorCode::Enum error); + +} // namespace xatlas + +#endif // XATLAS_H diff --git a/thirdparty/zstd/1314.diff b/thirdparty/zstd/1314.diff new file mode 100644 index 0000000000..c9f4efadbf --- /dev/null +++ b/thirdparty/zstd/1314.diff @@ -0,0 +1,13 @@ +diff --git a/common/cpu.h b/common/cpu.h +index 88e0ebf44..eeb428ad5 100644 +--- a/common/cpu.h ++++ b/common/cpu.h +@@ -36,7 +36,7 @@ MEM_STATIC ZSTD_cpuid_t ZSTD_cpuid(void) { + U32 f1d = 0; + U32 f7b = 0; + U32 f7c = 0; +-#ifdef _MSC_VER ++#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86)) + int reg[4]; + __cpuid((int*)reg, 0); + { diff --git a/thirdparty/zstd/SCsub b/thirdparty/zstd/SCsub deleted file mode 100644 index 899a18e1cf..0000000000 --- a/thirdparty/zstd/SCsub +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python - -Import('env') - -thirdparty_zstd_dir = "#thirdparty/zstd/" -thirdparty_zstd_sources = [ - "common/entropy_common.c", - "common/error_private.c", - "common/fse_decompress.c", - "common/pool.c", - "common/threading.c", - "common/xxhash.c", - "common/zstd_common.c", - "compress/fse_compress.c", - "compress/huf_compress.c", - "compress/zstd_compress.c", - "compress/zstd_double_fast.c", - "compress/zstd_fast.c", - "compress/zstd_lazy.c", - "compress/zstd_ldm.c", - "compress/zstdmt_compress.c", - "compress/zstd_opt.c", - "decompress/huf_decompress.c", - "decompress/zstd_decompress.c", -] -thirdparty_zstd_sources = [thirdparty_zstd_dir + file for file in thirdparty_zstd_sources] -env.add_source_files(env.core_sources, thirdparty_zstd_sources) -env.Append(CPPPATH=["#thirdparty/zstd", "#thirdparty/zstd/common"]) -env.Append(CCFLAGS="-DZSTD_STATIC_LINKING_ONLY") diff --git a/thirdparty/zstd/common/cpu.h b/thirdparty/zstd/common/cpu.h index 4eb48e39e1..a109520a33 100644 --- a/thirdparty/zstd/common/cpu.h +++ b/thirdparty/zstd/common/cpu.h @@ -36,7 +36,7 @@ MEM_STATIC ZSTD_cpuid_t ZSTD_cpuid(void) { U32 f1d = 0; U32 f7b = 0; U32 f7c = 0; -#ifdef _MSC_VER +#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86)) int reg[4]; __cpuid((int*)reg, 0); { diff --git a/version.py b/version.py index 0eff47acdc..558e2da56c 100644 --- a/version.py +++ b/version.py @@ -2,5 +2,5 @@ short_name = "godot" name = "Godot Engine" major = 3 minor = 1 -status = "dev" +status = "alpha" module_config = "" |